• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.car.settings.inputmethod;
18 
19 import android.app.admin.DevicePolicyManager;
20 import android.content.ContentResolver;
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 import android.content.pm.PackageManager.NameNotFoundException;
24 import android.graphics.Color;
25 import android.graphics.drawable.ColorDrawable;
26 import android.graphics.drawable.Drawable;
27 import android.provider.Settings;
28 import android.text.TextUtils;
29 import android.view.inputmethod.InputMethodInfo;
30 import android.view.inputmethod.InputMethodManager;
31 import android.view.inputmethod.InputMethodSubtype;
32 
33 import androidx.annotation.NonNull;
34 import androidx.annotation.VisibleForTesting;
35 
36 import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil;
37 
38 import java.util.List;
39 import java.util.stream.Collectors;
40 
41 /** Keyboard utility class. */
42 public final class InputMethodUtil {
43     /**
44      * Delimiter for Enabled Input Methods' concatenated string.
45      */
46     public static final char INPUT_METHOD_DELIMITER = ':';
47     /**
48      * A list of past and present Google Voice Typing package names
49      */
50     public static final List<String> GVT_PACKAGE_NAMES = List.of(
51             "com.google.android.tts",
52             "com.google.android.carassistant",
53             "com.google.android.googlequicksearchbox"
54     );
55     /**
56      * Splitter for Enabled Input Methods' concatenated string.
57      */
58     public static final TextUtils.SimpleStringSplitter sInputMethodSplitter =
59             new TextUtils.SimpleStringSplitter(INPUT_METHOD_DELIMITER);
60     @VisibleForTesting
61     static final Drawable NO_ICON = new ColorDrawable(Color.TRANSPARENT);
62 
InputMethodUtil()63     private InputMethodUtil() {
64     }
65 
66     /** Returns permitted list of enabled input methods. */
getPermittedAndEnabledInputMethodList( InputMethodManager imm, DevicePolicyManager dpm)67     public static List<InputMethodInfo> getPermittedAndEnabledInputMethodList(
68             InputMethodManager imm, DevicePolicyManager dpm) {
69         List<InputMethodInfo> inputMethodInfos = imm.getEnabledInputMethodList();
70         if (inputMethodInfos != null) {
71             // permittedList == null means all input methods are allowed.
72             List<String> permittedList = dpm.getPermittedInputMethodsForCurrentUser();
73 
74             inputMethodInfos = inputMethodInfos.stream().filter(info -> {
75                 boolean isAllowedByOrganization = permittedList == null
76                         || permittedList.contains(info.getPackageName());
77                 // Hide "Google voice typing" IME.
78                 boolean isGoogleVoiceTyping =
79                         InputMethodUtil.GVT_PACKAGE_NAMES.contains(info.getPackageName());
80                 return isAllowedByOrganization && !isGoogleVoiceTyping;
81             }).collect(Collectors.toList());
82         }
83         return inputMethodInfos;
84     }
85 
86     /** Returns package icon. */
getPackageIcon(@onNull PackageManager packageManager, @NonNull InputMethodInfo inputMethodInfo)87     public static Drawable getPackageIcon(@NonNull PackageManager packageManager,
88             @NonNull InputMethodInfo inputMethodInfo) {
89         Drawable icon;
90         try {
91             icon = packageManager.getApplicationIcon(inputMethodInfo.getPackageName());
92         } catch (NameNotFoundException e) {
93             icon = NO_ICON;
94         }
95 
96         return icon;
97     }
98 
99     /** Returns package label. */
getPackageLabel(@onNull PackageManager packageManager, @NonNull InputMethodInfo inputMethodInfo)100     public static String getPackageLabel(@NonNull PackageManager packageManager,
101             @NonNull InputMethodInfo inputMethodInfo) {
102         return inputMethodInfo.loadLabel(packageManager).toString();
103     }
104 
105     /** Returns input method summary. */
getSummaryString(@onNull Context context, @NonNull InputMethodManager inputMethodManager, @NonNull InputMethodInfo inputMethodInfo)106     public static String getSummaryString(@NonNull Context context,
107             @NonNull InputMethodManager inputMethodManager,
108             @NonNull InputMethodInfo inputMethodInfo) {
109         List<InputMethodSubtype> subtypes =
110                 inputMethodManager.getEnabledInputMethodSubtypeList(
111                         inputMethodInfo, /* allowsImplicitlySelectedSubtypes= */ true);
112         return InputMethodAndSubtypeUtil.getSubtypeLocaleNameListAsSentence(
113                 subtypes, context, inputMethodInfo);
114     }
115 
116     /**
117      * Check if input method is enabled.
118      *
119      * @return {@code true} if the input method is enabled.
120      */
isInputMethodEnabled(ContentResolver resolver, InputMethodInfo inputMethodInfo)121     public static boolean isInputMethodEnabled(ContentResolver resolver,
122             InputMethodInfo inputMethodInfo) {
123         String enabledImes = getEnabledInputMethodsConcatenatedIds(resolver);
124         if (TextUtils.isEmpty(enabledImes)) {
125             return false;
126         }
127         sInputMethodSplitter.setString(enabledImes);
128         while (sInputMethodSplitter.hasNext()) {
129             String inputMethodId = sInputMethodSplitter.next();
130             if (inputMethodId.equals(inputMethodInfo.getId())) {
131                 return true;
132             }
133         }
134         return false;
135     }
136 
137     /**
138      * Enable an input method using its InputMethodInfo.
139      */
enableInputMethod(ContentResolver resolver, InputMethodInfo inputMethodInfo)140     public static void enableInputMethod(ContentResolver resolver,
141             InputMethodInfo inputMethodInfo) {
142         if (isInputMethodEnabled(resolver, inputMethodInfo)) {
143             return;
144         }
145 
146         StringBuilder builder = new StringBuilder();
147         builder.append(getEnabledInputMethodsConcatenatedIds(resolver));
148 
149         if (!builder.toString().isEmpty()) {
150             builder.append(INPUT_METHOD_DELIMITER);
151         }
152 
153         builder.append(inputMethodInfo.getId());
154 
155         setEnabledInputMethodsConcatenatedIds(resolver, builder.toString());
156     }
157 
158     /**
159      * Disable an input method if its not the default system input method or if there exists another
160      * enabled input method that can also be set as the default system input method.
161      */
disableInputMethod(Context context, InputMethodManager inputMethodManager, InputMethodInfo inputMethodInfo)162     public static void disableInputMethod(Context context, InputMethodManager inputMethodManager,
163             InputMethodInfo inputMethodInfo) {
164         List<InputMethodInfo> enabledInputMethodInfos = inputMethodManager
165                 .getEnabledInputMethodList();
166         StringBuilder builder = new StringBuilder();
167 
168         boolean foundAnotherEnabledDefaultInputMethod = false;
169         boolean isSystemDefault = isDefaultInputMethod(context.getContentResolver(),
170                 inputMethodInfo);
171         for (InputMethodInfo enabledInputMethodInfo : enabledInputMethodInfos) {
172             if (enabledInputMethodInfo.getId().equals(inputMethodInfo.getId())) {
173                 continue;
174             }
175 
176             if (builder.length() > 0) {
177                 builder.append(INPUT_METHOD_DELIMITER);
178             }
179 
180             builder.append(enabledInputMethodInfo.getId());
181 
182             if (isSystemDefault && enabledInputMethodInfo.isDefault(context)) {
183                 foundAnotherEnabledDefaultInputMethod = true;
184                 setDefaultInputMethodId(context.getContentResolver(),
185                         enabledInputMethodInfo.getId());
186             }
187         }
188 
189         if (isSystemDefault && !foundAnotherEnabledDefaultInputMethod) {
190             return;
191         }
192 
193         setEnabledInputMethodsConcatenatedIds(context.getContentResolver(), builder.toString());
194     }
195 
getEnabledInputMethodsConcatenatedIds(ContentResolver resolver)196     private static String getEnabledInputMethodsConcatenatedIds(ContentResolver resolver) {
197         return Settings.Secure.getString(resolver, Settings.Secure.ENABLED_INPUT_METHODS);
198     }
199 
getDefaultInputMethodId(ContentResolver resolver)200     private static String getDefaultInputMethodId(ContentResolver resolver) {
201         return Settings.Secure.getString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD);
202     }
203 
isDefaultInputMethod(ContentResolver resolver, InputMethodInfo inputMethodInfo)204     private static boolean isDefaultInputMethod(ContentResolver resolver,
205             InputMethodInfo inputMethodInfo) {
206         return inputMethodInfo.getId().equals(getDefaultInputMethodId(resolver));
207     }
208 
setEnabledInputMethodsConcatenatedIds(ContentResolver resolver, String enabledInputMethodIds)209     private static void setEnabledInputMethodsConcatenatedIds(ContentResolver resolver,
210             String enabledInputMethodIds) {
211         Settings.Secure.putString(resolver, Settings.Secure.ENABLED_INPUT_METHODS,
212                 enabledInputMethodIds);
213     }
214 
setDefaultInputMethodId(ContentResolver resolver, String defaultInputMethodId)215     private static void setDefaultInputMethodId(ContentResolver resolver,
216             String defaultInputMethodId) {
217         Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD,
218                 defaultInputMethodId);
219     }
220 }
221