Home | History | Annotate | Download | only in latin
      1 /*
      2  * Copyright (C) 2008 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * 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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.inputmethod.latin;
     18 
     19 import java.util.HashMap;
     20 import java.util.Locale;
     21 import java.util.Map;
     22 
     23 import android.content.Context;
     24 import android.content.res.Configuration;
     25 import android.content.res.Resources;
     26 import android.inputmethodservice.InputMethodService;
     27 
     28 public class KeyboardSwitcher {
     29 
     30     public static final int MODE_TEXT = 1;
     31     public static final int MODE_SYMBOLS = 2;
     32     public static final int MODE_PHONE = 3;
     33     public static final int MODE_URL = 4;
     34     public static final int MODE_EMAIL = 5;
     35     public static final int MODE_IM = 6;
     36     public static final int MODE_WEB = 7;
     37 
     38     public static final int MODE_TEXT_QWERTY = 0;
     39     public static final int MODE_TEXT_ALPHA = 1;
     40     public static final int MODE_TEXT_COUNT = 2;
     41 
     42     public static final int KEYBOARDMODE_NORMAL = R.id.mode_normal;
     43     public static final int KEYBOARDMODE_URL = R.id.mode_url;
     44     public static final int KEYBOARDMODE_EMAIL = R.id.mode_email;
     45     public static final int KEYBOARDMODE_IM = R.id.mode_im;
     46     public static final int KEYBOARDMODE_WEB = R.id.mode_webentry;
     47 
     48     private static final int SYMBOLS_MODE_STATE_NONE = 0;
     49     private static final int SYMBOLS_MODE_STATE_BEGIN = 1;
     50     private static final int SYMBOLS_MODE_STATE_SYMBOL = 2;
     51 
     52     LatinKeyboardView mInputView;
     53     private static final int[] ALPHABET_MODES = {
     54         KEYBOARDMODE_NORMAL,
     55         KEYBOARDMODE_URL,
     56         KEYBOARDMODE_EMAIL,
     57         KEYBOARDMODE_IM,
     58         KEYBOARDMODE_WEB};
     59 
     60     //LatinIME mContext;
     61     Context mContext;
     62     InputMethodService mInputMethodService;
     63 
     64     private KeyboardId mSymbolsId;
     65     private KeyboardId mSymbolsShiftedId;
     66 
     67     private KeyboardId mCurrentId;
     68     private Map<KeyboardId, LatinKeyboard> mKeyboards;
     69 
     70     private int mMode; /** One of the MODE_XXX values */
     71     private int mImeOptions;
     72     private int mTextMode = MODE_TEXT_QWERTY;
     73     private boolean mIsSymbols;
     74     private boolean mHasVoice;
     75     private boolean mVoiceOnPrimary;
     76     private boolean mPreferSymbols;
     77     private int mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
     78 
     79     private int mLastDisplayWidth;
     80     private LanguageSwitcher mLanguageSwitcher;
     81     private Locale mInputLocale;
     82     private boolean mEnableMultipleLanguages;
     83 
     84     KeyboardSwitcher(Context context, InputMethodService ims) {
     85         mContext = context;
     86         mKeyboards = new HashMap<KeyboardId, LatinKeyboard>();
     87         mSymbolsId = new KeyboardId(R.xml.kbd_symbols, false);
     88         mSymbolsShiftedId = new KeyboardId(R.xml.kbd_symbols_shift, false);
     89         mInputMethodService = ims;
     90     }
     91 
     92     /**
     93      * Sets the input locale, when there are multiple locales for input.
     94      * If no locale switching is required, then the locale should be set to null.
     95      * @param locale the current input locale, or null for default locale with no locale
     96      * button.
     97      */
     98     void setLanguageSwitcher(LanguageSwitcher languageSwitcher) {
     99         mLanguageSwitcher = languageSwitcher;
    100         mInputLocale = mLanguageSwitcher.getInputLocale();
    101         mEnableMultipleLanguages = mLanguageSwitcher.getLocaleCount() > 1;
    102     }
    103 
    104     void setInputView(LatinKeyboardView inputView) {
    105         mInputView = inputView;
    106     }
    107 
    108     void makeKeyboards(boolean forceCreate) {
    109         if (forceCreate) mKeyboards.clear();
    110         // Configuration change is coming after the keyboard gets recreated. So don't rely on that.
    111         // If keyboards have already been made, check if we have a screen width change and
    112         // create the keyboard layouts again at the correct orientation
    113         int displayWidth = mInputMethodService.getMaxWidth();
    114         if (displayWidth == mLastDisplayWidth) return;
    115         mLastDisplayWidth = displayWidth;
    116         if (!forceCreate) mKeyboards.clear();
    117         mSymbolsId = new KeyboardId(R.xml.kbd_symbols, mHasVoice && !mVoiceOnPrimary);
    118         mSymbolsShiftedId = new KeyboardId(R.xml.kbd_symbols_shift,
    119                 mHasVoice && !mVoiceOnPrimary);
    120     }
    121 
    122     /**
    123      * Represents the parameters necessary to construct a new LatinKeyboard,
    124      * which also serve as a unique identifier for each keyboard type.
    125      */
    126     private static class KeyboardId {
    127         public int mXml;
    128         public int mKeyboardMode; /** A KEYBOARDMODE_XXX value */
    129         public boolean mEnableShiftLock;
    130         public boolean mHasVoice;
    131 
    132         public KeyboardId(int xml, int mode, boolean enableShiftLock, boolean hasVoice) {
    133             this.mXml = xml;
    134             this.mKeyboardMode = mode;
    135             this.mEnableShiftLock = enableShiftLock;
    136             this.mHasVoice = hasVoice;
    137         }
    138 
    139         public KeyboardId(int xml, boolean hasVoice) {
    140             this(xml, 0, false, hasVoice);
    141         }
    142 
    143         public boolean equals(Object other) {
    144             return other instanceof KeyboardId && equals((KeyboardId) other);
    145         }
    146 
    147         public boolean equals(KeyboardId other) {
    148           return other.mXml == this.mXml
    149               && other.mKeyboardMode == this.mKeyboardMode
    150               && other.mEnableShiftLock == this.mEnableShiftLock;
    151         }
    152 
    153         public int hashCode() {
    154             return (mXml + 1) * (mKeyboardMode + 1) * (mEnableShiftLock ? 2 : 1)
    155                     * (mHasVoice ? 4 : 8);
    156         }
    157     }
    158 
    159     void setVoiceMode(boolean enableVoice, boolean voiceOnPrimary) {
    160         if (enableVoice != mHasVoice || voiceOnPrimary != mVoiceOnPrimary) {
    161             mKeyboards.clear();
    162         }
    163         mHasVoice = enableVoice;
    164         mVoiceOnPrimary = voiceOnPrimary;
    165         setKeyboardMode(mMode, mImeOptions, mHasVoice,
    166                 mIsSymbols);
    167     }
    168 
    169     boolean hasVoiceButton(boolean isSymbols) {
    170         return mHasVoice && (isSymbols != mVoiceOnPrimary);
    171     }
    172 
    173     void setKeyboardMode(int mode, int imeOptions, boolean enableVoice) {
    174         mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
    175         mPreferSymbols = mode == MODE_SYMBOLS;
    176         setKeyboardMode(mode == MODE_SYMBOLS ? MODE_TEXT : mode, imeOptions, enableVoice,
    177                 mPreferSymbols);
    178     }
    179 
    180     void setKeyboardMode(int mode, int imeOptions, boolean enableVoice, boolean isSymbols) {
    181         if (mInputView == null) return;
    182         mMode = mode;
    183         mImeOptions = imeOptions;
    184         if (enableVoice != mHasVoice) {
    185             setVoiceMode(mHasVoice, mVoiceOnPrimary);
    186         }
    187         mIsSymbols = isSymbols;
    188 
    189         mInputView.setPreviewEnabled(true);
    190         KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols);
    191 
    192         LatinKeyboard keyboard = getKeyboard(id);
    193 
    194         if (mode == MODE_PHONE) {
    195             mInputView.setPhoneKeyboard(keyboard);
    196             mInputView.setPreviewEnabled(false);
    197         }
    198 
    199         mCurrentId = id;
    200         mInputView.setKeyboard(keyboard);
    201         keyboard.setShifted(false);
    202         keyboard.setShiftLocked(keyboard.isShiftLocked());
    203         keyboard.setImeOptions(mContext.getResources(), mMode, imeOptions);
    204     }
    205 
    206     private LatinKeyboard getKeyboard(KeyboardId id) {
    207         if (!mKeyboards.containsKey(id)) {
    208             Resources orig = mContext.getResources();
    209             Configuration conf = orig.getConfiguration();
    210             Locale saveLocale = conf.locale;
    211             conf.locale = mInputLocale;
    212             orig.updateConfiguration(conf, null);
    213             LatinKeyboard keyboard = new LatinKeyboard(
    214                 mContext, id.mXml, id.mKeyboardMode);
    215             keyboard.setVoiceMode(hasVoiceButton(id.mXml == R.xml.kbd_symbols), mHasVoice);
    216             keyboard.setLanguageSwitcher(mLanguageSwitcher);
    217             if (id.mKeyboardMode == KEYBOARDMODE_NORMAL
    218                     || id.mKeyboardMode == KEYBOARDMODE_URL
    219                     || id.mKeyboardMode == KEYBOARDMODE_IM
    220                     || id.mKeyboardMode == KEYBOARDMODE_EMAIL
    221                     || id.mKeyboardMode == KEYBOARDMODE_WEB
    222                     ) {
    223                 keyboard.setExtension(R.xml.kbd_extension);
    224             }
    225 
    226             if (id.mEnableShiftLock) {
    227                 keyboard.enableShiftLock();
    228             }
    229             mKeyboards.put(id, keyboard);
    230 
    231             conf.locale = saveLocale;
    232             orig.updateConfiguration(conf, null);
    233         }
    234         return mKeyboards.get(id);
    235     }
    236 
    237     private KeyboardId getKeyboardId(int mode, int imeOptions, boolean isSymbols) {
    238         boolean hasVoice = hasVoiceButton(isSymbols);
    239         if (isSymbols) {
    240             return (mode == MODE_PHONE)
    241                 ? new KeyboardId(R.xml.kbd_phone_symbols, hasVoice)
    242                 : new KeyboardId(R.xml.kbd_symbols, hasVoice);
    243         }
    244         switch (mode) {
    245             case MODE_TEXT:
    246                 if (mTextMode == MODE_TEXT_QWERTY) {
    247                     return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_NORMAL, true, hasVoice);
    248                 } else if (mTextMode == MODE_TEXT_ALPHA) {
    249                     return new KeyboardId(R.xml.kbd_alpha, KEYBOARDMODE_NORMAL, true, hasVoice);
    250                 }
    251                 break;
    252             case MODE_SYMBOLS:
    253                 return new KeyboardId(R.xml.kbd_symbols, hasVoice);
    254             case MODE_PHONE:
    255                 return new KeyboardId(R.xml.kbd_phone, hasVoice);
    256             case MODE_URL:
    257                 return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_URL, true, hasVoice);
    258             case MODE_EMAIL:
    259                 return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_EMAIL, true, hasVoice);
    260             case MODE_IM:
    261                 return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_IM, true, hasVoice);
    262             case MODE_WEB:
    263                 return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_WEB, true, hasVoice);
    264         }
    265         return null;
    266     }
    267 
    268     int getKeyboardMode() {
    269         return mMode;
    270     }
    271 
    272     boolean isTextMode() {
    273         return mMode == MODE_TEXT;
    274     }
    275 
    276     int getTextMode() {
    277         return mTextMode;
    278     }
    279 
    280     void setTextMode(int position) {
    281         if (position < MODE_TEXT_COUNT && position >= 0) {
    282             mTextMode = position;
    283         }
    284         if (isTextMode()) {
    285             setKeyboardMode(MODE_TEXT, mImeOptions, mHasVoice);
    286         }
    287     }
    288 
    289     int getTextModeCount() {
    290         return MODE_TEXT_COUNT;
    291     }
    292 
    293     boolean isAlphabetMode() {
    294         int currentMode = mCurrentId.mKeyboardMode;
    295         for (Integer mode : ALPHABET_MODES) {
    296             if (currentMode == mode) {
    297                 return true;
    298             }
    299         }
    300         return false;
    301     }
    302 
    303     void toggleShift() {
    304         if (mCurrentId.equals(mSymbolsId)) {
    305             LatinKeyboard symbolsKeyboard = getKeyboard(mSymbolsId);
    306             LatinKeyboard symbolsShiftedKeyboard = getKeyboard(mSymbolsShiftedId);
    307             symbolsKeyboard.setShifted(true);
    308             mCurrentId = mSymbolsShiftedId;
    309             mInputView.setKeyboard(symbolsShiftedKeyboard);
    310             symbolsShiftedKeyboard.setShifted(true);
    311             symbolsShiftedKeyboard.setImeOptions(mContext.getResources(), mMode, mImeOptions);
    312         } else if (mCurrentId.equals(mSymbolsShiftedId)) {
    313             LatinKeyboard symbolsKeyboard = getKeyboard(mSymbolsId);
    314             LatinKeyboard symbolsShiftedKeyboard = getKeyboard(mSymbolsShiftedId);
    315             symbolsShiftedKeyboard.setShifted(false);
    316             mCurrentId = mSymbolsId;
    317             mInputView.setKeyboard(getKeyboard(mSymbolsId));
    318             symbolsKeyboard.setShifted(false);
    319             symbolsKeyboard.setImeOptions(mContext.getResources(), mMode, mImeOptions);
    320         }
    321     }
    322 
    323     void toggleSymbols() {
    324         setKeyboardMode(mMode, mImeOptions, mHasVoice, !mIsSymbols);
    325         if (mIsSymbols && !mPreferSymbols) {
    326             mSymbolsModeState = SYMBOLS_MODE_STATE_BEGIN;
    327         } else {
    328             mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
    329         }
    330     }
    331 
    332     /**
    333      * Updates state machine to figure out when to automatically switch back to alpha mode.
    334      * Returns true if the keyboard needs to switch back
    335      */
    336     boolean onKey(int key) {
    337         // Switch back to alpha mode if user types one or more non-space/enter characters
    338         // followed by a space/enter
    339         switch (mSymbolsModeState) {
    340             case SYMBOLS_MODE_STATE_BEGIN:
    341                 if (key != LatinIME.KEYCODE_SPACE && key != LatinIME.KEYCODE_ENTER && key > 0) {
    342                     mSymbolsModeState = SYMBOLS_MODE_STATE_SYMBOL;
    343                 }
    344                 break;
    345             case SYMBOLS_MODE_STATE_SYMBOL:
    346                 if (key == LatinIME.KEYCODE_ENTER || key == LatinIME.KEYCODE_SPACE) return true;
    347                 break;
    348         }
    349         return false;
    350     }
    351 }
    352