1 /*
2  * Copyright (C) 2018 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 
20 import android.content.Context;
21 import android.hardware.input.InputDeviceIdentifier;
22 import android.hardware.input.InputManager;
23 import android.hardware.input.KeyboardLayout;
24 
25 import androidx.fragment.app.Fragment;
26 import androidx.preference.Preference;
27 import androidx.preference.PreferenceScreen;
28 import androidx.preference.SwitchPreferenceCompat;
29 import androidx.preference.TwoStatePreference;
30 
31 import com.android.settings.core.BasePreferenceController;
32 import com.android.settingslib.core.lifecycle.LifecycleObserver;
33 import com.android.settingslib.core.lifecycle.events.OnStart;
34 import com.android.settingslib.core.lifecycle.events.OnStop;
35 
36 import java.util.Arrays;
37 import java.util.HashMap;
38 import java.util.Map;
39 
40 public class KeyboardLayoutPickerController extends BasePreferenceController implements
41         InputManager.InputDeviceListener, LifecycleObserver, OnStart, OnStop {
42 
43     private final InputManager mIm;
44     private final Map<TwoStatePreference, KeyboardLayout> mPreferenceMap;
45 
46     private Fragment mParent;
47     private int mInputDeviceId;
48     private InputDeviceIdentifier mInputDeviceIdentifier;
49     private KeyboardLayout[] mKeyboardLayouts;
50     private PreferenceScreen mScreen;
51 
52 
KeyboardLayoutPickerController(Context context, String key)53     public KeyboardLayoutPickerController(Context context, String key) {
54         super(context, key);
55         mIm = (InputManager) context.getSystemService(Context.INPUT_SERVICE);
56         mInputDeviceId = -1;
57         mPreferenceMap = new HashMap<>();
58     }
59 
initialize(Fragment parent, InputDeviceIdentifier inputDeviceIdentifier)60     public void initialize(Fragment parent, InputDeviceIdentifier inputDeviceIdentifier) {
61         mParent = parent;
62         mInputDeviceIdentifier = inputDeviceIdentifier;
63         mKeyboardLayouts = mIm.getKeyboardLayoutsForInputDevice(mInputDeviceIdentifier);
64         Arrays.sort(mKeyboardLayouts);
65     }
66 
67     @Override
onStart()68     public void onStart() {
69         mIm.registerInputDeviceListener(this, null);
70         if (mInputDeviceIdentifier == null
71                 || NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier) == null) {
72             return;
73         }
74         mInputDeviceId =
75                 NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier).getId();
76         updateCheckedState();
77     }
78 
79     @Override
onStop()80     public void onStop() {
81         mIm.unregisterInputDeviceListener(this);
82         mInputDeviceId = -1;
83     }
84 
85     @Override
displayPreference(PreferenceScreen screen)86     public void displayPreference(PreferenceScreen screen) {
87         super.displayPreference(screen);
88         mScreen = screen;
89         createPreferenceHierarchy();
90     }
91 
92     @Override
getAvailabilityStatus()93     public int getAvailabilityStatus() {
94         return AVAILABLE;
95     }
96 
97     @Override
handlePreferenceTreeClick(Preference preference)98     public boolean handlePreferenceTreeClick(Preference preference) {
99         if (!(preference instanceof TwoStatePreference switchPref)) {
100             return false;
101         }
102 
103         final KeyboardLayout layout = mPreferenceMap.get(switchPref);
104         if (layout != null) {
105             final boolean checked = switchPref.isChecked();
106             if (checked) {
107                 mIm.addKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
108                         layout.getDescriptor());
109             } else {
110                 mIm.removeKeyboardLayoutForInputDevice(mInputDeviceIdentifier,
111                         layout.getDescriptor());
112             }
113         }
114         return true;
115     }
116 
117     @Override
onInputDeviceAdded(int deviceId)118     public void onInputDeviceAdded(int deviceId) {
119 
120     }
121 
122     @Override
onInputDeviceRemoved(int deviceId)123     public void onInputDeviceRemoved(int deviceId) {
124         if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
125             mParent.getActivity().finish();
126         }
127     }
128 
129     @Override
onInputDeviceChanged(int deviceId)130     public void onInputDeviceChanged(int deviceId) {
131         if (mInputDeviceId >= 0 && deviceId == mInputDeviceId) {
132             updateCheckedState();
133         }
134     }
135 
updateCheckedState()136     private void updateCheckedState() {
137         final String[] enabledKeyboardLayouts = mIm.getEnabledKeyboardLayoutsForInputDevice(
138                 mInputDeviceIdentifier);
139         Arrays.sort(enabledKeyboardLayouts);
140 
141         for (Map.Entry<TwoStatePreference, KeyboardLayout> entry : mPreferenceMap.entrySet()) {
142             entry.getKey().setChecked(Arrays.binarySearch(enabledKeyboardLayouts,
143                     entry.getValue().getDescriptor()) >= 0);
144         }
145     }
146 
createPreferenceHierarchy()147     private void createPreferenceHierarchy() {
148         if (mKeyboardLayouts == null) {
149             return;
150         }
151         for (KeyboardLayout layout : mKeyboardLayouts) {
152             final TwoStatePreference pref = new SwitchPreferenceCompat(mScreen.getContext());
153             pref.setTitle(layout.getLabel());
154             pref.setSummary(layout.getCollection());
155             // TODO: Waiting for new API to use a prefix with special number to setKey
156             pref.setKey(layout.getDescriptor());
157             mScreen.addPreference(pref);
158             mPreferenceMap.put(pref, layout);
159         }
160     }
161 }
162 
163