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