• 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.ArrayList;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.stream.Collectors;
42 
43 /** Keyboard utility class. */
44 public final class InputMethodUtil {
45     /**
46      * Delimiter for Enabled Input Methods' concatenated string.
47      */
48     public static final char INPUT_METHOD_DELIMITER = ':';
49     /**
50      * A list of past and present Google Voice Typing package names
51      */
52     public static final List<String> GVT_PACKAGE_NAMES =
53             Collections.unmodifiableList(
54                     new ArrayList<String>(){{
55                         add("com.google.android.tts");
56                         add("com.google.android.carassistant");
57                         add("com.google.android.googlequicksearchbox");
58                     }});
59     /**
60      * Splitter for Enabled Input Methods' concatenated string.
61      */
62     public static final TextUtils.SimpleStringSplitter sInputMethodSplitter =
63             new TextUtils.SimpleStringSplitter(INPUT_METHOD_DELIMITER);
64     @VisibleForTesting
65     static final Drawable NO_ICON = new ColorDrawable(Color.TRANSPARENT);
66 
InputMethodUtil()67     private InputMethodUtil() {
68     }
69 
70     /** Returns permitted list of enabled input methods. */
getPermittedAndEnabledInputMethodList( InputMethodManager imm, DevicePolicyManager dpm)71     public static List<InputMethodInfo> getPermittedAndEnabledInputMethodList(
72             InputMethodManager imm, DevicePolicyManager dpm) {
73         List<InputMethodInfo> inputMethodInfos = imm.getEnabledInputMethodList();
74         if (inputMethodInfos != null) {
75             // permittedList == null means all input methods are allowed.
76             List<String> permittedList = dpm.getPermittedInputMethodsForCurrentUser();
77 
78             inputMethodInfos = inputMethodInfos.stream().filter(info -> {
79                 boolean isAllowedByOrganization = permittedList == null
80                         || permittedList.contains(info.getPackageName());
81                 // Hide "Google voice typing" IME.
82                 boolean isGoogleVoiceTyping =
83                         InputMethodUtil.GVT_PACKAGE_NAMES.contains(info.getPackageName());
84                 return isAllowedByOrganization && !isGoogleVoiceTyping;
85             }).collect(Collectors.toList());
86         }
87         return inputMethodInfos;
88     }
89 
90     /** Returns package icon. */
getPackageIcon(@onNull PackageManager packageManager, @NonNull InputMethodInfo inputMethodInfo)91     public static Drawable getPackageIcon(@NonNull PackageManager packageManager,
92             @NonNull InputMethodInfo inputMethodInfo) {
93         Drawable icon;
94         try {
95             icon = packageManager.getApplicationIcon(inputMethodInfo.getPackageName());
96         } catch (NameNotFoundException e) {
97             icon = NO_ICON;
98         }
99 
100         return icon;
101     }
102 
103     /** Returns package label. */
getPackageLabel(@onNull PackageManager packageManager, @NonNull InputMethodInfo inputMethodInfo)104     public static String getPackageLabel(@NonNull PackageManager packageManager,
105             @NonNull InputMethodInfo inputMethodInfo) {
106         return inputMethodInfo.loadLabel(packageManager).toString();
107     }
108 
109     /** Returns input method summary. */
getSummaryString(@onNull Context context, @NonNull InputMethodManager inputMethodManager, @NonNull InputMethodInfo inputMethodInfo)110     public static String getSummaryString(@NonNull Context context,
111             @NonNull InputMethodManager inputMethodManager,
112             @NonNull InputMethodInfo inputMethodInfo) {
113         List<InputMethodSubtype> subtypes =
114                 inputMethodManager.getEnabledInputMethodSubtypeList(
115                         inputMethodInfo, /* allowsImplicitlySelectedSubtypes= */ true);
116         return InputMethodAndSubtypeUtil.getSubtypeLocaleNameListAsSentence(
117                 subtypes, context, inputMethodInfo);
118     }
119 
120     /**
121      * Check if input method is enabled.
122      *
123      * @return {@code true} if the input method is enabled.
124      */
isInputMethodEnabled(ContentResolver resolver, InputMethodInfo inputMethodInfo)125     public static boolean isInputMethodEnabled(ContentResolver resolver,
126             InputMethodInfo inputMethodInfo) {
127         sInputMethodSplitter.setString(getEnabledInputMethodsConcatenatedIds(resolver));
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