1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.inputmethod; 18 19 import android.content.Context; 20 import android.graphics.drawable.Drawable; 21 import android.hardware.input.InputManager; 22 import android.os.Bundle; 23 import android.text.Spannable; 24 import android.text.SpannableString; 25 import android.text.style.ForegroundColorSpan; 26 import android.util.Pair; 27 import android.view.KeyEvent; 28 import android.widget.ImageView; 29 import android.widget.TextView; 30 31 import androidx.fragment.app.Fragment; 32 import androidx.fragment.app.FragmentManager; 33 import androidx.preference.Preference; 34 import androidx.preference.PreferenceScreen; 35 36 import com.android.settings.R; 37 import com.android.settings.core.BasePreferenceController; 38 import com.android.settings.overlay.FeatureFactory; 39 import com.android.settingslib.widget.LayoutPreference; 40 41 import java.util.ArrayList; 42 import java.util.Arrays; 43 import java.util.List; 44 import java.util.Map; 45 import java.util.Objects; 46 47 public class ModifierKeysPreferenceController extends BasePreferenceController { 48 49 private static final String KEY_TAG = "modifier_keys_dialog_tag"; 50 private static final String KEY_RESTORE_PREFERENCE = "modifier_keys_restore"; 51 52 private static final String KEY_PREFERENCE_CAPS_LOCK = "modifier_keys_caps_lock"; 53 private static final String KEY_PREFERENCE_CTRL = "modifier_keys_ctrl"; 54 private static final String KEY_PREFERENCE_META = "modifier_keys_meta"; 55 private static final String KEY_PREFERENCE_ALT = "modifier_keys_alt"; 56 57 private Fragment mParent; 58 private FragmentManager mFragmentManager; 59 private final InputManager mIm; 60 private PreferenceScreen mScreen; 61 private Drawable mDrawable; 62 63 private final List<Integer> mRemappableKeys = new ArrayList<>( 64 Arrays.asList( 65 KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_CTRL_RIGHT, 66 KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_META_RIGHT, 67 KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT, 68 KeyEvent.KEYCODE_CAPS_LOCK)); 69 70 private final List<Pair<String, Integer>> mKeys = new ArrayList<>( 71 Arrays.asList( 72 Pair.create(KEY_PREFERENCE_CTRL, R.string.modifier_keys_ctrl), 73 Pair.create(KEY_PREFERENCE_META, R.string.modifier_keys_meta), 74 Pair.create(KEY_PREFERENCE_ALT, R.string.modifier_keys_alt), 75 Pair.create(KEY_PREFERENCE_CAPS_LOCK, R.string.modifier_keys_caps_lock) 76 )); 77 78 private String[] mKeyNames = new String[] { 79 mContext.getString(R.string.modifier_keys_ctrl), 80 mContext.getString(R.string.modifier_keys_ctrl), 81 mContext.getString(R.string.modifier_keys_meta), 82 mContext.getString(R.string.modifier_keys_meta), 83 mContext.getString(R.string.modifier_keys_alt), 84 mContext.getString(R.string.modifier_keys_alt), 85 mContext.getString(R.string.modifier_keys_caps_lock)}; 86 ModifierKeysPreferenceController(Context context, String key)87 public ModifierKeysPreferenceController(Context context, String key) { 88 super(context, key); 89 mIm = context.getSystemService(InputManager.class); 90 Objects.requireNonNull(mIm, "InputManager service cannot be null"); 91 KeyboardSettingsFeatureProvider featureProvider = 92 FeatureFactory.getFeatureFactory().getKeyboardSettingsFeatureProvider(); 93 mDrawable = featureProvider.getActionKeyIcon(context); 94 } 95 setFragment(Fragment parent)96 public void setFragment(Fragment parent) { 97 mParent = parent; 98 } 99 100 @Override displayPreference(PreferenceScreen screen)101 public void displayPreference(PreferenceScreen screen) { 102 super.displayPreference(screen); 103 if (mParent == null) { 104 return; 105 } 106 mScreen = screen; 107 refreshUi(); 108 } 109 refreshUi()110 private void refreshUi() { 111 initDefaultKeysName(); 112 for (Map.Entry<Integer, Integer> entry : mIm.getModifierKeyRemapping().entrySet()) { 113 int fromKey = entry.getKey(); 114 int toKey = entry.getValue(); 115 int index = mRemappableKeys.indexOf(toKey); 116 117 if (isCtrl(fromKey) && mRemappableKeys.contains(toKey)) { 118 setSummaryColor(KEY_PREFERENCE_CTRL, index); 119 } 120 121 if (isMeta(fromKey) && mRemappableKeys.contains(toKey)) { 122 setSummaryColor(KEY_PREFERENCE_META, index); 123 } 124 125 if (isAlt(fromKey) && mRemappableKeys.contains(toKey)) { 126 setSummaryColor(KEY_PREFERENCE_ALT, index); 127 } 128 129 if (isCapLock(fromKey) && mRemappableKeys.contains(toKey)) { 130 setSummaryColor(KEY_PREFERENCE_CAPS_LOCK, index); 131 } 132 } 133 } 134 initDefaultKeysName()135 private void initDefaultKeysName() { 136 for (Pair<String, Integer> key : mKeys) { 137 LayoutPreference layoutPreference = mScreen.findPreference(key.first); 138 TextView title = layoutPreference.findViewById(R.id.title); 139 TextView summary = layoutPreference.findViewById(R.id.summary); 140 title.setText(key.second); 141 summary.setText(R.string.modifier_keys_default_summary); 142 143 if (key.first.equals(KEY_PREFERENCE_META) && mDrawable != null) { 144 setActionKeyIcon(layoutPreference, mDrawable); 145 } 146 } 147 } 148 setActionKeyIcon(LayoutPreference preference, Drawable drawable)149 private static void setActionKeyIcon(LayoutPreference preference, Drawable drawable) { 150 TextView leftBracket = preference.findViewById(R.id.modifier_key_left_bracket); 151 TextView rightBracket = preference.findViewById(R.id.modifier_key_right_bracket); 152 ImageView actionKeyIcon = preference.findViewById(R.id.modifier_key_action_key_icon); 153 leftBracket.setText("("); 154 rightBracket.setText(")"); 155 actionKeyIcon.setImageDrawable(drawable); 156 } 157 setSummaryColor(String key, int targetIndex)158 private void setSummaryColor(String key, int targetIndex) { 159 LayoutPreference layoutPreference = mScreen.findPreference(key); 160 TextView summary = layoutPreference.findViewById(R.id.summary); 161 summary.setText(changeSummaryColor(mKeyNames[targetIndex])); 162 } 163 164 @Override handlePreferenceTreeClick(Preference preference)165 public boolean handlePreferenceTreeClick(Preference preference) { 166 if (preference.getKey().equals(KEY_RESTORE_PREFERENCE)) { 167 return false; 168 } 169 showModifierKeysDialog(preference); 170 return true; 171 } 172 173 @Override getAvailabilityStatus()174 public int getAvailabilityStatus() { 175 return AVAILABLE; 176 } 177 showModifierKeysDialog(Preference preference)178 private void showModifierKeysDialog(Preference preference) { 179 mFragmentManager = mParent.getFragmentManager(); 180 ModifierKeysPickerDialogFragment fragment = new ModifierKeysPickerDialogFragment(); 181 fragment.setTargetFragment(mParent, 0); 182 Bundle bundle = new Bundle(); 183 TextView title = ((LayoutPreference) preference).findViewById(R.id.title); 184 TextView summary = ((LayoutPreference) preference).findViewById(R.id.summary); 185 bundle.putString( 186 ModifierKeysPickerDialogFragment.DEFAULT_KEY, 187 title.getText().toString()); 188 bundle.putString( 189 ModifierKeysPickerDialogFragment.SELECTION_KEY, 190 summary.getText().toString()); 191 fragment.setArguments(bundle); 192 fragment.show(mFragmentManager, KEY_TAG); 193 } 194 changeSummaryColor(String summary)195 private Spannable changeSummaryColor(String summary) { 196 Spannable spannableSummary = new SpannableString(summary); 197 spannableSummary.setSpan( 198 new ForegroundColorSpan(getColorOfMaterialColorPrimary()), 199 0, spannableSummary.length(), 0); 200 return spannableSummary; 201 } 202 getColorOfMaterialColorPrimary()203 private int getColorOfMaterialColorPrimary() { 204 return mContext.getColor(com.android.internal.R.color.materialColorPrimary); 205 } 206 isCtrl(int keyCode)207 private static boolean isCtrl(int keyCode) { 208 return keyCode == KeyEvent.KEYCODE_CTRL_LEFT || keyCode == KeyEvent.KEYCODE_CTRL_RIGHT; 209 } 210 isMeta(int keyCode)211 private static boolean isMeta(int keyCode) { 212 return keyCode == KeyEvent.KEYCODE_META_LEFT || keyCode == KeyEvent.KEYCODE_META_RIGHT; 213 } 214 isAlt(int keyCode)215 private static boolean isAlt(int keyCode) { 216 return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT; 217 } 218 isCapLock(int keyCode)219 private static boolean isCapLock(int keyCode) { 220 return keyCode == KeyEvent.KEYCODE_CAPS_LOCK; 221 } 222 } 223