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.Utils; 40 import com.android.settingslib.widget.LayoutPreference; 41 42 import java.util.ArrayList; 43 import java.util.Arrays; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Objects; 47 48 public class ModifierKeysPreferenceController extends BasePreferenceController { 49 50 private static final String KEY_TAG = "modifier_keys_dialog_tag"; 51 private static final String KEY_RESTORE_PREFERENCE = "modifier_keys_restore"; 52 53 private static final String KEY_PREFERENCE_CAPS_LOCK = "modifier_keys_caps_lock"; 54 private static final String KEY_PREFERENCE_CTRL = "modifier_keys_ctrl"; 55 private static final String KEY_PREFERENCE_META = "modifier_keys_meta"; 56 private static final String KEY_PREFERENCE_ALT = "modifier_keys_alt"; 57 58 private Fragment mParent; 59 private FragmentManager mFragmentManager; 60 private final InputManager mIm; 61 private PreferenceScreen mScreen; 62 private Drawable mDrawable; 63 64 private final List<Integer> mRemappableKeys = new ArrayList<>( 65 Arrays.asList( 66 KeyEvent.KEYCODE_CTRL_LEFT, KeyEvent.KEYCODE_CTRL_RIGHT, 67 KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_META_RIGHT, 68 KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_ALT_RIGHT, 69 KeyEvent.KEYCODE_CAPS_LOCK)); 70 71 private final List<Pair<String, Integer>> mKeys = new ArrayList<>( 72 Arrays.asList( 73 Pair.create(KEY_PREFERENCE_CTRL, R.string.modifier_keys_ctrl), 74 Pair.create(KEY_PREFERENCE_META, R.string.modifier_keys_meta), 75 Pair.create(KEY_PREFERENCE_ALT, R.string.modifier_keys_alt), 76 Pair.create(KEY_PREFERENCE_CAPS_LOCK, R.string.modifier_keys_caps_lock) 77 )); 78 79 private String[] mKeyNames = new String[] { 80 mContext.getString(R.string.modifier_keys_ctrl), 81 mContext.getString(R.string.modifier_keys_ctrl), 82 mContext.getString(R.string.modifier_keys_meta), 83 mContext.getString(R.string.modifier_keys_meta), 84 mContext.getString(R.string.modifier_keys_alt), 85 mContext.getString(R.string.modifier_keys_alt), 86 mContext.getString(R.string.modifier_keys_caps_lock)}; 87 ModifierKeysPreferenceController(Context context, String key)88 public ModifierKeysPreferenceController(Context context, String key) { 89 super(context, key); 90 mIm = context.getSystemService(InputManager.class); 91 Objects.requireNonNull(mIm, "InputManager service cannot be null"); 92 KeyboardSettingsFeatureProvider featureProvider = 93 FeatureFactory.getFeatureFactory().getKeyboardSettingsFeatureProvider(); 94 mDrawable = featureProvider.getActionKeyIcon(context); 95 } 96 setFragment(Fragment parent)97 public void setFragment(Fragment parent) { 98 mParent = parent; 99 } 100 101 @Override displayPreference(PreferenceScreen screen)102 public void displayPreference(PreferenceScreen screen) { 103 super.displayPreference(screen); 104 if (mParent == null) { 105 return; 106 } 107 mScreen = screen; 108 refreshUi(); 109 } 110 refreshUi()111 private void refreshUi() { 112 initDefaultKeysName(); 113 for (Map.Entry<Integer, Integer> entry : mIm.getModifierKeyRemapping().entrySet()) { 114 int fromKey = entry.getKey(); 115 int toKey = entry.getValue(); 116 int index = mRemappableKeys.indexOf(toKey); 117 118 if (isCtrl(fromKey) && mRemappableKeys.contains(toKey)) { 119 setSummaryColor(KEY_PREFERENCE_CTRL, index); 120 } 121 122 if (isMeta(fromKey) && mRemappableKeys.contains(toKey)) { 123 setSummaryColor(KEY_PREFERENCE_META, index); 124 } 125 126 if (isAlt(fromKey) && mRemappableKeys.contains(toKey)) { 127 setSummaryColor(KEY_PREFERENCE_ALT, index); 128 } 129 130 if (isCapLock(fromKey) && mRemappableKeys.contains(toKey)) { 131 setSummaryColor(KEY_PREFERENCE_CAPS_LOCK, index); 132 } 133 } 134 } 135 initDefaultKeysName()136 private void initDefaultKeysName() { 137 for (Pair<String, Integer> key : mKeys) { 138 LayoutPreference layoutPreference = mScreen.findPreference(key.first); 139 TextView title = layoutPreference.findViewById(R.id.title); 140 TextView summary = layoutPreference.findViewById(R.id.summary); 141 title.setText(key.second); 142 summary.setText(R.string.modifier_keys_default_summary); 143 144 if (key.first.equals(KEY_PREFERENCE_META) && mDrawable != null) { 145 setActionKeyIcon(layoutPreference, mDrawable); 146 } 147 } 148 } 149 setActionKeyIcon(LayoutPreference preference, Drawable drawable)150 private static void setActionKeyIcon(LayoutPreference preference, Drawable drawable) { 151 TextView leftBracket = preference.findViewById(R.id.modifier_key_left_bracket); 152 TextView rightBracket = preference.findViewById(R.id.modifier_key_right_bracket); 153 ImageView actionKeyIcon = preference.findViewById(R.id.modifier_key_action_key_icon); 154 leftBracket.setText("("); 155 rightBracket.setText(")"); 156 actionKeyIcon.setImageDrawable(drawable); 157 } 158 setSummaryColor(String key, int targetIndex)159 private void setSummaryColor(String key, int targetIndex) { 160 LayoutPreference layoutPreference = mScreen.findPreference(key); 161 TextView summary = layoutPreference.findViewById(R.id.summary); 162 summary.setText(changeSummaryColor(mKeyNames[targetIndex])); 163 } 164 165 @Override handlePreferenceTreeClick(Preference preference)166 public boolean handlePreferenceTreeClick(Preference preference) { 167 if (preference.getKey().equals(KEY_RESTORE_PREFERENCE)) { 168 return false; 169 } 170 showModifierKeysDialog(preference); 171 return true; 172 } 173 174 @Override getAvailabilityStatus()175 public int getAvailabilityStatus() { 176 return AVAILABLE; 177 } 178 showModifierKeysDialog(Preference preference)179 private void showModifierKeysDialog(Preference preference) { 180 mFragmentManager = mParent.getFragmentManager(); 181 ModifierKeysPickerDialogFragment fragment = new ModifierKeysPickerDialogFragment(); 182 fragment.setTargetFragment(mParent, 0); 183 Bundle bundle = new Bundle(); 184 TextView title = ((LayoutPreference) preference).findViewById(R.id.title); 185 TextView summary = ((LayoutPreference) preference).findViewById(R.id.summary); 186 bundle.putString( 187 ModifierKeysPickerDialogFragment.DEFAULT_KEY, 188 title.getText().toString()); 189 bundle.putString( 190 ModifierKeysPickerDialogFragment.SELECTION_KEY, 191 summary.getText().toString()); 192 fragment.setArguments(bundle); 193 fragment.show(mFragmentManager, KEY_TAG); 194 } 195 changeSummaryColor(String summary)196 private Spannable changeSummaryColor(String summary) { 197 Spannable spannableSummary = new SpannableString(summary); 198 spannableSummary.setSpan( 199 new ForegroundColorSpan(getColorOfMaterialColorPrimary()), 200 0, spannableSummary.length(), 0); 201 return spannableSummary; 202 } 203 getColorOfMaterialColorPrimary()204 private int getColorOfMaterialColorPrimary() { 205 return Utils.getColorAttrDefaultColor( 206 mContext, com.android.internal.R.attr.materialColorPrimary); 207 } 208 isCtrl(int keyCode)209 private static boolean isCtrl(int keyCode) { 210 return keyCode == KeyEvent.KEYCODE_CTRL_LEFT || keyCode == KeyEvent.KEYCODE_CTRL_RIGHT; 211 } 212 isMeta(int keyCode)213 private static boolean isMeta(int keyCode) { 214 return keyCode == KeyEvent.KEYCODE_META_LEFT || keyCode == KeyEvent.KEYCODE_META_RIGHT; 215 } 216 isAlt(int keyCode)217 private static boolean isAlt(int keyCode) { 218 return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT; 219 } 220 isCapLock(int keyCode)221 private static boolean isCapLock(int keyCode) { 222 return keyCode == KeyEvent.KEYCODE_CAPS_LOCK; 223 } 224 } 225