• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.settings.SettingsEnums;
20 import android.content.Context;
21 import android.hardware.input.InputDeviceIdentifier;
22 import android.hardware.input.InputManager;
23 import android.hardware.input.KeyboardLayout;
24 import android.hardware.input.KeyboardLayoutSelectionResult;
25 import android.os.Bundle;
26 import android.view.inputmethod.InputMethodInfo;
27 import android.view.inputmethod.InputMethodSubtype;
28 
29 import androidx.annotation.Nullable;
30 import androidx.fragment.app.Fragment;
31 import androidx.preference.Preference;
32 import androidx.preference.PreferenceScreen;
33 
34 import com.android.settings.R;
35 import com.android.settings.core.BasePreferenceController;
36 import com.android.settings.overlay.FeatureFactory;
37 import com.android.settings.widget.TickButtonPreference;
38 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
39 import com.android.settingslib.core.lifecycle.LifecycleObserver;
40 import com.android.settingslib.core.lifecycle.events.OnStart;
41 import com.android.settingslib.core.lifecycle.events.OnStop;
42 
43 import java.util.HashMap;
44 import java.util.Map;
45 
46 public class NewKeyboardLayoutPickerController extends BasePreferenceController implements
47         InputManager.InputDeviceListener, LifecycleObserver, OnStart, OnStop {
48 
49     private final InputManager mIm;
50     private final Map<TickButtonPreference, KeyboardLayout> mPreferenceMap;
51     private Fragment mParent;
52     private CharSequence mTitle;
53     private int mInputDeviceId;
54     private int mUserId;
55     private InputDeviceIdentifier mInputDeviceIdentifier;
56     private InputMethodInfo mInputMethodInfo;
57     private InputMethodSubtype mInputMethodSubtype;
58     private KeyboardLayout[] mKeyboardLayouts;
59     private PreferenceScreen mScreen;
60     private String mPreviousSelection;
61     private String mFinalSelectedLayoutDescriptor;
62     @Nullable private String mSelectedLayoutDescriptor;
63     private MetricsFeatureProvider mMetricsFeatureProvider;
64     private KeyboardLayoutSelectedCallback mKeyboardLayoutSelectedCallback;
65 
NewKeyboardLayoutPickerController(Context context, String key)66     public NewKeyboardLayoutPickerController(Context context, String key) {
67         super(context, key);
68         mIm = context.getSystemService(InputManager.class);
69         mInputDeviceId = -1;
70         mPreferenceMap = new HashMap<>();
71         mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
72     }
73 
initialize(Fragment parent)74     public void initialize(Fragment parent) {
75         mParent = parent;
76         Bundle arguments = parent.getArguments();
77         mTitle = arguments.getCharSequence(InputPeripheralsSettingsUtils.EXTRA_TITLE);
78         mUserId = arguments.getInt(InputPeripheralsSettingsUtils.EXTRA_USER_ID);
79         mInputDeviceIdentifier =
80                 arguments.getParcelable(
81                         InputPeripheralsSettingsUtils.EXTRA_INPUT_DEVICE_IDENTIFIER);
82         mInputMethodInfo =
83                 arguments.getParcelable(InputPeripheralsSettingsUtils.EXTRA_INPUT_METHOD_INFO);
84         mInputMethodSubtype =
85                 arguments.getParcelable(
86                         InputPeripheralsSettingsUtils.EXTRA_INPUT_METHOD_SUBTYPE);
87         mSelectedLayoutDescriptor = getSelectedLayoutDescriptor();
88         mFinalSelectedLayoutDescriptor = mSelectedLayoutDescriptor;
89         mKeyboardLayouts = mIm.getKeyboardLayoutListForInputDevice(
90                 mInputDeviceIdentifier, mUserId, mInputMethodInfo, mInputMethodSubtype);
91         InputPeripheralsSettingsUtils.sortKeyboardLayoutsByLabel(mKeyboardLayouts);
92         parent.getActivity().setTitle(mTitle);
93     }
94 
95     @Override
onStart()96     public void onStart() {
97         mIm.registerInputDeviceListener(this, null);
98         if (mInputDeviceIdentifier == null
99                 || InputPeripheralsSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier)
100                 == null) {
101             return;
102         }
103         mInputDeviceId =
104                 InputPeripheralsSettingsUtils.getInputDevice(mIm,
105                         mInputDeviceIdentifier).getId();
106     }
107 
108     @Override
onStop()109     public void onStop() {
110         if (mSelectedLayoutDescriptor != null
111                 && !mSelectedLayoutDescriptor.equals(mFinalSelectedLayoutDescriptor)) {
112             String change = "From:"
113                     + getLayoutLabel(mSelectedLayoutDescriptor)
114                     + ", to:"
115                     + getLayoutLabel(mFinalSelectedLayoutDescriptor);
116             mMetricsFeatureProvider.action(
117                     mContext, SettingsEnums.ACTION_PK_LAYOUT_CHANGED, change);
118         }
119         mIm.unregisterInputDeviceListener(this);
120         mInputDeviceId = -1;
121     }
122 
123     @Override
displayPreference(PreferenceScreen screen)124     public void displayPreference(PreferenceScreen screen) {
125         super.displayPreference(screen);
126         mScreen = screen;
127         createPreferenceHierarchy();
128     }
129 
130     @Override
getAvailabilityStatus()131     public int getAvailabilityStatus() {
132         return AVAILABLE;
133     }
134 
135     /**
136      * Registers {@link KeyboardLayoutSelectedCallback} and get updated.
137      */
registerKeyboardSelectedCallback(KeyboardLayoutSelectedCallback keyboardLayoutSelectedCallback)138     public void registerKeyboardSelectedCallback(KeyboardLayoutSelectedCallback
139             keyboardLayoutSelectedCallback) {
140         this.mKeyboardLayoutSelectedCallback = keyboardLayoutSelectedCallback;
141     }
142 
143     @Override
handlePreferenceTreeClick(Preference preference)144     public boolean handlePreferenceTreeClick(Preference preference) {
145         if (!(preference instanceof TickButtonPreference)) {
146             return false;
147         }
148 
149         final TickButtonPreference pref = (TickButtonPreference) preference;
150         if (mKeyboardLayoutSelectedCallback != null && mPreferenceMap.containsKey(preference)) {
151             mKeyboardLayoutSelectedCallback.onSelected(mPreferenceMap.get(preference));
152         }
153         pref.setSelected(true);
154         if (mPreviousSelection != null && !mPreviousSelection.equals(preference.getKey())) {
155             TickButtonPreference preSelectedPref = mScreen.findPreference(mPreviousSelection);
156             preSelectedPref.setSelected(false);
157         }
158         setLayout(pref);
159         mPreviousSelection = preference.getKey();
160         mFinalSelectedLayoutDescriptor = mPreviousSelection;
161         return true;
162     }
163 
164     @Override
onInputDeviceAdded(int deviceId)165     public void onInputDeviceAdded(int deviceId) {
166         // Do nothing.
167     }
168 
169     @Override
onInputDeviceRemoved(int deviceId)170     public void onInputDeviceRemoved(int deviceId) {
171         if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
172             mParent.getActivity().finish();
173         }
174     }
175 
176     @Override
onInputDeviceChanged(int deviceId)177     public void onInputDeviceChanged(int deviceId) {
178         // Do nothing.
179     }
180 
createPreferenceHierarchy()181     private void createPreferenceHierarchy() {
182         if (mKeyboardLayouts == null) {
183             return;
184         }
185         for (KeyboardLayout layout : mKeyboardLayouts) {
186             final TickButtonPreference pref;
187             pref = new TickButtonPreference(mScreen.getContext());
188             pref.setTitle(layout.getLabel());
189 
190             if (mSelectedLayoutDescriptor != null && mSelectedLayoutDescriptor.equals(
191                     layout.getDescriptor())) {
192                 if (mKeyboardLayoutSelectedCallback != null) {
193                     mKeyboardLayoutSelectedCallback.onSelected(layout);
194                 }
195                 pref.setSelected(true);
196                 mPreviousSelection = mSelectedLayoutDescriptor;
197             }
198             pref.setKey(layout.getDescriptor());
199             mScreen.addPreference(pref);
200             mPreferenceMap.put(pref, layout);
201         }
202 
203         if (mSelectedLayoutDescriptor == null && mKeyboardLayoutSelectedCallback != null) {
204             // Pass null here since getKeyboardLayoutPreview() accept null layout, which will
205             // return default preview image
206             mKeyboardLayoutSelectedCallback.onSelected(null);
207         }
208     }
209 
setLayout(TickButtonPreference preference)210     private void setLayout(TickButtonPreference preference) {
211         mIm.setKeyboardLayoutForInputDevice(
212                 mInputDeviceIdentifier,
213                 mUserId,
214                 mInputMethodInfo,
215                 mInputMethodSubtype,
216                 mPreferenceMap.get(preference).getDescriptor());
217     }
218 
getSelectedLayoutDescriptor()219     private String getSelectedLayoutDescriptor() {
220         KeyboardLayoutSelectionResult result = InputPeripheralsSettingsUtils.getKeyboardLayout(
221                 mIm, mUserId, mInputDeviceIdentifier, mInputMethodInfo, mInputMethodSubtype);
222         return result.getLayoutDescriptor();
223     }
224 
getLayoutLabel(String descriptor)225     private String getLayoutLabel(String descriptor) {
226         String label = mContext.getString(R.string.keyboard_default_layout);
227         KeyboardLayout[] keyboardLayouts = InputPeripheralsSettingsUtils.getKeyboardLayouts(
228                 mIm, mUserId, mInputDeviceIdentifier, mInputMethodInfo, mInputMethodSubtype);
229         if (descriptor != null) {
230             for (KeyboardLayout keyboardLayout : keyboardLayouts) {
231                 if (keyboardLayout.getDescriptor().equals(descriptor)) {
232                     label = keyboardLayout.getLabel();
233                     break;
234                 }
235             }
236         }
237         return label;
238     }
239 
240     public interface KeyboardLayoutSelectedCallback {
241         /**
242          * Called when KeyboardLayout been selected.
243          */
onSelected(@ullable KeyboardLayout keyboardLayout)244         void onSelected(@Nullable KeyboardLayout keyboardLayout);
245     }
246 }
247