• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Resources;
24 import android.inputmethodservice.InputMethodService;
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.annotations.UsedForTesting;
35 import com.android.inputmethod.keyboard.KeyboardSwitcher;
36 
37 import java.util.List;
38 import java.util.Locale;
39 import java.util.Map;
40 
41 public final class SubtypeSwitcher {
42     private static boolean DBG = LatinImeLogger.sDBG;
43     private static final String TAG = SubtypeSwitcher.class.getSimpleName();
44 
45     private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
46     private /* final */ RichInputMethodManager mRichImm;
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     /*-----------------------------------------------------------*/
57 
58     private boolean mIsNetworkConnected;
59 
60     static final class NeedsToDisplayLanguage {
61         private int mEnabledSubtypeCount;
62         private boolean mIsSystemLanguageSameAsInputLanguage;
63 
getValue()64         public boolean getValue() {
65             return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage;
66         }
67 
updateEnabledSubtypeCount(final int count)68         public void updateEnabledSubtypeCount(final int count) {
69             mEnabledSubtypeCount = count;
70         }
71 
updateIsSystemLanguageSameAsInputLanguage(final boolean isSame)72         public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) {
73             mIsSystemLanguageSameAsInputLanguage = isSame;
74         }
75     }
76 
getInstance()77     public static SubtypeSwitcher getInstance() {
78         return sInstance;
79     }
80 
init(final Context context)81     public static void init(final Context context) {
82         SubtypeLocale.init(context);
83         RichInputMethodManager.init(context);
84         sInstance.initialize(context);
85     }
86 
SubtypeSwitcher()87     private SubtypeSwitcher() {
88         // Intentional empty constructor for singleton.
89     }
90 
initialize(final Context context)91     private void initialize(final Context context) {
92         if (mResources != null) {
93             return;
94         }
95         mResources = context.getResources();
96         mRichImm = RichInputMethodManager.getInstance();
97         mConnectivityManager = (ConnectivityManager) context.getSystemService(
98                 Context.CONNECTIVITY_SERVICE);
99         mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
100                 SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
101         if (mNoLanguageSubtype == null) {
102             throw new RuntimeException("Can't find no lanugage with QWERTY subtype");
103         }
104 
105         final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
106         mIsNetworkConnected = (info != null && info.isConnected());
107 
108         onSubtypeChanged(getCurrentSubtype());
109         updateParametersOnStartInputView();
110     }
111 
112     /**
113      * Update parameters which are changed outside LatinIME. This parameters affect UI so that they
114      * should be updated every time onStartInputView is called.
115      */
updateParametersOnStartInputView()116     public void updateParametersOnStartInputView() {
117         final List<InputMethodSubtype> enabledSubtypesOfThisIme =
118                 mRichImm.getMyEnabledInputMethodSubtypeList(true);
119         mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size());
120         updateShortcutIME();
121     }
122 
updateShortcutIME()123     private void updateShortcutIME() {
124         if (DBG) {
125             Log.d(TAG, "Update shortcut IME from : "
126                     + (mShortcutInputMethodInfo == null
127                             ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
128                     + (mShortcutSubtype == null ? "<null>" : (
129                             mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
130         }
131         // TODO: Update an icon for shortcut IME
132         final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
133                 mRichImm.getInputMethodManager().getShortcutInputMethodsAndSubtypes();
134         mShortcutInputMethodInfo = null;
135         mShortcutSubtype = null;
136         for (final InputMethodInfo imi : shortcuts.keySet()) {
137             final List<InputMethodSubtype> subtypes = shortcuts.get(imi);
138             // TODO: Returns the first found IMI for now. Should handle all shortcuts as
139             // appropriate.
140             mShortcutInputMethodInfo = imi;
141             // TODO: Pick up the first found subtype for now. Should handle all subtypes
142             // as appropriate.
143             mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
144             break;
145         }
146         if (DBG) {
147             Log.d(TAG, "Update shortcut IME to : "
148                     + (mShortcutInputMethodInfo == null
149                             ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
150                     + (mShortcutSubtype == null ? "<null>" : (
151                             mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
152         }
153     }
154 
155     // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
onSubtypeChanged(final InputMethodSubtype newSubtype)156     public void onSubtypeChanged(final InputMethodSubtype newSubtype) {
157         if (DBG) {
158             Log.w(TAG, "onSubtypeChanged: " + SubtypeLocale.getSubtypeDisplayName(newSubtype));
159         }
160 
161         final Locale newLocale = SubtypeLocale.getSubtypeLocale(newSubtype);
162         final Locale systemLocale = mResources.getConfiguration().locale;
163         final boolean sameLocale = systemLocale.equals(newLocale);
164         final boolean sameLanguage = systemLocale.getLanguage().equals(newLocale.getLanguage());
165         final boolean implicitlyEnabled =
166                 mRichImm.checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype);
167         mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage(
168                 sameLocale || (sameLanguage && implicitlyEnabled));
169 
170         updateShortcutIME();
171     }
172 
173     ////////////////////////////
174     // Shortcut IME functions //
175     ////////////////////////////
176 
switchToShortcutIME(final InputMethodService context)177     public void switchToShortcutIME(final InputMethodService context) {
178         if (mShortcutInputMethodInfo == null) {
179             return;
180         }
181 
182         final String imiId = mShortcutInputMethodInfo.getId();
183         switchToTargetIME(imiId, mShortcutSubtype, context);
184     }
185 
switchToTargetIME(final String imiId, final InputMethodSubtype subtype, final InputMethodService context)186     private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
187             final InputMethodService context) {
188         final IBinder token = context.getWindow().getWindow().getAttributes().token;
189         if (token == null) {
190             return;
191         }
192         final InputMethodManager imm = mRichImm.getInputMethodManager();
193         new AsyncTask<Void, Void, Void>() {
194             @Override
195             protected Void doInBackground(Void... params) {
196                 imm.setInputMethodAndSubtype(token, imiId, subtype);
197                 return null;
198             }
199         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
200     }
201 
isShortcutImeEnabled()202     public boolean isShortcutImeEnabled() {
203         if (mShortcutInputMethodInfo == null) {
204             return false;
205         }
206         if (mShortcutSubtype == null) {
207             return true;
208         }
209         return mRichImm.checkIfSubtypeBelongsToImeAndEnabled(
210                 mShortcutInputMethodInfo, mShortcutSubtype);
211     }
212 
isShortcutImeReady()213     public boolean isShortcutImeReady() {
214         if (mShortcutInputMethodInfo == null)
215             return false;
216         if (mShortcutSubtype == null)
217             return true;
218         if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) {
219             return mIsNetworkConnected;
220         }
221         return true;
222     }
223 
onNetworkStateChanged(final Intent intent)224     public void onNetworkStateChanged(final Intent intent) {
225         final boolean noConnection = intent.getBooleanExtra(
226                 ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
227         mIsNetworkConnected = !noConnection;
228 
229         KeyboardSwitcher.getInstance().onNetworkStateChanged();
230     }
231 
232     //////////////////////////////////
233     // Subtype Switching functions //
234     //////////////////////////////////
235 
needsToDisplayLanguage(final Locale keyboardLocale)236     public boolean needsToDisplayLanguage(final Locale keyboardLocale) {
237         if (keyboardLocale.toString().equals(SubtypeLocale.NO_LANGUAGE)) {
238             return true;
239         }
240         if (!keyboardLocale.equals(getCurrentSubtypeLocale())) {
241             return false;
242         }
243         return mNeedsToDisplayLanguage.getValue();
244     }
245 
246     private static Locale sForcedLocaleForTesting = null;
247     @UsedForTesting
forceLocale(final Locale locale)248     void forceLocale(final Locale locale) {
249         sForcedLocaleForTesting = locale;
250     }
251 
getCurrentSubtypeLocale()252     public Locale getCurrentSubtypeLocale() {
253         if (null != sForcedLocaleForTesting) return sForcedLocaleForTesting;
254         return SubtypeLocale.getSubtypeLocale(getCurrentSubtype());
255     }
256 
getCurrentSubtype()257     public InputMethodSubtype getCurrentSubtype() {
258         return mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype);
259     }
260 
getNoLanguageSubtype()261     public InputMethodSubtype getNoLanguageSubtype() {
262         return mNoLanguageSubtype;
263     }
264 }
265