Input settings: Associate keyboard layouts with device/IME subtype.

Implements selection of keyboard layouts using the new InputManager
methods that associate keyboard layouts to
device/InputMethodInfo/InputMethodSubtype
See: Ie88ce1ab77dbfe03ab51d89c1dc9e0a7ddbb3216

Bug: 25752812
Change-Id: Ib76880d66391ca37978054de80f4b3b5147cecc3
This commit is contained in:
Abodunrinwa Toki
2016-01-25 23:02:23 +00:00
parent 1092be7f0b
commit 5f0b59babf
6 changed files with 291 additions and 44 deletions

View File

@@ -627,7 +627,7 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.inputmethod.KeyboardLayoutPickerFragment" />
android:value="com.android.settings.inputmethod.KeyboardLayoutPickerFragment2" />
</activity>
<!-- Keep compatibility with old shortcuts. -->

View File

@@ -3548,6 +3548,10 @@
<string name="show_ime_summary">Keep it on screen while physical keyboard is active</string>
<!-- Title for the button to trigger the 'keyboard shortcuts helper' dialog. [CHAR LIMIT=35] -->
<string name="keyboard_shortcuts_helper">Keyboard shortcuts helper</string>
<!--
Format string for a physical device in the form: InputMethodSubtype - InputMethodEditor.
e.g. English (US) - X Keyboard -->
<string name="physical_device_title"><xliff:g id="input_method_subtype" example="English (US)">%1$s</xliff:g> - <xliff:g id="input_method_editor" example="X Keyboard">%2$s</xliff:g></string>
<!-- On Language & input settings screen, heading. Inside the "Language & input settings" screen, this is the header for settings that relate to mouse and trackpad devices. [CHAR LIMIT=40] -->
<string name="pointer_settings_category">Mouse/trackpad</string>

View File

@@ -80,6 +80,7 @@ import com.android.settings.fuelgauge.PowerUsageSummary;
import com.android.settings.inputmethod.AvailableVirtualKeyboardFragment;
import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
import com.android.settings.inputmethod.KeyboardLayoutPickerFragment2;
import com.android.settings.inputmethod.SpellCheckersSettings;
import com.android.settings.inputmethod.UserDictionaryList;
import com.android.settings.localepicker.LocaleListEditor;
@@ -293,6 +294,7 @@ public class SettingsActivity extends SettingsDrawerActivity
TrustedCredentialsSettings.class.getName(),
PaymentSettings.class.getName(),
KeyboardLayoutPickerFragment.class.getName(),
KeyboardLayoutPickerFragment2.class.getName(),
ZenModeSettings.class.getName(),
SoundSettings.class.getName(),
ConfigureNotificationSettings.class.getName(),

View File

@@ -301,7 +301,7 @@ public class KeyboardLayoutDialogFragment extends DialogFragment
}
}
static final class KeyboardLayoutLoader extends AsyncTaskLoader<Keyboards> {
private static final class KeyboardLayoutLoader extends AsyncTaskLoader<Keyboards> {
private final InputDeviceIdentifier mInputDeviceIdentifier;
public KeyboardLayoutLoader(Context context, InputDeviceIdentifier inputDeviceIdentifier) {

View File

@@ -0,0 +1,155 @@
/*
* Copyright (C) 2012 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.Activity;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputManager.InputDeviceListener;
import android.hardware.input.KeyboardLayout;
import android.os.Bundle;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;
import android.view.InputDevice;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.Preconditions;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public final class KeyboardLayoutPickerFragment2 extends SettingsPreferenceFragment
implements InputDeviceListener {
private InputDeviceIdentifier mInputDeviceIdentifier;
private int mInputDeviceId = -1;
private InputManager mIm;
private InputMethodInfo mImi;
private InputMethodSubtype mSubtype;
private KeyboardLayout[] mKeyboardLayouts;
private Map<Preference, KeyboardLayout> mPreferenceMap = new HashMap<>();
// TODO: Make these constants public API for b/25752827
/**
* Intent extra: The input device descriptor of the keyboard whose keyboard
* layout is to be changed.
*/
public static final String EXTRA_INPUT_DEVICE_IDENTIFIER = "input_device_identifier";
/**
* Intent extra: The associated {@link InputMethodInfo}.
*/
public static final String EXTRA_INPUT_METHOD_INFO = "input_method_info";
/**
* Intent extra: The associated {@link InputMethodSubtype}.
*/
public static final String EXTRA_INPUT_METHOD_SUBTYPE = "input_method_subtype";
@Override
protected int getMetricsCategory() {
return MetricsLogger.INPUTMETHOD_KEYBOARD;
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Activity activity = Preconditions.checkNotNull(getActivity());
mInputDeviceIdentifier = activity.getIntent().getParcelableExtra(
EXTRA_INPUT_DEVICE_IDENTIFIER);
mImi = activity.getIntent().getParcelableExtra(EXTRA_INPUT_METHOD_INFO);
mSubtype = activity.getIntent().getParcelableExtra(EXTRA_INPUT_METHOD_SUBTYPE);
if (mInputDeviceIdentifier == null || mImi == null || mSubtype == null) {
activity.finish();
}
mIm = activity.getSystemService(InputManager.class);
mKeyboardLayouts = mIm.getKeyboardLayoutsForInputDevice(mInputDeviceIdentifier);
Arrays.sort(mKeyboardLayouts);
setPreferenceScreen(createPreferenceHierarchy());
}
@Override
public void onResume() {
super.onResume();
mIm.registerInputDeviceListener(this, null);
InputDevice inputDevice =
mIm.getInputDeviceByDescriptor(mInputDeviceIdentifier.getDescriptor());
if (inputDevice == null) {
getActivity().finish();
return;
}
mInputDeviceId = inputDevice.getId();
}
@Override
public void onPause() {
mIm.unregisterInputDeviceListener(this);
mInputDeviceId = -1;
super.onPause();
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
KeyboardLayout layout = mPreferenceMap.get(preference);
if (layout != null) {
mIm.setKeyboardLayoutForInputDevice(mInputDeviceIdentifier, mImi, mSubtype,
layout.getDescriptor());
getActivity().finish();
return true;
}
return super.onPreferenceTreeClick(preference);
}
@Override
public void onInputDeviceAdded(int deviceId) {}
@Override
public void onInputDeviceChanged(int deviceId) {}
@Override
public void onInputDeviceRemoved(int deviceId) {
if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
getActivity().finish();
}
}
private PreferenceScreen createPreferenceHierarchy() {
PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity());
for (KeyboardLayout layout : mKeyboardLayouts) {
Preference pref = new Preference(getPrefContext());
pref.setTitle(layout.getLabel());
root.addPreference(pref);
mPreferenceMap.put(pref, layout);
}
root.setTitle(PhysicalKeyboardFragment.getDisplayName(getContext(), mImi, mSubtype));
return root;
}
}

View File

@@ -18,6 +18,8 @@ package com.android.settings.inputmethod;
import android.app.Activity;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.database.ContentObserver;
@@ -34,6 +36,8 @@ import android.support.v14.preference.SwitchPreference;
import android.util.Pair;
import android.view.InputDevice;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.Toast;
import com.android.internal.inputmethod.InputMethodUtils;
@@ -44,22 +48,25 @@ import com.android.settings.Settings;
import com.android.settings.SettingsPreferenceFragment;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
implements LoaderManager.LoaderCallbacks<KeyboardLayoutDialogFragment.Keyboards>,
implements LoaderManager.LoaderCallbacks<PhysicalKeyboardFragment.Keyboards>,
InputManager.InputDeviceListener {
private static final int USER_SYSTEM = 0;
private static final String KEYBOARD_ASSISTANCE_CATEGORY = "keyboard_assistance_category";
private static final String SHOW_VIRTUAL_KEYBOARD_SWITCH = "show_virtual_keyboard_switch";
private static final String KEYBOARD_SHORTCUTS_HELPER = "keyboard_shortcuts_helper";
private static final String IM_SUBTYPE_MODE_KEYBOARD = "keyboard";
private final ArrayList<PreferenceCategory> mHardKeyboardPreferenceList = new ArrayList<>();
private final HashMap<Integer, Pair<InputDeviceIdentifier, PreferenceCategory>> mLoaderReference
= new HashMap<>();
private final Map<InputMethodInfo, List<InputMethodSubtype>> mImiSubtypes = new HashMap<>();
private InputManager mIm;
private InputMethodManager mImm;
private PreferenceCategory mKeyboardAssistanceCategory;
private SwitchPreference mShowVirtualKeyboardSwitch;
private InputMethodUtils.InputMethodSettings mSettings;
@@ -69,6 +76,7 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
Activity activity = Preconditions.checkNotNull(getActivity());
addPreferencesFromResource(R.xml.physical_keyboard_settings);
mIm = Preconditions.checkNotNull(activity.getSystemService(InputManager.class));
mImm = Preconditions.checkNotNull(activity.getSystemService(InputMethodManager.class));
mSettings = new InputMethodUtils.InputMethodSettings(
activity.getResources(),
getContentResolver(),
@@ -110,29 +118,32 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
}
@Override
public Loader<KeyboardLayoutDialogFragment.Keyboards> onCreateLoader(int id, Bundle args) {
InputDeviceIdentifier deviceId = mLoaderReference.get(id).first;
return new KeyboardLayoutDialogFragment.KeyboardLayoutLoader(
getActivity().getBaseContext(), deviceId);
public Loader<Keyboards> onCreateLoader(int id, Bundle args) {
final InputDeviceIdentifier deviceId = mLoaderReference.get(id).first;
return new KeyboardLayoutLoader(
getActivity().getBaseContext(), mIm, mImiSubtypes, deviceId);
}
@Override
public void onLoadFinished(
final Loader<KeyboardLayoutDialogFragment.Keyboards> loader,
KeyboardLayoutDialogFragment.Keyboards data) {
public void onLoadFinished(Loader<Keyboards> loader, Keyboards data) {
// TODO: Investigate why this is being called twice.
final InputDeviceIdentifier deviceId = mLoaderReference.get(loader.getId()).first;
final PreferenceCategory category = mLoaderReference.get(loader.getId()).second;
category.removeAll();
for (KeyboardLayout layout : data.keyboardLayouts) {
if (layout != null) {
for (Keyboards.KeyboardInfo info : data.mInfos) {
Preference pref = new Preference(getPrefContext(), null);
pref.setTitle(layout.getLabel());
pref.setSummary(layout.getCollection());
final InputMethodInfo imi = info.mImi;
final InputMethodSubtype imSubtype = info.mImSubtype;
if (imi != null && imSubtype != null) {
pref.setTitle(getDisplayName(getContext(), imi, imSubtype));
KeyboardLayout layout = info.mLayout;
if (layout != null) {
pref.setSummary(layout.getLabel());
}
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
showKeyboardLayoutScreen(deviceId);
showKeyboardLayoutScreen(deviceId, imi, imSubtype);
return true;
}
});
@@ -142,7 +153,7 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
}
@Override
public void onLoaderReset(Loader<KeyboardLayoutDialogFragment.Keyboards> loader) {}
public void onLoaderReset(Loader<Keyboards> loader) {}
@Override
public void onInputDeviceAdded(int deviceId) {
@@ -166,38 +177,21 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
private void updateHardKeyboards() {
clearHardKeyboardsData();
loadInputMethodInfoSubtypes();
final int[] devices = InputDevice.getDeviceIds();
for (int deviceIndex = 0; deviceIndex < devices.length; deviceIndex++) {
InputDevice device = InputDevice.getDevice(devices[deviceIndex]);
if (device != null
&& !device.isVirtual()
&& device.isFullKeyboard()) {
final InputDeviceIdentifier deviceId = device.getIdentifier();
final String keyboardLayoutDescriptor =
mIm.getCurrentKeyboardLayoutForInputDevice(deviceId);
final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null;
final PreferenceCategory category = new PreferenceCategory(getPrefContext(), null);
category.setTitle(device.getName());
if (keyboardLayout != null) {
category.setSummary(keyboardLayout.toString());
} else {
category.setSummary(R.string.keyboard_layout_default_label);
}
mLoaderReference.put(deviceIndex, new Pair(deviceId, category));
mHardKeyboardPreferenceList.add(category);
}
}
Collections.sort(mHardKeyboardPreferenceList);
final int count = mHardKeyboardPreferenceList.size();
for (int i = 0; i < count; i++) {
final PreferenceCategory category = mHardKeyboardPreferenceList.get(i);
category.setOrder(i);
category.setOrder(0);
mLoaderReference.put(deviceIndex, new Pair(device.getIdentifier(), category));
getPreferenceScreen().addPreference(category);
}
mKeyboardAssistanceCategory.setOrder(count);
}
mKeyboardAssistanceCategory.setOrder(1);
getPreferenceScreen().addPreference(mKeyboardAssistanceCategory);
for (int deviceIndex : mLoaderReference.keySet()) {
@@ -206,11 +200,16 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
updateShowVirtualKeyboardSwitch();
}
private void showKeyboardLayoutScreen(InputDeviceIdentifier inputDeviceIdentifier) {
private void showKeyboardLayoutScreen(
InputDeviceIdentifier inputDeviceIdentifier,
InputMethodInfo imi,
InputMethodSubtype imSubtype) {
final Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setClass(getActivity(), Settings.KeyboardLayoutPickerActivity.class);
intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
intent.putExtra(KeyboardLayoutPickerFragment2.EXTRA_INPUT_DEVICE_IDENTIFIER,
inputDeviceIdentifier);
intent.putExtra(KeyboardLayoutPickerFragment2.EXTRA_INPUT_METHOD_INFO, imi);
intent.putExtra(KeyboardLayoutPickerFragment2.EXTRA_INPUT_METHOD_SUBTYPE, imSubtype);
startActivity(intent);
}
@@ -220,7 +219,21 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
getLoaderManager().destroyLoader(index);
}
mLoaderReference.clear();
mHardKeyboardPreferenceList.clear();
}
private void loadInputMethodInfoSubtypes() {
mImiSubtypes.clear();
final List<InputMethodInfo> imis = mImm.getEnabledInputMethodList();
for (InputMethodInfo imi : imis) {
final List<InputMethodSubtype> subtypes = new ArrayList<>();
for (InputMethodSubtype subtype : mImm.getEnabledInputMethodSubtypeList(
imi, true /* allowsImplicitlySelectedSubtypes */)) {
if (IM_SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) {
subtypes.add(subtype);
}
}
mImiSubtypes.put(imi, subtypes);
}
}
private void registerShowVirtualKeyboardSettingsObserver() {
@@ -261,4 +274,77 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment
updateShowVirtualKeyboardSwitch();
}
};
static String getDisplayName(
Context context, InputMethodInfo imi, InputMethodSubtype imSubtype) {
CharSequence imSubtypeName = imSubtype.getDisplayName(
context, imi.getPackageName(),
imi.getServiceInfo().applicationInfo);
CharSequence imeName = imi.loadLabel(context.getPackageManager());
return String.format(
context.getString(R.string.physical_device_title), imSubtypeName, imeName);
}
private static final class KeyboardLayoutLoader extends AsyncTaskLoader<Keyboards> {
private final Map<InputMethodInfo, List<InputMethodSubtype>> mImiSubtypes;
private final InputDeviceIdentifier mInputDeviceIdentifier;
private final InputManager mIm;
public KeyboardLayoutLoader(
Context context,
InputManager im,
Map<InputMethodInfo, List<InputMethodSubtype>> imiSubtypes,
InputDeviceIdentifier inputDeviceIdentifier) {
super(context);
mIm = Preconditions.checkNotNull(im);
mInputDeviceIdentifier = Preconditions.checkNotNull(inputDeviceIdentifier);
mImiSubtypes = new HashMap<>(imiSubtypes);
}
@Override
public Keyboards loadInBackground() {
final Keyboards keyboards = new Keyboards();
for (InputMethodInfo imi : mImiSubtypes.keySet()) {
for (InputMethodSubtype subtype : mImiSubtypes.get(imi)) {
final KeyboardLayout layout = mIm.getKeyboardLayoutForInputDevice(
mInputDeviceIdentifier, imi, subtype);
keyboards.mInfos.add(new Keyboards.KeyboardInfo(imi, subtype, layout));
}
}
return keyboards;
}
@Override
protected void onStartLoading() {
super.onStartLoading();
forceLoad();
}
@Override
protected void onStopLoading() {
super.onStopLoading();
cancelLoad();
}
}
public static final class Keyboards {
public final ArrayList<KeyboardInfo> mInfos = new ArrayList<>();
public static final class KeyboardInfo {
public final InputMethodInfo mImi;
public final InputMethodSubtype mImSubtype;
public final KeyboardLayout mLayout;
public KeyboardInfo(
InputMethodInfo imi, InputMethodSubtype imSubtype, KeyboardLayout layout) {
mImi = imi;
mImSubtype = imSubtype;
mLayout = layout;
}
}
}
}