• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.settings.inputmethod;
18 
19 import android.annotation.DrawableRes;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.Activity;
23 import android.app.admin.DevicePolicyManager;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.ServiceInfo;
29 import android.content.res.Configuration;
30 import android.graphics.Color;
31 import android.graphics.drawable.ColorDrawable;
32 import android.graphics.drawable.Drawable;
33 import android.os.Bundle;
34 import android.support.v7.preference.PreferenceScreen;
35 import android.view.inputmethod.InputMethodInfo;
36 import android.view.inputmethod.InputMethodManager;
37 import android.view.inputmethod.InputMethodSubtype;
38 
39 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
40 import com.android.settings.R;
41 import com.android.settings.SettingsPreferenceFragment;
42 import com.android.settings.search.BaseSearchIndexProvider;
43 import com.android.settings.search.Indexable;
44 import com.android.settings.search.SearchIndexableRaw;
45 import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
46 import com.android.settingslib.inputmethod.InputMethodPreference;
47 import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper;
48 
49 import java.text.Collator;
50 import java.util.ArrayList;
51 import java.util.List;
52 
53 public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFragment
54         implements InputMethodPreference.OnSavePreferenceListener, Indexable {
55 
56     private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
57     private InputMethodSettingValuesWrapper mInputMethodSettingValues;
58     private InputMethodManager mImm;
59     private DevicePolicyManager mDpm;
60 
61     @Override
onCreatePreferences(Bundle bundle, String s)62     public void onCreatePreferences(Bundle bundle, String s) {
63         Activity activity = getActivity();
64         PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(activity);
65         screen.setTitle(activity.getString(R.string.available_virtual_keyboard_category));
66         setPreferenceScreen(screen);
67         mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity);
68         mImm = activity.getSystemService(InputMethodManager.class);
69         mDpm = activity.getSystemService(DevicePolicyManager.class);
70     }
71 
72     @Override
onResume()73     public void onResume() {
74         super.onResume();
75         // Refresh internal states in mInputMethodSettingValues to keep the latest
76         // "InputMethodInfo"s and "InputMethodSubtype"s
77         mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
78         updateInputMethodPreferenceViews();
79     }
80 
81     @Override
onSaveInputMethodPreference(final InputMethodPreference pref)82     public void onSaveInputMethodPreference(final InputMethodPreference pref) {
83         final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard
84                 == Configuration.KEYBOARD_QWERTY;
85         InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
86                 mImm.getInputMethodList(), hasHardwareKeyboard);
87         // Update input method settings and preference list.
88         mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
89         for (final InputMethodPreference p : mInputMethodPreferenceList) {
90             p.updatePreferenceViews();
91         }
92     }
93 
94     @Override
getMetricsCategory()95     public int getMetricsCategory() {
96         return MetricsEvent.ENABLE_VIRTUAL_KEYBOARDS;
97     }
98 
99     @Nullable
loadDrawable(@onNull final PackageManager packageManager, @NonNull final String packageName, @DrawableRes final int resId, @NonNull final ApplicationInfo applicationInfo)100     private static Drawable loadDrawable(@NonNull final PackageManager packageManager,
101             @NonNull final String packageName, @DrawableRes final int resId,
102             @NonNull final ApplicationInfo applicationInfo) {
103         if (resId == 0) {
104             return null;
105         }
106         try {
107             return packageManager.getDrawable(packageName, resId, applicationInfo);
108         } catch (Exception e){
109             return null;
110         }
111     }
112 
113     @NonNull
getInputMethodIcon(@onNull final PackageManager packageManager, @NonNull final InputMethodInfo imi)114     private static Drawable getInputMethodIcon(@NonNull final PackageManager packageManager,
115             @NonNull final InputMethodInfo imi) {
116         final ServiceInfo si = imi.getServiceInfo();
117         final ApplicationInfo ai = si != null ? si.applicationInfo : null;
118         final String packageName = imi.getPackageName();
119         if (si == null || ai == null || packageName == null) {
120             return new ColorDrawable(Color.TRANSPARENT);
121         }
122         // We do not use ServiceInfo#loadLogo() and ServiceInfo#loadIcon here since those methods
123         // internally have some fallback rules, which we want to do manually.
124         Drawable drawable = loadDrawable(packageManager, packageName, si.logo, ai);
125         if (drawable != null) {
126             return drawable;
127         }
128         drawable = loadDrawable(packageManager, packageName, si.icon, ai);
129         if (drawable != null) {
130             return drawable;
131         }
132         // We do not use ApplicationInfo#loadLogo() and ApplicationInfo#loadIcon here since those
133         // methods internally have some fallback rules, which we want to do manually.
134         drawable = loadDrawable(packageManager, packageName, ai.logo, ai);
135         if (drawable != null) {
136             return drawable;
137         }
138         drawable = loadDrawable(packageManager, packageName, ai.icon, ai);
139         if (drawable != null) {
140             return drawable;
141         }
142         return new ColorDrawable(Color.TRANSPARENT);
143     }
144 
updateInputMethodPreferenceViews()145     private void updateInputMethodPreferenceViews() {
146         mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
147         // Clear existing "InputMethodPreference"s
148         mInputMethodPreferenceList.clear();
149         List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser();
150         final Context context = getPrefContext();
151         final PackageManager packageManager = getActivity().getPackageManager();
152         final List<InputMethodInfo> imis = mInputMethodSettingValues.getInputMethodList();
153         final int numImis = (imis == null ? 0 : imis.size());
154         for (int i = 0; i < numImis; ++i) {
155             final InputMethodInfo imi = imis.get(i);
156             final boolean isAllowedByOrganization = permittedList == null
157                     || permittedList.contains(imi.getPackageName());
158             final InputMethodPreference pref = new InputMethodPreference(
159                     context, imi, true, isAllowedByOrganization, this);
160             pref.setIcon(getInputMethodIcon(packageManager, imi));
161             mInputMethodPreferenceList.add(pref);
162         }
163         final Collator collator = Collator.getInstance();
164         mInputMethodPreferenceList.sort((lhs, rhs) -> lhs.compareTo(rhs, collator));
165         getPreferenceScreen().removeAll();
166         for (int i = 0; i < numImis; ++i) {
167             final InputMethodPreference pref = mInputMethodPreferenceList.get(i);
168             pref.setOrder(i);
169             getPreferenceScreen().addPreference(pref);
170             InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref);
171             pref.updatePreferenceViews();
172         }
173     }
174 
getAllSubtypesOf(final InputMethodInfo imi)175     private static List<InputMethodSubtype> getAllSubtypesOf(final InputMethodInfo imi) {
176         final int subtypeCount = imi.getSubtypeCount();
177         final List<InputMethodSubtype> allSubtypes = new ArrayList<>(subtypeCount);
178         for (int index = 0; index < subtypeCount; index++) {
179             allSubtypes.add(imi.getSubtypeAt(index));
180         }
181         return allSubtypes;
182     }
183 
buildSearchIndexOfInputMethods(final Context context, final List<InputMethodInfo> inputMethods, final String screenTitle)184     static List<SearchIndexableRaw> buildSearchIndexOfInputMethods(final Context context,
185             final List<InputMethodInfo> inputMethods, final String screenTitle) {
186         final List<SearchIndexableRaw> indexes = new ArrayList<>();
187         for (int i = 0; i < inputMethods.size(); i++) {
188             final InputMethodInfo imi = inputMethods.get(i);
189             final ServiceInfo serviceInfo = imi.getServiceInfo();
190             final SearchIndexableRaw index = new SearchIndexableRaw(context);
191             index.key = new ComponentName(serviceInfo.packageName, serviceInfo.name)
192                     .flattenToString();
193             index.title = imi.loadLabel(context.getPackageManager()).toString();
194             index.summaryOn = index.summaryOff = InputMethodAndSubtypeUtil
195                     .getSubtypeLocaleNameListAsSentence(getAllSubtypesOf(imi), context, imi);
196             index.screenTitle = screenTitle;
197             indexes.add(index);
198         }
199         return indexes;
200     }
201 
202     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
203             new BaseSearchIndexProvider() {
204         @Override
205         public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
206             final InputMethodManager imm = context.getSystemService(InputMethodManager.class);
207             final List<InputMethodInfo> enabledInputMethods = imm.getEnabledInputMethodList();
208             final List<InputMethodInfo> disabledInputMethods = new ArrayList<>();
209             for (final InputMethodInfo imi : imm.getInputMethodList()) {
210                 if (!enabledInputMethods.contains(imi)) {
211                     disabledInputMethods.add(imi);
212                 }
213             }
214             final String screenTitle = context.getString(
215                     R.string.available_virtual_keyboard_category);
216             return buildSearchIndexOfInputMethods(context, disabledInputMethods, screenTitle);
217         }
218     };
219 }
220