From UX mocks, we need to show IME names in heading if there are multiple IMEs. But in situation where we have multiple IMEs but one of the IMEs doesn't have any valid subtype for PK, it should work as if there is only a single IME. Bug: 361528038 Test: manual Flag: EXEMPT bugfix Change-Id: Idd03a224b300df31b3265a5d58f79c0fc5d1535a
339 lines
13 KiB
Java
339 lines
13 KiB
Java
/*
|
|
* Copyright (C) 2022 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.settings.inputmethod;
|
|
|
|
import android.app.settings.SettingsEnums;
|
|
import android.content.Context;
|
|
import android.hardware.input.InputDeviceIdentifier;
|
|
import android.hardware.input.InputManager;
|
|
import android.hardware.input.KeyboardLayout;
|
|
import android.hardware.input.KeyboardLayoutSelectionResult;
|
|
import android.os.Bundle;
|
|
import android.os.UserHandle;
|
|
import android.os.UserManager;
|
|
import android.util.Log;
|
|
import android.view.InputDevice;
|
|
import android.view.inputmethod.InputMethodInfo;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
import android.view.inputmethod.InputMethodSubtype;
|
|
|
|
import androidx.preference.Preference;
|
|
import androidx.preference.PreferenceCategory;
|
|
import androidx.preference.PreferenceScreen;
|
|
|
|
import com.android.settings.R;
|
|
import com.android.settings.Utils;
|
|
import com.android.settings.core.SubSettingLauncher;
|
|
import com.android.settings.dashboard.DashboardFragment;
|
|
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
|
|
import com.android.settings.inputmethod.NewKeyboardSettingsUtils.KeyboardInfo;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Comparator;
|
|
import java.util.List;
|
|
|
|
public class NewKeyboardLayoutEnabledLocalesFragment extends DashboardFragment
|
|
implements InputManager.InputDeviceListener {
|
|
|
|
private static final String TAG = "NewKeyboardLayoutEnabledLocalesFragment";
|
|
|
|
private InputManager mIm;
|
|
private InputMethodManager mImm;
|
|
private InputDeviceIdentifier mInputDeviceIdentifier;
|
|
private int mUserId;
|
|
private int mInputDeviceId;
|
|
private Context mContext;
|
|
private ArrayList<KeyboardInfo> mKeyboardInfoList = new ArrayList<>();
|
|
|
|
@Override
|
|
public void onAttach(Context context) {
|
|
super.onAttach(context);
|
|
|
|
mContext = context;
|
|
final int profileType = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE);
|
|
final int currentUserId = UserHandle.myUserId();
|
|
final int newUserId;
|
|
final UserManager userManager = mContext.getSystemService(UserManager.class);
|
|
|
|
switch (profileType) {
|
|
case ProfileSelectFragment.ProfileType.WORK: {
|
|
// If the user is a managed profile user, use currentUserId directly. Or get the
|
|
// managed profile userId instead.
|
|
newUserId = userManager.isManagedProfile()
|
|
? currentUserId : Utils.getManagedProfileId(userManager, currentUserId);
|
|
break;
|
|
}
|
|
case ProfileSelectFragment.ProfileType.PRIVATE: {
|
|
// If the user is a private profile user, use currentUserId directly. Or get the
|
|
// private profile userId instead.
|
|
newUserId = userManager.isPrivateProfile()
|
|
? currentUserId
|
|
: Utils.getCurrentUserIdOfType(
|
|
userManager, ProfileSelectFragment.ProfileType.PRIVATE);
|
|
break;
|
|
}
|
|
case ProfileSelectFragment.ProfileType.PERSONAL: {
|
|
// Use the parent user of the current user if the current user is profile.
|
|
final UserHandle currentUser = UserHandle.of(currentUserId);
|
|
final UserHandle userProfileParent = userManager.getProfileParent(currentUser);
|
|
if (userProfileParent != null) {
|
|
newUserId = userProfileParent.getIdentifier();
|
|
} else {
|
|
newUserId = currentUserId;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
newUserId = currentUserId;
|
|
}
|
|
|
|
mUserId = newUserId;
|
|
mIm = mContext.getSystemService(InputManager.class);
|
|
mImm = mContext.getSystemService(InputMethodManager.class);
|
|
mInputDeviceId = -1;
|
|
}
|
|
|
|
@Override
|
|
public void onActivityCreated(final Bundle icicle) {
|
|
super.onActivityCreated(icicle);
|
|
Bundle arguments = getArguments();
|
|
if (arguments == null) {
|
|
Log.e(TAG, "Arguments should not be null");
|
|
return;
|
|
}
|
|
mInputDeviceIdentifier =
|
|
arguments.getParcelable(NewKeyboardSettingsUtils.EXTRA_INPUT_DEVICE_IDENTIFIER,
|
|
InputDeviceIdentifier.class);
|
|
if (mInputDeviceIdentifier == null) {
|
|
Log.e(TAG, "The inputDeviceIdentifier should not be null");
|
|
return;
|
|
}
|
|
InputDevice inputDevice =
|
|
NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier);
|
|
if (inputDevice == null) {
|
|
Log.e(TAG, "inputDevice is null");
|
|
return;
|
|
}
|
|
final String title = inputDevice.getName();
|
|
getActivity().setTitle(title);
|
|
}
|
|
|
|
@Override
|
|
public void onStart() {
|
|
super.onStart();
|
|
mIm.registerInputDeviceListener(this, null);
|
|
InputDevice inputDevice =
|
|
NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier);
|
|
if (inputDevice == null) {
|
|
Log.e(TAG, "Unable to start: input device is null");
|
|
getActivity().finish();
|
|
return;
|
|
}
|
|
mInputDeviceId = inputDevice.getId();
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
updateCheckedState();
|
|
}
|
|
|
|
@Override
|
|
public void onStop() {
|
|
super.onStop();
|
|
mIm.unregisterInputDeviceListener(this);
|
|
mInputDeviceId = -1;
|
|
}
|
|
|
|
private void updateCheckedState() {
|
|
if (NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier) == null) {
|
|
return;
|
|
}
|
|
|
|
PreferenceScreen preferenceScreen = getPreferenceScreen();
|
|
preferenceScreen.removeAll();
|
|
List<InputMethodInfo> infoList =
|
|
mImm.getEnabledInputMethodListAsUser(UserHandle.of(mUserId));
|
|
|
|
// Remove IMEs with no suitable ime subtypes
|
|
infoList.removeIf(imeInfo -> {
|
|
List<InputMethodSubtype> subtypes =
|
|
mImm.getEnabledInputMethodSubtypeListAsUser(imeInfo.getId(), true,
|
|
UserHandle.of(mUserId));
|
|
for (InputMethodSubtype subtype : subtypes) {
|
|
if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
});
|
|
Collections.sort(infoList, new Comparator<InputMethodInfo>() {
|
|
public int compare(InputMethodInfo o1, InputMethodInfo o2) {
|
|
String s1 = o1.loadLabel(mContext.getPackageManager()).toString();
|
|
String s2 = o2.loadLabel(mContext.getPackageManager()).toString();
|
|
return s1.compareTo(s2);
|
|
}
|
|
});
|
|
|
|
for (InputMethodInfo info : infoList) {
|
|
mKeyboardInfoList.clear();
|
|
List<InputMethodSubtype> subtypes =
|
|
mImm.getEnabledInputMethodSubtypeListAsUser(info.getId(), true,
|
|
UserHandle.of(mUserId));
|
|
for (InputMethodSubtype subtype : subtypes) {
|
|
if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
|
|
mapLanguageWithLayout(info, subtype);
|
|
}
|
|
}
|
|
updatePreferenceLayout(preferenceScreen, info, infoList.size() > 1);
|
|
}
|
|
}
|
|
|
|
private void mapLanguageWithLayout(InputMethodInfo info, InputMethodSubtype subtype) {
|
|
CharSequence subtypeLabel = getSubtypeLabel(mContext, info, subtype);
|
|
KeyboardLayout[] keyboardLayouts =
|
|
NewKeyboardSettingsUtils.getKeyboardLayouts(
|
|
mIm, mUserId, mInputDeviceIdentifier, info, subtype);
|
|
KeyboardLayoutSelectionResult result = NewKeyboardSettingsUtils.getKeyboardLayout(
|
|
mIm, mUserId, mInputDeviceIdentifier, info, subtype);
|
|
if (result.getLayoutDescriptor() != null) {
|
|
for (int i = 0; i < keyboardLayouts.length; i++) {
|
|
if (keyboardLayouts[i].getDescriptor().equals(result.getLayoutDescriptor())) {
|
|
KeyboardInfo keyboardInfo = new KeyboardInfo(
|
|
subtypeLabel,
|
|
keyboardLayouts[i].getLabel(),
|
|
result.getSelectionCriteria(),
|
|
info,
|
|
subtype);
|
|
mKeyboardInfoList.add(keyboardInfo);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// if there is no auto-selected layout, we should show "Default"
|
|
KeyboardInfo keyboardInfo = new KeyboardInfo(
|
|
subtypeLabel,
|
|
mContext.getString(R.string.keyboard_default_layout),
|
|
KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_UNSPECIFIED,
|
|
info,
|
|
subtype);
|
|
mKeyboardInfoList.add(keyboardInfo);
|
|
}
|
|
}
|
|
|
|
private void updatePreferenceLayout(PreferenceScreen preferenceScreen, InputMethodInfo info,
|
|
boolean hasMultipleImes) {
|
|
if (mKeyboardInfoList.isEmpty()) {
|
|
return;
|
|
}
|
|
PreferenceCategory preferenceCategory = new PreferenceCategory(mContext);
|
|
preferenceCategory.setTitle(hasMultipleImes ? mContext.getString(R.string.ime_label_title,
|
|
info.loadLabel(mContext.getPackageManager()))
|
|
: mContext.getString(R.string.enabled_locales_keyboard_layout));
|
|
preferenceCategory.setKey(info.getPackageName());
|
|
preferenceScreen.addPreference(preferenceCategory);
|
|
Collections.sort(mKeyboardInfoList, new Comparator<KeyboardInfo>() {
|
|
public int compare(KeyboardInfo o1, KeyboardInfo o2) {
|
|
String s1 = o1.getSubtypeLabel().toString();
|
|
String s2 = o2.getSubtypeLabel().toString();
|
|
return s1.compareTo(s2);
|
|
}
|
|
});
|
|
|
|
for (KeyboardInfo keyboardInfo : mKeyboardInfoList) {
|
|
final Preference pref = new Preference(mContext);
|
|
pref.setKey(keyboardInfo.getPrefId());
|
|
pref.setTitle(keyboardInfo.getSubtypeLabel());
|
|
pref.setSummary(keyboardInfo.getLayoutSummaryText(mContext));
|
|
pref.setOnPreferenceClickListener(
|
|
preference -> {
|
|
showKeyboardLayoutPicker(
|
|
keyboardInfo.getSubtypeLabel(),
|
|
mInputDeviceIdentifier,
|
|
mUserId,
|
|
keyboardInfo.getInputMethodInfo(),
|
|
keyboardInfo.getInputMethodSubtype());
|
|
return true;
|
|
});
|
|
preferenceCategory.addPreference(pref);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onInputDeviceAdded(int deviceId) {
|
|
// Do nothing.
|
|
}
|
|
|
|
@Override
|
|
public void onInputDeviceRemoved(int deviceId) {
|
|
if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
|
|
getActivity().finish();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onInputDeviceChanged(int deviceId) {
|
|
if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
|
|
updateCheckedState();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected String getLogTag() {
|
|
return TAG;
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return SettingsEnums.SETTINGS_KEYBOARDS_ENABLED_LOCALES;
|
|
}
|
|
|
|
@Override
|
|
protected int getPreferenceScreenResId() {
|
|
return R.xml.keyboard_settings_enabled_locales_list;
|
|
}
|
|
|
|
private void showKeyboardLayoutPicker(
|
|
CharSequence subtypeLabel,
|
|
InputDeviceIdentifier inputDeviceIdentifier,
|
|
int userId,
|
|
InputMethodInfo inputMethodInfo,
|
|
InputMethodSubtype inputMethodSubtype) {
|
|
Bundle arguments = new Bundle();
|
|
arguments.putParcelable(
|
|
NewKeyboardSettingsUtils.EXTRA_INPUT_DEVICE_IDENTIFIER, inputDeviceIdentifier);
|
|
arguments.putParcelable(
|
|
NewKeyboardSettingsUtils.EXTRA_INPUT_METHOD_INFO, inputMethodInfo);
|
|
arguments.putParcelable(
|
|
NewKeyboardSettingsUtils.EXTRA_INPUT_METHOD_SUBTYPE, inputMethodSubtype);
|
|
arguments.putInt(NewKeyboardSettingsUtils.EXTRA_USER_ID, userId);
|
|
arguments.putCharSequence(NewKeyboardSettingsUtils.EXTRA_TITLE, subtypeLabel);
|
|
new SubSettingLauncher(mContext)
|
|
.setSourceMetricsCategory(getMetricsCategory())
|
|
.setDestination(NewKeyboardLayoutPickerFragment.class.getName())
|
|
.setArguments(arguments)
|
|
.launch();
|
|
}
|
|
|
|
private CharSequence getSubtypeLabel(
|
|
Context context, InputMethodInfo info, InputMethodSubtype subtype) {
|
|
return subtype.getDisplayName(
|
|
context, info.getPackageName(), info.getServiceInfo().applicationInfo);
|
|
}
|
|
}
|