1 /* 2 * Copyright (C) 2022 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.app.settings.SettingsEnums; 20 import android.content.Context; 21 import android.hardware.input.InputDeviceIdentifier; 22 import android.hardware.input.InputManager; 23 import android.hardware.input.KeyboardLayout; 24 import android.os.Bundle; 25 import android.os.UserHandle; 26 import android.os.UserManager; 27 import android.util.Log; 28 import android.view.InputDevice; 29 import android.view.inputmethod.InputMethodInfo; 30 import android.view.inputmethod.InputMethodManager; 31 import android.view.inputmethod.InputMethodSubtype; 32 33 import androidx.preference.Preference; 34 import androidx.preference.PreferenceCategory; 35 import androidx.preference.PreferenceScreen; 36 37 import com.android.settings.R; 38 import com.android.settings.Utils; 39 import com.android.settings.core.SubSettingLauncher; 40 import com.android.settings.dashboard.DashboardFragment; 41 import com.android.settings.dashboard.profileselector.ProfileSelectFragment; 42 import com.android.settings.inputmethod.NewKeyboardSettingsUtils.KeyboardInfo; 43 44 import java.util.ArrayList; 45 import java.util.Collections; 46 import java.util.Comparator; 47 import java.util.List; 48 49 public class NewKeyboardLayoutEnabledLocalesFragment extends DashboardFragment 50 implements InputManager.InputDeviceListener { 51 52 private static final String TAG = "NewKeyboardLayoutEnabledLocalesFragment"; 53 54 private InputManager mIm; 55 private InputMethodManager mImm; 56 private InputDeviceIdentifier mInputDeviceIdentifier; 57 private int mUserId; 58 private int mInputDeviceId; 59 private Context mContext; 60 private ArrayList<KeyboardInfo> mKeyboardInfoList = new ArrayList<>(); 61 62 @Override onAttach(Context context)63 public void onAttach(Context context) { 64 super.onAttach(context); 65 66 mContext = context; 67 final int profileType = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE); 68 final int currentUserId = UserHandle.myUserId(); 69 final int newUserId; 70 final UserManager userManager = mContext.getSystemService(UserManager.class); 71 72 switch (profileType) { 73 case ProfileSelectFragment.ProfileType.WORK: { 74 // If the user is a managed profile user, use currentUserId directly. Or get the 75 // managed profile userId instead. 76 newUserId = userManager.isManagedProfile() 77 ? currentUserId : Utils.getManagedProfileId(userManager, currentUserId); 78 break; 79 } 80 case ProfileSelectFragment.ProfileType.PERSONAL: { 81 final UserHandle primaryUser = userManager.getPrimaryUser().getUserHandle(); 82 newUserId = primaryUser.getIdentifier(); 83 break; 84 } 85 default: 86 newUserId = currentUserId; 87 } 88 89 mUserId = newUserId; 90 mIm = mContext.getSystemService(InputManager.class); 91 mImm = mContext.getSystemService(InputMethodManager.class); 92 mInputDeviceId = -1; 93 } 94 95 @Override onActivityCreated(final Bundle icicle)96 public void onActivityCreated(final Bundle icicle) { 97 super.onActivityCreated(icicle); 98 Bundle arguments = getArguments(); 99 mInputDeviceIdentifier = 100 arguments.getParcelable(NewKeyboardSettingsUtils.EXTRA_INPUT_DEVICE_IDENTIFIER); 101 if (mInputDeviceIdentifier == null) { 102 Log.e(TAG, "The inputDeviceIdentifier should not be null"); 103 return; 104 } 105 InputDevice inputDevice = 106 NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier); 107 if (inputDevice == null) { 108 Log.e(TAG, "inputDevice is null"); 109 return; 110 } 111 final String title = inputDevice.getName(); 112 getActivity().setTitle(title); 113 } 114 115 @Override onStart()116 public void onStart() { 117 super.onStart(); 118 mIm.registerInputDeviceListener(this, null); 119 InputDevice inputDevice = 120 NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier); 121 if (inputDevice == null) { 122 getActivity().finish(); 123 return; 124 } 125 mInputDeviceId = inputDevice.getId(); 126 } 127 128 @Override onResume()129 public void onResume() { 130 super.onResume(); 131 updateCheckedState(); 132 } 133 134 @Override onStop()135 public void onStop() { 136 super.onStop(); 137 mIm.unregisterInputDeviceListener(this); 138 mInputDeviceId = -1; 139 } 140 updateCheckedState()141 private void updateCheckedState() { 142 if (NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier) == null) { 143 return; 144 } 145 146 PreferenceScreen preferenceScreen = getPreferenceScreen(); 147 preferenceScreen.removeAll(); 148 List<InputMethodInfo> infoList = mImm.getEnabledInputMethodListAsUser(mUserId); 149 Collections.sort(infoList, new Comparator<InputMethodInfo>() { 150 public int compare(InputMethodInfo o1, InputMethodInfo o2) { 151 String s1 = o1.loadLabel(mContext.getPackageManager()).toString(); 152 String s2 = o2.loadLabel(mContext.getPackageManager()).toString(); 153 return s1.compareTo(s2); 154 } 155 }); 156 157 for (InputMethodInfo info : infoList) { 158 mKeyboardInfoList.clear(); 159 List<InputMethodSubtype> subtypes = 160 mImm.getEnabledInputMethodSubtypeListAsUser(info.getId(), true, mUserId); 161 for (InputMethodSubtype subtype : subtypes) { 162 if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) { 163 mapLanguageWithLayout(info, subtype); 164 } 165 } 166 updatePreferenceLayout(preferenceScreen, info); 167 } 168 } 169 mapLanguageWithLayout(InputMethodInfo info, InputMethodSubtype subtype)170 private void mapLanguageWithLayout(InputMethodInfo info, InputMethodSubtype subtype) { 171 CharSequence subtypeLabel = getSubtypeLabel(mContext, info, subtype); 172 KeyboardLayout[] keyboardLayouts = 173 NewKeyboardSettingsUtils.getKeyboardLayouts( 174 mIm, mUserId, mInputDeviceIdentifier, info, subtype); 175 String layout = NewKeyboardSettingsUtils.getKeyboardLayout( 176 mIm, mUserId, mInputDeviceIdentifier, info, subtype); 177 if (layout != null) { 178 for (int i = 0; i < keyboardLayouts.length; i++) { 179 if (keyboardLayouts[i].getDescriptor().equals(layout)) { 180 KeyboardInfo keyboardInfo = new KeyboardInfo( 181 subtypeLabel, 182 keyboardLayouts[i].getLabel(), 183 info, 184 subtype); 185 mKeyboardInfoList.add(keyboardInfo); 186 break; 187 } 188 } 189 } else { 190 // if there is no auto-selected layout, we should show "Default" 191 KeyboardInfo keyboardInfo = new KeyboardInfo( 192 subtypeLabel, 193 mContext.getString(R.string.keyboard_default_layout), 194 info, 195 subtype); 196 mKeyboardInfoList.add(keyboardInfo); 197 } 198 } 199 updatePreferenceLayout(PreferenceScreen preferenceScreen, InputMethodInfo info)200 private void updatePreferenceLayout(PreferenceScreen preferenceScreen, InputMethodInfo info) { 201 if (mKeyboardInfoList.isEmpty()) { 202 return; 203 } 204 PreferenceCategory preferenceCategory = new PreferenceCategory(mContext); 205 preferenceCategory.setTitle(info.loadLabel(mContext.getPackageManager())); 206 preferenceCategory.setKey(info.getPackageName()); 207 preferenceScreen.addPreference(preferenceCategory); 208 Collections.sort(mKeyboardInfoList, new Comparator<KeyboardInfo>() { 209 public int compare(KeyboardInfo o1, KeyboardInfo o2) { 210 String s1 = o1.getSubtypeLabel().toString(); 211 String s2 = o2.getSubtypeLabel().toString(); 212 return s1.compareTo(s2); 213 } 214 }); 215 216 for (KeyboardInfo keyboardInfo : mKeyboardInfoList) { 217 final Preference pref = new Preference(mContext); 218 pref.setKey(keyboardInfo.getPrefId()); 219 pref.setTitle(keyboardInfo.getSubtypeLabel()); 220 pref.setSummary(keyboardInfo.getLayout()); 221 pref.setOnPreferenceClickListener( 222 preference -> { 223 showKeyboardLayoutPicker( 224 keyboardInfo.getSubtypeLabel(), 225 mInputDeviceIdentifier, 226 mUserId, 227 keyboardInfo.getInputMethodInfo(), 228 keyboardInfo.getInputMethodSubtype()); 229 return true; 230 }); 231 preferenceCategory.addPreference(pref); 232 } 233 } 234 235 @Override onInputDeviceAdded(int deviceId)236 public void onInputDeviceAdded(int deviceId) { 237 // Do nothing. 238 } 239 240 @Override onInputDeviceRemoved(int deviceId)241 public void onInputDeviceRemoved(int deviceId) { 242 if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) { 243 getActivity().finish(); 244 } 245 } 246 247 @Override onInputDeviceChanged(int deviceId)248 public void onInputDeviceChanged(int deviceId) { 249 if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) { 250 updateCheckedState(); 251 } 252 } 253 254 @Override getLogTag()255 protected String getLogTag() { 256 return TAG; 257 } 258 259 @Override getMetricsCategory()260 public int getMetricsCategory() { 261 return SettingsEnums.SETTINGS_KEYBOARDS_ENABLED_LOCALES; 262 } 263 264 @Override getPreferenceScreenResId()265 protected int getPreferenceScreenResId() { 266 return R.xml.keyboard_settings_enabled_locales_list; 267 } 268 showKeyboardLayoutPicker( CharSequence subtypeLabel, InputDeviceIdentifier inputDeviceIdentifier, int userId, InputMethodInfo inputMethodInfo, InputMethodSubtype inputMethodSubtype)269 private void showKeyboardLayoutPicker( 270 CharSequence subtypeLabel, 271 InputDeviceIdentifier inputDeviceIdentifier, 272 int userId, 273 InputMethodInfo inputMethodInfo, 274 InputMethodSubtype inputMethodSubtype) { 275 Bundle arguments = new Bundle(); 276 arguments.putParcelable( 277 NewKeyboardSettingsUtils.EXTRA_INPUT_DEVICE_IDENTIFIER, inputDeviceIdentifier); 278 arguments.putParcelable( 279 NewKeyboardSettingsUtils.EXTRA_INPUT_METHOD_INFO, inputMethodInfo); 280 arguments.putParcelable( 281 NewKeyboardSettingsUtils.EXTRA_INPUT_METHOD_SUBTYPE, inputMethodSubtype); 282 arguments.putInt(NewKeyboardSettingsUtils.EXTRA_USER_ID, userId); 283 arguments.putCharSequence(NewKeyboardSettingsUtils.EXTRA_TITLE, subtypeLabel); 284 new SubSettingLauncher(mContext) 285 .setSourceMetricsCategory(getMetricsCategory()) 286 .setDestination(NewKeyboardLayoutPickerFragment.class.getName()) 287 .setArguments(arguments) 288 .launch(); 289 } 290 getSubtypeLabel( Context context, InputMethodInfo info, InputMethodSubtype subtype)291 private CharSequence getSubtypeLabel( 292 Context context, InputMethodInfo info, InputMethodSubtype subtype) { 293 return subtype.getDisplayName( 294 context, info.getPackageName(), info.getServiceInfo().applicationInfo); 295 } 296 } 297