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