From 5f0b59babfbfe483855b294098613f8d0fc2f9b4 Mon Sep 17 00:00:00 2001 From: Abodunrinwa Toki Date: Mon, 25 Jan 2016 23:02:23 +0000 Subject: [PATCH] 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 --- AndroidManifest.xml | 2 +- res/values/strings.xml | 4 + .../android/settings/SettingsActivity.java | 2 + .../KeyboardLayoutDialogFragment.java | 2 +- .../KeyboardLayoutPickerFragment2.java | 155 ++++++++++++++++ .../inputmethod/PhysicalKeyboardFragment.java | 170 +++++++++++++----- 6 files changed, 291 insertions(+), 44 deletions(-) create mode 100644 src/com/android/settings/inputmethod/KeyboardLayoutPickerFragment2.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4366b0b876c..dfc6787fbbd 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -627,7 +627,7 @@ + android:value="com.android.settings.inputmethod.KeyboardLayoutPickerFragment2" /> diff --git a/res/values/strings.xml b/res/values/strings.xml index bbd97a42c92..51b23250488 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3548,6 +3548,10 @@ Keep it on screen while physical keyboard is active Keyboard shortcuts helper + + %1$s - %2$s Mouse/trackpad diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java index dc134ff4a32..bcb1ce96211 100644 --- a/src/com/android/settings/SettingsActivity.java +++ b/src/com/android/settings/SettingsActivity.java @@ -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(), diff --git a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java index 68ceeefc062..ad7a2b167a5 100644 --- a/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java +++ b/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java @@ -301,7 +301,7 @@ public class KeyboardLayoutDialogFragment extends DialogFragment } } - static final class KeyboardLayoutLoader extends AsyncTaskLoader { + private static final class KeyboardLayoutLoader extends AsyncTaskLoader { 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 00000000000..28c82e25a18 --- /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 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 8f7d99e2595..74d474c45e1 100644 --- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java +++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java @@ -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, + implements LoaderManager.LoaderCallbacks, 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 mHardKeyboardPreferenceList = new ArrayList<>(); private final HashMap> mLoaderReference = new HashMap<>(); + private final Map> 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 onCreateLoader(int id, Bundle args) { - InputDeviceIdentifier deviceId = mLoaderReference.get(id).first; - return new KeyboardLayoutDialogFragment.KeyboardLayoutLoader( - getActivity().getBaseContext(), deviceId); + public Loader 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 loader, - KeyboardLayoutDialogFragment.Keyboards data) { + public void onLoadFinished(Loader 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 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment } @Override - public void onLoaderReset(Loader loader) {} + public void onLoaderReset(Loader 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); + 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 @@ 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 imis = mImm.getEnabledInputMethodList(); + for (InputMethodInfo imi : imis) { + final List 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 { + + private final Map> mImiSubtypes; + private final InputDeviceIdentifier mInputDeviceIdentifier; + private final InputManager mIm; + + public KeyboardLayoutLoader( + Context context, + InputManager im, + Map> 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 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; + } + } + } + }