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