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.content.ContentResolver; 20 import android.content.Context; 21 import android.content.pm.PackageManager; 22 import android.content.pm.PackageManager.NameNotFoundException; 23 import android.graphics.Color; 24 import android.graphics.drawable.ColorDrawable; 25 import android.graphics.drawable.Drawable; 26 import android.provider.Settings; 27 import android.text.TextUtils; 28 import android.view.inputmethod.InputMethodInfo; 29 import android.view.inputmethod.InputMethodManager; 30 import android.view.inputmethod.InputMethodSubtype; 31 32 import androidx.annotation.NonNull; 33 import androidx.annotation.VisibleForTesting; 34 35 import com.android.settingslib.inputmethod.InputMethodAndSubtypeUtil; 36 37 import java.util.List; 38 39 /** Keyboard utility class. */ 40 public final class InputMethodUtil { 41 /** 42 * Delimiter for Enabled Input Methods' concatenated string. 43 */ 44 public static final char INPUT_METHOD_DELIMITER = ':'; 45 /** 46 * Splitter for Enabled Input Methods' concatenated string. 47 */ 48 public static final TextUtils.SimpleStringSplitter sInputMethodSplitter = 49 new TextUtils.SimpleStringSplitter(INPUT_METHOD_DELIMITER); 50 @VisibleForTesting 51 static final Drawable NO_ICON = new ColorDrawable(Color.TRANSPARENT); 52 InputMethodUtil()53 private InputMethodUtil() { 54 } 55 56 /** Returns package icon. */ getPackageIcon(@onNull PackageManager packageManager, @NonNull InputMethodInfo inputMethodInfo)57 public static Drawable getPackageIcon(@NonNull PackageManager packageManager, 58 @NonNull InputMethodInfo inputMethodInfo) { 59 Drawable icon; 60 try { 61 icon = packageManager.getApplicationIcon(inputMethodInfo.getPackageName()); 62 } catch (NameNotFoundException e) { 63 icon = NO_ICON; 64 } 65 66 return icon; 67 } 68 69 /** Returns package label. */ getPackageLabel(@onNull PackageManager packageManager, @NonNull InputMethodInfo inputMethodInfo)70 public static String getPackageLabel(@NonNull PackageManager packageManager, 71 @NonNull InputMethodInfo inputMethodInfo) { 72 return inputMethodInfo.loadLabel(packageManager).toString(); 73 } 74 75 /** Returns input method summary. */ getSummaryString(@onNull Context context, @NonNull InputMethodManager inputMethodManager, @NonNull InputMethodInfo inputMethodInfo)76 public static String getSummaryString(@NonNull Context context, 77 @NonNull InputMethodManager inputMethodManager, 78 @NonNull InputMethodInfo inputMethodInfo) { 79 List<InputMethodSubtype> subtypes = 80 inputMethodManager.getEnabledInputMethodSubtypeList( 81 inputMethodInfo, /* allowsImplicitlySelectedSubtypes= */ true); 82 return InputMethodAndSubtypeUtil.getSubtypeLocaleNameListAsSentence( 83 subtypes, context, inputMethodInfo); 84 } 85 86 /** 87 * Check if input method is enabled. 88 * 89 * @return {@code true} if the input method is enabled. 90 */ isInputMethodEnabled(ContentResolver resolver, InputMethodInfo inputMethodInfo)91 public static boolean isInputMethodEnabled(ContentResolver resolver, 92 InputMethodInfo inputMethodInfo) { 93 sInputMethodSplitter.setString(getEnabledInputMethodsConcatenatedIds(resolver)); 94 while (sInputMethodSplitter.hasNext()) { 95 String inputMethodId = sInputMethodSplitter.next(); 96 if (inputMethodId.equals(inputMethodInfo.getId())) { 97 return true; 98 } 99 } 100 return false; 101 } 102 103 /** 104 * Enable an input method using its InputMethodInfo. 105 */ enableInputMethod(ContentResolver resolver, InputMethodInfo inputMethodInfo)106 public static void enableInputMethod(ContentResolver resolver, 107 InputMethodInfo inputMethodInfo) { 108 if (isInputMethodEnabled(resolver, inputMethodInfo)) { 109 return; 110 } 111 112 StringBuilder builder = new StringBuilder(); 113 builder.append(getEnabledInputMethodsConcatenatedIds(resolver)); 114 115 if (!builder.toString().isEmpty()) { 116 builder.append(INPUT_METHOD_DELIMITER); 117 } 118 119 builder.append(inputMethodInfo.getId()); 120 121 setEnabledInputMethodsConcatenatedIds(resolver, builder.toString()); 122 } 123 124 /** 125 * Disable an input method if its not the default system input method or if there exists another 126 * enabled input method that can also be set as the default system input method. 127 */ disableInputMethod(Context context, InputMethodManager inputMethodManager, InputMethodInfo inputMethodInfo)128 public static void disableInputMethod(Context context, InputMethodManager inputMethodManager, 129 InputMethodInfo inputMethodInfo) { 130 List<InputMethodInfo> enabledInputMethodInfos = inputMethodManager 131 .getEnabledInputMethodList(); 132 StringBuilder builder = new StringBuilder(); 133 134 boolean foundAnotherEnabledDefaultInputMethod = false; 135 boolean isSystemDefault = isDefaultInputMethod(context.getContentResolver(), 136 inputMethodInfo); 137 for (InputMethodInfo enabledInputMethodInfo : enabledInputMethodInfos) { 138 if (enabledInputMethodInfo.getId().equals(inputMethodInfo.getId())) { 139 continue; 140 } 141 142 if (builder.length() > 0) { 143 builder.append(INPUT_METHOD_DELIMITER); 144 } 145 146 builder.append(enabledInputMethodInfo.getId()); 147 148 if (isSystemDefault && enabledInputMethodInfo.isDefault(context)) { 149 foundAnotherEnabledDefaultInputMethod = true; 150 setDefaultInputMethodId(context.getContentResolver(), 151 enabledInputMethodInfo.getId()); 152 } 153 } 154 155 if (isSystemDefault && !foundAnotherEnabledDefaultInputMethod) { 156 return; 157 } 158 159 setEnabledInputMethodsConcatenatedIds(context.getContentResolver(), builder.toString()); 160 } 161 getEnabledInputMethodsConcatenatedIds(ContentResolver resolver)162 private static String getEnabledInputMethodsConcatenatedIds(ContentResolver resolver) { 163 return Settings.Secure.getString(resolver, Settings.Secure.ENABLED_INPUT_METHODS); 164 } 165 getDefaultInputMethodId(ContentResolver resolver)166 private static String getDefaultInputMethodId(ContentResolver resolver) { 167 return Settings.Secure.getString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD); 168 } 169 isDefaultInputMethod(ContentResolver resolver, InputMethodInfo inputMethodInfo)170 private static boolean isDefaultInputMethod(ContentResolver resolver, 171 InputMethodInfo inputMethodInfo) { 172 return inputMethodInfo.getId().equals(getDefaultInputMethodId(resolver)); 173 } 174 setEnabledInputMethodsConcatenatedIds(ContentResolver resolver, String enabledInputMethodIds)175 private static void setEnabledInputMethodsConcatenatedIds(ContentResolver resolver, 176 String enabledInputMethodIds) { 177 Settings.Secure.putString(resolver, Settings.Secure.ENABLED_INPUT_METHODS, 178 enabledInputMethodIds); 179 } 180 setDefaultInputMethodId(ContentResolver resolver, String defaultInputMethodId)181 private static void setDefaultInputMethodId(ContentResolver resolver, 182 String defaultInputMethodId) { 183 Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD, 184 defaultInputMethodId); 185 } 186 } 187