Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aa1d9c4d authored by danielwbhuang's avatar danielwbhuang
Browse files

Integrate modifier keys settings UI with APIs

1. getModifierKeyRemapping
2. remapModifierKey
3. clearAllModifierKeyRemappings

Demo: go/modifier_keys_settings_demo

Bug: 244535460
Test: local test
Change-Id: I47bcd0b58637feb68c579112a991371490af0157
parent 4525cfb4
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -127,6 +127,7 @@
    <uses-permission android:name="android.permission.START_VIEW_APP_FEATURES" />
    <uses-permission android:name="android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS" />
    <uses-permission android:name="android.permission.CUSTOMIZE_SYSTEM_UI" />
    <uses-permission android:name="android.permission.REMAP_MODIFIER_KEYS" />

    <application
            android:name=".SettingsApplication"
+51 −10
Original line number Diff line number Diff line
@@ -21,10 +21,12 @@ 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;
import android.text.style.ForegroundColorSpan;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -43,31 +45,48 @@ import com.android.settings.R;
import com.android.settingslib.Utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ModifierKeysPickerDialogFragment extends DialogFragment {

    private Preference mPreference;
    private String mKeyDefaultName;
    private Context mContext;
    private InputManager mIm;

    private List<int[]> 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<String, int[]> 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<String> modifierKeys = new ArrayList<String>(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<String> 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;
+95 −4
Original line number Diff line number Diff line
@@ -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<Integer> 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<Integer, Integer> 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;
    }
}
+8 −2
Original line number Diff line number Diff line
@@ -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() {
+4 −1
Original line number Diff line number Diff line
@@ -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);
    }