1 /*
2  * Copyright (C) 2023 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 static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_USER;
20 import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEVICE;
21 import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.SuppressLint;
26 import android.annotation.UserIdInt;
27 import android.content.Context;
28 import android.hardware.input.InputDeviceIdentifier;
29 import android.hardware.input.InputManager;
30 import android.hardware.input.KeyboardLayout;
31 import android.hardware.input.KeyboardLayoutSelectionResult;
32 import android.hardware.input.KeyboardLayoutSelectionResult.LayoutSelectionCriteria;
33 import android.os.UserHandle;
34 import android.view.InputDevice;
35 import android.view.inputmethod.InputMethodInfo;
36 import android.view.inputmethod.InputMethodManager;
37 import android.view.inputmethod.InputMethodSubtype;
38 
39 import com.android.settings.R;
40 
41 import java.util.Arrays;
42 import java.util.Comparator;
43 
44 /**
45  * Utilities of keyboard settings
46  */
47 public class NewKeyboardSettingsUtils {
48 
49     static final String EXTRA_TITLE = "keyboard_layout_picker_title";
50     static final String EXTRA_USER_ID = "user_id";
51     static final String EXTRA_INPUT_DEVICE_IDENTIFIER = "input_device_identifier";
52     static final String EXTRA_INPUT_METHOD_INFO = "input_method_info";
53     static final String EXTRA_INPUT_METHOD_SUBTYPE = "input_method_subtype";
54 
isTouchpad()55     static boolean isTouchpad() {
56         for (int deviceId : InputDevice.getDeviceIds()) {
57             final InputDevice device = InputDevice.getDevice(deviceId);
58             if (device == null) {
59                 continue;
60             }
61             if ((device.getSources() & InputDevice.SOURCE_TOUCHPAD)
62                     == InputDevice.SOURCE_TOUCHPAD) {
63                 return true;
64             }
65         }
66         return false;
67     }
68 
69     @SuppressLint("MissingPermission")
70     @Nullable
getSelectedKeyboardLayoutLabelForUser(Context context, @UserIdInt int userId, InputDeviceIdentifier inputDeviceIdentifier)71     static String getSelectedKeyboardLayoutLabelForUser(Context context, @UserIdInt int userId,
72             InputDeviceIdentifier inputDeviceIdentifier) {
73         InputMethodManager imm = context.getSystemService(InputMethodManager.class);
74         InputManager im = context.getSystemService(InputManager.class);
75         if (imm == null || im == null) {
76             return null;
77         }
78         InputMethodInfo imeInfo = imm.getCurrentInputMethodInfoAsUser(UserHandle.of(userId));
79         InputMethodSubtype subtype = imm.getCurrentInputMethodSubtype();
80         KeyboardLayout[] keyboardLayouts = getKeyboardLayouts(im, userId, inputDeviceIdentifier,
81                 imeInfo, subtype);
82         KeyboardLayoutSelectionResult result = getKeyboardLayout(im, userId, inputDeviceIdentifier,
83                 imeInfo, subtype);
84         if (result != null) {
85             for (KeyboardLayout keyboardLayout : keyboardLayouts) {
86                 if (keyboardLayout.getDescriptor().equals(result.getLayoutDescriptor())) {
87                     return keyboardLayout.getLabel();
88                 }
89             }
90         }
91         return null;
92     }
93 
94     static class KeyboardInfo {
95         CharSequence mSubtypeLabel;
96         String mLayout;
97         @LayoutSelectionCriteria int mSelectionCriteria;
98         InputMethodInfo mInputMethodInfo;
99         InputMethodSubtype mInputMethodSubtype;
100 
KeyboardInfo( CharSequence subtypeLabel, String layout, @LayoutSelectionCriteria int selectionCriteria, InputMethodInfo inputMethodInfo, InputMethodSubtype inputMethodSubtype)101         KeyboardInfo(
102                 CharSequence subtypeLabel,
103                 String layout,
104                 @LayoutSelectionCriteria int selectionCriteria,
105                 InputMethodInfo inputMethodInfo,
106                 InputMethodSubtype inputMethodSubtype) {
107             mSubtypeLabel = subtypeLabel;
108             mLayout = layout;
109             mSelectionCriteria = selectionCriteria;
110             mInputMethodInfo = inputMethodInfo;
111             mInputMethodSubtype = inputMethodSubtype;
112         }
113 
getPrefId()114         String getPrefId() {
115             return mInputMethodInfo.getId() + "_" + mInputMethodSubtype.hashCode();
116         }
117 
getSubtypeLabel()118         CharSequence getSubtypeLabel() {
119             return mSubtypeLabel;
120         }
121 
getLayout()122         String getLayout() {
123             return mLayout;
124         }
125 
getLayoutSummaryText(Context context)126         String getLayoutSummaryText(Context context) {
127             if (isAutomaticSelection(mSelectionCriteria)) {
128                 return context.getResources().getString(R.string.automatic_keyboard_layout_label,
129                         mLayout);
130             } else if (isUserSelection(mSelectionCriteria)) {
131                 return context.getResources().getString(
132                         R.string.user_selected_keyboard_layout_label, mLayout);
133             }
134             return mLayout;
135         }
136 
getInputMethodInfo()137         InputMethodInfo getInputMethodInfo() {
138             return mInputMethodInfo;
139         }
140 
getInputMethodSubtype()141         InputMethodSubtype getInputMethodSubtype() {
142             return mInputMethodSubtype;
143         }
144     }
145 
getInputDevice(InputManager im, InputDeviceIdentifier identifier)146     static InputDevice getInputDevice(InputManager im, InputDeviceIdentifier identifier) {
147         return identifier == null ? null : im.getInputDeviceByDescriptor(
148                 identifier.getDescriptor());
149     }
150 
getKeyboardLayouts(InputManager inputManager, int userId, InputDeviceIdentifier identifier, InputMethodInfo info, InputMethodSubtype subtype)151     static KeyboardLayout[] getKeyboardLayouts(InputManager inputManager, int userId,
152             InputDeviceIdentifier identifier, InputMethodInfo info, InputMethodSubtype subtype) {
153         return inputManager.getKeyboardLayoutListForInputDevice(identifier, userId, info, subtype);
154     }
155 
156     @NonNull
getKeyboardLayout(InputManager inputManager, int userId, InputDeviceIdentifier identifier, InputMethodInfo info, InputMethodSubtype subtype)157     static KeyboardLayoutSelectionResult getKeyboardLayout(InputManager inputManager, int userId,
158             InputDeviceIdentifier identifier, InputMethodInfo info, InputMethodSubtype subtype) {
159         return inputManager.getKeyboardLayoutForInputDevice(identifier, userId, info, subtype);
160     }
161 
isAutomaticSelection(@ayoutSelectionCriteria int criteria)162     static boolean isAutomaticSelection(@LayoutSelectionCriteria int criteria) {
163         return criteria == LAYOUT_SELECTION_CRITERIA_DEVICE
164                 || criteria == LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
165     }
166 
isUserSelection(@ayoutSelectionCriteria int criteria)167     static boolean isUserSelection(@LayoutSelectionCriteria int criteria) {
168         return criteria == LAYOUT_SELECTION_CRITERIA_USER;
169     }
170 
sortKeyboardLayoutsByLabel(KeyboardLayout[] keyboardLayouts)171     static void sortKeyboardLayoutsByLabel(KeyboardLayout[] keyboardLayouts) {
172         Arrays.sort(
173                 keyboardLayouts,
174                 Comparator.comparing(KeyboardLayout::getLabel)
175         );
176     }
177 }
178