diff --git a/AndroidManifest.xml b/AndroidManifest.xml index a794d8e1384..48b8eb2fef6 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -127,6 +127,7 @@ + mRemappableKeyList = + new ArrayList<>(Arrays.asList( + new int[]{KeyEvent.KEYCODE_CAPS_LOCK}, + new int[]{KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_CTRL_RIGHT}, + new int[]{KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_META_RIGHT}, + new int[]{KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT})); + + private Map mRemappableKeyMap = new HashMap<>(); public ModifierKeysPickerDialogFragment() { } - public ModifierKeysPickerDialogFragment(Preference preference) { + public ModifierKeysPickerDialogFragment(Preference preference, InputManager inputManager) { mPreference = preference; mKeyDefaultName = preference.getTitle().toString(); + mIm = inputManager; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { super.onCreateDialog(savedInstanceState); mContext = getActivity(); - String[] modifierKeys = new String[] { + List modifierKeys = new ArrayList(Arrays.asList( mContext.getString(R.string.modifier_keys_caps_lock), mContext.getString(R.string.modifier_keys_ctrl), mContext.getString(R.string.modifier_keys_meta), - mContext.getString(R.string.modifier_keys_alt)}; + mContext.getString(R.string.modifier_keys_alt))); + for (int i = 0; i < modifierKeys.size(); i++) { + mRemappableKeyMap.put(modifierKeys.get(i), mRemappableKeyList.get(i)); + } View dialoglayout = LayoutInflater.from(mContext).inflate(R.layout.modifier_key_picker_dialog, null); @@ -79,11 +98,7 @@ public class ModifierKeysPickerDialogFragment extends DialogFragment { R.string.modifier_keys_picker_summary, mKeyDefaultName); summary.setText(summaryText); - List list = new ArrayList<>(); - for (int i = 0; i < modifierKeys.length; i++) { - list.add(modifierKeys[i]); - } - ModifierKeyAdapter adapter = new ModifierKeyAdapter(list); + ModifierKeyAdapter adapter = new ModifierKeyAdapter(modifierKeys); ListView listView = dialoglayout.findViewById(R.id.modifier_key_picker); listView.setAdapter(adapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @@ -98,7 +113,7 @@ public class ModifierKeysPickerDialogFragment extends DialogFragment { AlertDialog modifierKeyDialog = dialogBuilder.create(); Button doneButton = dialoglayout.findViewById(R.id.modifier_key_done_button); doneButton.setOnClickListener(v -> { - String selectedItem = list.get(adapter.getCurrentItem()); + String selectedItem = modifierKeys.get(adapter.getCurrentItem()); Spannable itemSummary; if (selectedItem.equals(mKeyDefaultName)) { itemSummary = new SpannableString( @@ -106,12 +121,34 @@ public class ModifierKeysPickerDialogFragment extends DialogFragment { itemSummary.setSpan( new ForegroundColorSpan(getColorOfTextColorSecondary()), 0, itemSummary.length(), 0); - // TODO(b/252812993): remapModifierKey + // Set keys to default. + int[] keys = mRemappableKeyMap.get(mKeyDefaultName); + for (int i = 0; i < keys.length; i++) { + mIm.remapModifierKey(keys[i], keys[i]); + } } else { itemSummary = new SpannableString(selectedItem); itemSummary.setSpan( new ForegroundColorSpan(getColorOfColorAccentPrimaryVariant()), 0, itemSummary.length(), 0); + int[] fromKeys = mRemappableKeyMap.get(mKeyDefaultName); + int[] toKeys = mRemappableKeyMap.get(selectedItem); + // CAPS_LOCK only one key, so always choose the left key for remapping. + if (isKeyCapsLock(mContext, mKeyDefaultName)) { + mIm.remapModifierKey(fromKeys[0], toKeys[0]); + } + // Remap KEY_LEFT and KEY_RIGHT to CAPS_LOCK. + if (!isKeyCapsLock(mContext, mKeyDefaultName) + && isKeyCapsLock(mContext, selectedItem)) { + mIm.remapModifierKey(fromKeys[0], toKeys[0]); + mIm.remapModifierKey(fromKeys[1], toKeys[0]); + } + // Auto handle left and right keys remapping. + if (!isKeyCapsLock(mContext, mKeyDefaultName) + && !isKeyCapsLock(mContext, selectedItem)) { + mIm.remapModifierKey(fromKeys[0], toKeys[0]); + mIm.remapModifierKey(fromKeys[1], toKeys[1]); + } } mPreference.setSummary(itemSummary); modifierKeyDialog.dismiss(); @@ -128,6 +165,10 @@ public class ModifierKeysPickerDialogFragment extends DialogFragment { return modifierKeyDialog; } + private static boolean isKeyCapsLock(Context context, String key) { + return key.equals(context.getString(R.string.modifier_keys_caps_lock)); + } + class ModifierKeyAdapter extends BaseAdapter { private int mCurrentItem = 0; private boolean mIsClick = false; diff --git a/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java b/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java index a7f8a37af1e..69f84a2b576 100644 --- a/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java +++ b/src/com/android/settings/inputmethod/ModifierKeysPreferenceController.java @@ -17,6 +17,11 @@ package com.android.settings.inputmethod; import android.content.Context; +import android.hardware.input.InputManager; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.view.KeyEvent; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; @@ -24,18 +29,50 @@ import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.Utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; public class ModifierKeysPreferenceController extends BasePreferenceController { private static String KEY_TAG = "modifier_keys_dialog_tag"; private static String KEY_RESTORE_PREFERENCE = "modifier_keys_restore"; + private static final String KEY_PREFERENCE_CAPS_LOCK = "modifier_keys_caps_lock"; + private static final String KEY_PREFERENCE_CTRL = "modifier_keys_ctrl"; + private static final String KEY_PREFERENCE_META = "modifier_keys_meta"; + private static final String KEY_PREFERENCE_ALT = "modifier_keys_alt"; + private Fragment mParent; private FragmentManager mFragmentManager; + private final InputManager mIm; + + private final List mRemappableKeys = new ArrayList<>( + Arrays.asList( + KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_CTRL_RIGHT, + KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_META_RIGHT, + KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT, + KeyEvent.KEYCODE_CAPS_LOCK)); + + private String[] mKeyNames = new String[] { + mContext.getString(R.string.modifier_keys_ctrl), + mContext.getString(R.string.modifier_keys_ctrl), + mContext.getString(R.string.modifier_keys_meta), + mContext.getString(R.string.modifier_keys_meta), + mContext.getString(R.string.modifier_keys_alt), + mContext.getString(R.string.modifier_keys_alt), + mContext.getString(R.string.modifier_keys_caps_lock)}; public ModifierKeysPreferenceController(Context context, String key) { super(context, key); + mIm = context.getSystemService(InputManager.class); + Objects.requireNonNull(mIm, "InputManager service cannot be null"); } public void setFragment(Fragment parent) { @@ -45,12 +82,37 @@ public class ModifierKeysPreferenceController extends BasePreferenceController { @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - // TODO: getModifierKeyRemapping() - // setTitle - // setSummary + if (mParent == null) { return; } + + for (Map.Entry entry : mIm.getModifierKeyRemapping().entrySet()) { + int fromKey = entry.getKey(); + int toKey = entry.getValue(); + int index = mRemappableKeys.indexOf(toKey); + + if (isCtrl(fromKey) && mRemappableKeys.contains(toKey)) { + Preference preference = screen.findPreference(KEY_PREFERENCE_CTRL); + preference.setSummary(changeSummaryColor(mKeyNames[index])); + } + + if (isMeta(fromKey) && mRemappableKeys.contains(toKey)) { + Preference preference = screen.findPreference(KEY_PREFERENCE_META); + preference.setSummary(changeSummaryColor(mKeyNames[index])); + } + + if (isAlt(fromKey) && mRemappableKeys.contains(toKey)) { + Preference preference = screen.findPreference(KEY_PREFERENCE_ALT); + preference.setSummary(changeSummaryColor(mKeyNames[index])); + } + + if (isCapLock(fromKey) && mRemappableKeys.contains(toKey)) { + Preference preference = screen.findPreference(KEY_PREFERENCE_CAPS_LOCK); + preference.setSummary(changeSummaryColor(mKeyNames[index])); + } + } + // The dialog screen depends on the previous selected key's fragment. // In the rotation scenario, we should remove the previous dialog screen first. clearPreviousDialog(); @@ -72,7 +134,7 @@ public class ModifierKeysPreferenceController extends BasePreferenceController { private void showModifierKeysDialog(Preference preference) { ModifierKeysPickerDialogFragment fragment = - new ModifierKeysPickerDialogFragment(preference); + new ModifierKeysPickerDialogFragment(preference, mIm); fragment.setTargetFragment(mParent, 0); fragment.show(mFragmentManager, KEY_TAG); } @@ -85,4 +147,33 @@ public class ModifierKeysPreferenceController extends BasePreferenceController { preKeysDialogFragment.dismiss(); } } + + private Spannable changeSummaryColor(String summary) { + Spannable spannableSummary = new SpannableString(summary); + spannableSummary.setSpan( + new ForegroundColorSpan(getColorOfColorAccentPrimaryVariant()), + 0, spannableSummary.length(), 0); + return spannableSummary; + } + + private int getColorOfColorAccentPrimaryVariant() { + return Utils.getColorAttrDefaultColor( + mContext, com.android.internal.R.attr.colorAccentPrimaryVariant); + } + + private static boolean isCtrl(int keyCode) { + return keyCode == KeyEvent.KEYCODE_CTRL_LEFT || keyCode == KeyEvent.KEYCODE_CTRL_RIGHT; + } + + private static boolean isMeta(int keyCode) { + return keyCode == KeyEvent.KEYCODE_META_LEFT || keyCode == KeyEvent.KEYCODE_META_RIGHT; + } + + private static boolean isAlt(int keyCode) { + return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT; + } + + private static boolean isCapLock(int keyCode) { + return keyCode == KeyEvent.KEYCODE_CAPS_LOCK; + } } diff --git a/src/com/android/settings/inputmethod/ModifierKeysResetDialogFragment.java b/src/com/android/settings/inputmethod/ModifierKeysResetDialogFragment.java index 0b605a085fc..4ca5ddde6c8 100644 --- a/src/com/android/settings/inputmethod/ModifierKeysResetDialogFragment.java +++ b/src/com/android/settings/inputmethod/ModifierKeysResetDialogFragment.java @@ -21,6 +21,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; +import android.hardware.input.InputManager; import android.os.Bundle; import android.text.Spannable; import android.text.SpannableString; @@ -44,6 +45,7 @@ public class ModifierKeysResetDialogFragment extends DialogFragment { private static final String MODIFIER_KEYS_ALT = "modifier_keys_alt"; private PreferenceScreen mScreen; + private InputManager mIm; private String[] mKeys = { MODIFIER_KEYS_CAPS_LOCK, MODIFIER_KEYS_CTRL, @@ -53,8 +55,9 @@ public class ModifierKeysResetDialogFragment extends DialogFragment { public ModifierKeysResetDialogFragment() { } - public ModifierKeysResetDialogFragment(PreferenceScreen screen) { + public ModifierKeysResetDialogFragment(PreferenceScreen screen, InputManager inputManager) { mScreen = screen; + mIm = inputManager; } @Override @@ -95,7 +98,10 @@ public class ModifierKeysResetDialogFragment extends DialogFragment { 0, title.length(), 0); preference.setSummary(title); } - // TODO(b/252812993): clearAllModifierKeyRemappings() + + if (mIm != null) { + mIm.clearAllModifierKeyRemappings(); + } } private int getColorOfTextColorSecondary() { diff --git a/src/com/android/settings/inputmethod/ModifierKeysRestorePreferenceController.java b/src/com/android/settings/inputmethod/ModifierKeysRestorePreferenceController.java index 159cc11c0c1..189f4c3d500 100644 --- a/src/com/android/settings/inputmethod/ModifierKeysRestorePreferenceController.java +++ b/src/com/android/settings/inputmethod/ModifierKeysRestorePreferenceController.java @@ -17,6 +17,7 @@ package com.android.settings.inputmethod; import android.content.Context; +import android.hardware.input.InputManager; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; @@ -38,9 +39,11 @@ public class ModifierKeysRestorePreferenceController extends BasePreferenceContr private Fragment mParent; private FragmentManager mFragmentManager; private PreferenceScreen mScreen; + private final InputManager mIm; public ModifierKeysRestorePreferenceController(Context context, String key) { super(context, key); + mIm = context.getSystemService(InputManager.class); } public void setFragment(Fragment parent) { @@ -76,7 +79,7 @@ public class ModifierKeysRestorePreferenceController extends BasePreferenceContr private void showResetDialog() { ModifierKeysResetDialogFragment fragment = - new ModifierKeysResetDialogFragment(mScreen); + new ModifierKeysResetDialogFragment(mScreen, mIm); fragment.setTargetFragment(mParent, 0); fragment.show(mFragmentManager, KEY_TAG); }