Home | History | Annotate | Download | only in latin
      1 /*
      2  * Copyright (C) 2010 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.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY;
     20 
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.res.Configuration;
     24 import android.content.res.Resources;
     25 import android.net.ConnectivityManager;
     26 import android.net.NetworkInfo;
     27 import android.os.AsyncTask;
     28 import android.os.IBinder;
     29 import android.util.Log;
     30 import android.view.inputmethod.InputMethodInfo;
     31 import android.view.inputmethod.InputMethodManager;
     32 import android.view.inputmethod.InputMethodSubtype;
     33 
     34 import com.android.inputmethod.keyboard.KeyboardSwitcher;
     35 
     36 import java.util.List;
     37 import java.util.Locale;
     38 import java.util.Map;
     39 
     40 public class SubtypeSwitcher {
     41     private static boolean DBG = LatinImeLogger.sDBG;
     42     private static final String TAG = SubtypeSwitcher.class.getSimpleName();
     43 
     44     private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
     45     private /* final */ LatinIME mService;
     46     private /* final */ InputMethodManager mImm;
     47     private /* final */ Resources mResources;
     48     private /* final */ ConnectivityManager mConnectivityManager;
     49 
     50     /*-----------------------------------------------------------*/
     51     // Variants which should be changed only by reload functions.
     52     private NeedsToDisplayLanguage mNeedsToDisplayLanguage = new NeedsToDisplayLanguage();
     53     private InputMethodInfo mShortcutInputMethodInfo;
     54     private InputMethodSubtype mShortcutSubtype;
     55     private InputMethodSubtype mNoLanguageSubtype;
     56     // Note: This variable is always non-null after {@link #initialize(LatinIME)}.
     57     private InputMethodSubtype mCurrentSubtype;
     58     private Locale mCurrentSystemLocale;
     59     /*-----------------------------------------------------------*/
     60 
     61     private boolean mIsNetworkConnected;
     62 
     63     static class NeedsToDisplayLanguage {
     64         private int mEnabledSubtypeCount;
     65         private boolean mIsSystemLanguageSameAsInputLanguage;
     66 
     67         public boolean getValue() {
     68             return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage;
     69         }
     70 
     71         public void updateEnabledSubtypeCount(int count) {
     72             mEnabledSubtypeCount = count;
     73         }
     74 
     75         public void updateIsSystemLanguageSameAsInputLanguage(boolean isSame) {
     76             mIsSystemLanguageSameAsInputLanguage = isSame;
     77         }
     78     }
     79 
     80     public static SubtypeSwitcher getInstance() {
     81         return sInstance;
     82     }
     83 
     84     public static void init(LatinIME service) {
     85         SubtypeLocale.init(service);
     86         sInstance.initialize(service);
     87         sInstance.updateAllParameters();
     88     }
     89 
     90     private SubtypeSwitcher() {
     91         // Intentional empty constructor for singleton.
     92     }
     93 
     94     private void initialize(LatinIME service) {
     95         mService = service;
     96         mResources = service.getResources();
     97         mImm = ImfUtils.getInputMethodManager(service);
     98         mConnectivityManager = (ConnectivityManager) service.getSystemService(
     99                 Context.CONNECTIVITY_SERVICE);
    100         mCurrentSystemLocale = mResources.getConfiguration().locale;
    101         mCurrentSubtype = mImm.getCurrentInputMethodSubtype();
    102         mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
    103                 service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
    104         if (mNoLanguageSubtype == null) {
    105             throw new RuntimeException("Can't find no lanugage with QWERTY subtype");
    106         }
    107 
    108         final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
    109         mIsNetworkConnected = (info != null && info.isConnected());
    110     }
    111 
    112     // Update all parameters stored in SubtypeSwitcher.
    113     // Only configuration changed event is allowed to call this because this is heavy.
    114     private void updateAllParameters() {
    115         mCurrentSystemLocale = mResources.getConfiguration().locale;
    116         updateSubtype(mImm.getCurrentInputMethodSubtype());
    117         updateParametersOnStartInputView();
    118     }
    119 
    120     // Update parameters which are changed outside LatinIME. This parameters affect UI so they
    121     // should be updated every time onStartInputview.
    122     public void updateParametersOnStartInputView() {
    123         updateEnabledSubtypes();
    124         updateShortcutIME();
    125     }
    126 
    127     // Reload enabledSubtypes from the framework.
    128     private void updateEnabledSubtypes() {
    129         final InputMethodSubtype currentSubtype = mCurrentSubtype;
    130         boolean foundCurrentSubtypeBecameDisabled = true;
    131         final List<InputMethodSubtype> enabledSubtypesOfThisIme =
    132                 mImm.getEnabledInputMethodSubtypeList(null, true);
    133         for (InputMethodSubtype ims : enabledSubtypesOfThisIme) {
    134             if (ims.equals(currentSubtype)) {
    135                 foundCurrentSubtypeBecameDisabled = false;
    136             }
    137         }
    138         mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size());
    139         if (foundCurrentSubtypeBecameDisabled) {
    140             if (DBG) {
    141                 Log.w(TAG, "Last subtype: "
    142                         + currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue());
    143                 Log.w(TAG, "Last subtype was disabled. Update to the current one.");
    144             }
    145             updateSubtype(mImm.getCurrentInputMethodSubtype());
    146         }
    147     }
    148 
    149     private void updateShortcutIME() {
    150         if (DBG) {
    151             Log.d(TAG, "Update shortcut IME from : "
    152                     + (mShortcutInputMethodInfo == null
    153                             ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
    154                     + (mShortcutSubtype == null ? "<null>" : (
    155                             mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
    156         }
    157         // TODO: Update an icon for shortcut IME
    158         final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
    159                 mImm.getShortcutInputMethodsAndSubtypes();
    160         mShortcutInputMethodInfo = null;
    161         mShortcutSubtype = null;
    162         for (InputMethodInfo imi : shortcuts.keySet()) {
    163             List<InputMethodSubtype> subtypes = shortcuts.get(imi);
    164             // TODO: Returns the first found IMI for now. Should handle all shortcuts as
    165             // appropriate.
    166             mShortcutInputMethodInfo = imi;
    167             // TODO: Pick up the first found subtype for now. Should handle all subtypes
    168             // as appropriate.
    169             mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
    170             break;
    171         }
    172         if (DBG) {
    173             Log.d(TAG, "Update shortcut IME to : "
    174                     + (mShortcutInputMethodInfo == null
    175                             ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
    176                     + (mShortcutSubtype == null ? "<null>" : (
    177                             mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
    178         }
    179     }
    180 
    181     // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
    182     public void updateSubtype(InputMethodSubtype newSubtype) {
    183         if (DBG) {
    184             Log.w(TAG, "onCurrentInputMethodSubtypeChanged: to: "
    185                     + newSubtype.getLocale() + "/" + newSubtype.getExtraValue() + ", from: "
    186                     + mCurrentSubtype.getLocale() + "/" + mCurrentSubtype.getExtraValue());
    187         }
    188 
    189         final Locale newLocale = SubtypeLocale.getSubtypeLocale(newSubtype);
    190         mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage(
    191                 mCurrentSystemLocale.equals(newLocale));
    192 
    193         if (newSubtype.equals(mCurrentSubtype)) return;
    194 
    195         mCurrentSubtype = newSubtype;
    196         updateShortcutIME();
    197         mService.onRefreshKeyboard();
    198     }
    199 
    200     ////////////////////////////
    201     // Shortcut IME functions //
    202     ////////////////////////////
    203 
    204     public void switchToShortcutIME() {
    205         if (mShortcutInputMethodInfo == null) {
    206             return;
    207         }
    208 
    209         final String imiId = mShortcutInputMethodInfo.getId();
    210         switchToTargetIME(imiId, mShortcutSubtype);
    211     }
    212 
    213     private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype) {
    214         final IBinder token = mService.getWindow().getWindow().getAttributes().token;
    215         if (token == null) {
    216             return;
    217         }
    218         final InputMethodManager imm = mImm;
    219         new AsyncTask<Void, Void, Void>() {
    220             @Override
    221             protected Void doInBackground(Void... params) {
    222                 imm.setInputMethodAndSubtype(token, imiId, subtype);
    223                 return null;
    224             }
    225         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    226     }
    227 
    228     public boolean isShortcutImeEnabled() {
    229         if (mShortcutInputMethodInfo == null) {
    230             return false;
    231         }
    232         if (mShortcutSubtype == null) {
    233             return true;
    234         }
    235         final boolean allowsImplicitlySelectedSubtypes = true;
    236         for (final InputMethodSubtype enabledSubtype : mImm.getEnabledInputMethodSubtypeList(
    237                 mShortcutInputMethodInfo, allowsImplicitlySelectedSubtypes)) {
    238             if (enabledSubtype.equals(mShortcutSubtype)) {
    239                 return true;
    240             }
    241         }
    242         return false;
    243     }
    244 
    245     public boolean isShortcutImeReady() {
    246         if (mShortcutInputMethodInfo == null)
    247             return false;
    248         if (mShortcutSubtype == null)
    249             return true;
    250         if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) {
    251             return mIsNetworkConnected;
    252         }
    253         return true;
    254     }
    255 
    256     public void onNetworkStateChanged(Intent intent) {
    257         final boolean noConnection = intent.getBooleanExtra(
    258                 ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
    259         mIsNetworkConnected = !noConnection;
    260 
    261         KeyboardSwitcher.getInstance().onNetworkStateChanged();
    262     }
    263 
    264     //////////////////////////////////
    265     // Subtype Switching functions //
    266     //////////////////////////////////
    267 
    268     public boolean needsToDisplayLanguage(Locale keyboardLocale) {
    269         if (keyboardLocale.toString().equals(SubtypeLocale.NO_LANGUAGE)) {
    270             return true;
    271         }
    272         if (!keyboardLocale.equals(getCurrentSubtypeLocale())) {
    273             return false;
    274         }
    275         return mNeedsToDisplayLanguage.getValue();
    276     }
    277 
    278     public Locale getCurrentSubtypeLocale() {
    279         return SubtypeLocale.getSubtypeLocale(mCurrentSubtype);
    280     }
    281 
    282     public void onConfigurationChanged(Configuration conf) {
    283         final Locale systemLocale = conf.locale;
    284         // If system configuration was changed, update all parameters.
    285         if (!systemLocale.equals(mCurrentSystemLocale)) {
    286             updateAllParameters();
    287         }
    288     }
    289 
    290     public InputMethodSubtype getCurrentSubtype() {
    291         return mCurrentSubtype;
    292     }
    293 
    294     public InputMethodSubtype getNoLanguageSubtype() {
    295         return mNoLanguageSubtype;
    296     }
    297 }
    298