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 17package com.android.settings.inputmethod; 18 19import android.annotation.DrawableRes; 20import android.annotation.NonNull; 21import android.annotation.Nullable; 22import android.app.Activity; 23import android.app.admin.DevicePolicyManager; 24import android.content.Context; 25import android.content.pm.ApplicationInfo; 26import android.content.pm.PackageManager; 27import android.content.pm.ServiceInfo; 28import android.content.res.Configuration; 29import android.graphics.Color; 30import android.graphics.drawable.ColorDrawable; 31import android.graphics.drawable.Drawable; 32import android.os.Bundle; 33import android.provider.SearchIndexableResource; 34import android.view.inputmethod.InputMethodInfo; 35import android.view.inputmethod.InputMethodManager; 36 37import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 38import com.android.settings.R; 39import com.android.settings.SettingsPreferenceFragment; 40import com.android.settings.search.BaseSearchIndexProvider; 41import com.android.settings.search.Indexable; 42import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil; 43import com.android.settingslib.inputmethod.InputMethodPreference; 44import com.android.settingslib.inputmethod.InputMethodSettingValuesWrapper; 45 46import java.text.Collator; 47import java.util.ArrayList; 48import java.util.List; 49 50public final class AvailableVirtualKeyboardFragment extends SettingsPreferenceFragment 51 implements InputMethodPreference.OnSavePreferenceListener, Indexable { 52 53 private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>(); 54 private InputMethodSettingValuesWrapper mInputMethodSettingValues; 55 private InputMethodManager mImm; 56 private DevicePolicyManager mDpm; 57 58 @Override 59 public void onCreatePreferences(Bundle bundle, String s) { 60 addPreferencesFromResource(R.xml.available_virtual_keyboard); 61 Activity activity = getActivity(); 62 63 mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity); 64 mImm = activity.getSystemService(InputMethodManager.class); 65 mDpm = activity.getSystemService(DevicePolicyManager.class); 66 } 67 68 @Override 69 public void onResume() { 70 super.onResume(); 71 // Refresh internal states in mInputMethodSettingValues to keep the latest 72 // "InputMethodInfo"s and "InputMethodSubtype"s 73 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes(); 74 updateInputMethodPreferenceViews(); 75 } 76 77 @Override 78 public void onSaveInputMethodPreference(final InputMethodPreference pref) { 79 final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard 80 == Configuration.KEYBOARD_QWERTY; 81 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(), 82 mImm.getInputMethodList(), hasHardwareKeyboard); 83 // Update input method settings and preference list. 84 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes(); 85 for (final InputMethodPreference p : mInputMethodPreferenceList) { 86 p.updatePreferenceViews(); 87 } 88 } 89 90 @Override 91 public int getMetricsCategory() { 92 return MetricsEvent.ENABLE_VIRTUAL_KEYBOARDS; 93 } 94 95 @Nullable 96 private static Drawable loadDrawable(@NonNull final PackageManager packageManager, 97 @NonNull final String packageName, @DrawableRes final int resId, 98 @NonNull final ApplicationInfo applicationInfo) { 99 if (resId == 0) { 100 return null; 101 } 102 try { 103 return packageManager.getDrawable(packageName, resId, applicationInfo); 104 } catch (Exception e) { 105 return null; 106 } 107 } 108 109 @NonNull 110 private static Drawable getInputMethodIcon(@NonNull final PackageManager packageManager, 111 @NonNull final InputMethodInfo imi) { 112 final ServiceInfo si = imi.getServiceInfo(); 113 final ApplicationInfo ai = si != null ? si.applicationInfo : null; 114 final String packageName = imi.getPackageName(); 115 if (si == null || ai == null || packageName == null) { 116 return new ColorDrawable(Color.TRANSPARENT); 117 } 118 // We do not use ServiceInfo#loadLogo() and ServiceInfo#loadIcon here since those methods 119 // internally have some fallback rules, which we want to do manually. 120 Drawable drawable = loadDrawable(packageManager, packageName, si.logo, ai); 121 if (drawable != null) { 122 return drawable; 123 } 124 drawable = loadDrawable(packageManager, packageName, si.icon, ai); 125 if (drawable != null) { 126 return drawable; 127 } 128 // We do not use ApplicationInfo#loadLogo() and ApplicationInfo#loadIcon here since those 129 // methods internally have some fallback rules, which we want to do manually. 130 drawable = loadDrawable(packageManager, packageName, ai.logo, ai); 131 if (drawable != null) { 132 return drawable; 133 } 134 drawable = loadDrawable(packageManager, packageName, ai.icon, ai); 135 if (drawable != null) { 136 return drawable; 137 } 138 return new ColorDrawable(Color.TRANSPARENT); 139 } 140 141 private void updateInputMethodPreferenceViews() { 142 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes(); 143 // Clear existing "InputMethodPreference"s 144 mInputMethodPreferenceList.clear(); 145 List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser(); 146 final Context context = getPrefContext(); 147 final PackageManager packageManager = getActivity().getPackageManager(); 148 final List<InputMethodInfo> imis = mInputMethodSettingValues.getInputMethodList(); 149 final int numImis = (imis == null ? 0 : imis.size()); 150 for (int i = 0; i < numImis; ++i) { 151 final InputMethodInfo imi = imis.get(i); 152 final boolean isAllowedByOrganization = permittedList == null 153 || permittedList.contains(imi.getPackageName()); 154 final InputMethodPreference pref = new InputMethodPreference( 155 context, imi, true, isAllowedByOrganization, this); 156 pref.setIcon(getInputMethodIcon(packageManager, imi)); 157 mInputMethodPreferenceList.add(pref); 158 } 159 final Collator collator = Collator.getInstance(); 160 mInputMethodPreferenceList.sort((lhs, rhs) -> lhs.compareTo(rhs, collator)); 161 getPreferenceScreen().removeAll(); 162 for (int i = 0; i < numImis; ++i) { 163 final InputMethodPreference pref = mInputMethodPreferenceList.get(i); 164 pref.setOrder(i); 165 getPreferenceScreen().addPreference(pref); 166 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref); 167 pref.updatePreferenceViews(); 168 } 169 } 170 171 public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 172 new BaseSearchIndexProvider() { 173 @Override 174 public List<SearchIndexableResource> getXmlResourcesToIndex(Context context, 175 boolean enabled) { 176 List<SearchIndexableResource> res = new ArrayList<>(); 177 SearchIndexableResource index = new SearchIndexableResource(context); 178 index.xmlResId = R.xml.available_virtual_keyboard; 179 res.add(index); 180 return res; 181 } 182 }; 183} 184