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
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index dc134ff..bcb1ce9 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -80,6 +80,7 @@
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 @@
TrustedCredentialsSettings.class.getName(),
PaymentSettings.class.getName(),
KeyboardLayoutPickerFragment.class.getName(),
+ KeyboardLayoutPickerFragment2.class.getName(),
ZenModeSettings.class.getName(),
SoundSettings.class.getName(),
ConfigureNotificationSettings.class.getName(),
diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java
index 68ceeef..ad7a2b1 100644
--- a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java
+++ b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java
@@ -301,7 +301,7 @@
}
}
- 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) {
diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java
new file mode 100644
index 0000000..28c82e2
--- /dev/null
+++ b/src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java
@@ -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;
+ }
+}
diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
index 8f7d99e..74d474c 100644
--- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java
@@ -18,6 +18,8 @@
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.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.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 @@
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 @@
}
@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) {
- Preference pref = new Preference(getPrefContext(), null);
- pref.setTitle(layout.getLabel());
- pref.setSummary(layout.getCollection());
+ for (Keyboards.KeyboardInfo info : data.mInfos) {
+ Preference pref = new Preference(getPrefContext(), null);
+ 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 @@
}
@Override
- public void onLoaderReset(Loader<KeyboardLayoutDialogFragment.Keyboards> loader) {}
+ public void onLoaderReset(Loader<Keyboards> loader) {}
@Override
public void onInputDeviceAdded(int deviceId) {
@@ -166,38 +177,21 @@
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);
+ category.setOrder(0);
+ mLoaderReference.put(deviceIndex, new Pair(device.getIdentifier(), category));
+ getPreferenceScreen().addPreference(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);
- getPreferenceScreen().addPreference(category);
- }
- mKeyboardAssistanceCategory.setOrder(count);
+ mKeyboardAssistanceCategory.setOrder(1);
getPreferenceScreen().addPreference(mKeyboardAssistanceCategory);
for (int deviceIndex : mLoaderReference.keySet()) {
@@ -206,11 +200,16 @@
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 @@
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 @@
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;
+ }
+ }
+ }
+
}