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 static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.Matchers.anyBoolean; 22 import static org.mockito.Matchers.eq; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.when; 25 26 import android.content.Context; 27 import android.content.pm.PackageManager; 28 import android.content.pm.PackageManager.NameNotFoundException; 29 import android.content.pm.ServiceInfo; 30 import android.graphics.drawable.Drawable; 31 import android.provider.Settings; 32 import android.view.inputmethod.InputMethodInfo; 33 import android.view.inputmethod.InputMethodManager; 34 import android.view.inputmethod.InputMethodSubtype; 35 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; 36 37 import com.android.car.settings.CarSettingsRobolectricTestRunner; 38 39 import org.junit.Before; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 import org.mockito.Mock; 43 import org.mockito.MockitoAnnotations; 44 import org.robolectric.RuntimeEnvironment; 45 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.HashSet; 49 import java.util.List; 50 import java.util.Set; 51 import java.util.stream.Collectors; 52 53 @RunWith(CarSettingsRobolectricTestRunner.class) 54 public class InputMethodUtilTest { 55 private static final String DUMMY_PACKAGE_NAME = "dummy package name"; 56 private static final String DUMMY_LABEL = "dummy label"; 57 private static final String DUMMY_SETTINGS_ACTIVITY = "dummy settings activity"; 58 private static final String SUBTYPES_STRING = 59 "English (United States), German (Belgium), and Occitan (France)"; 60 private static final String DUMMY_ENABLED_INPUT_METHODS = 61 "com.google.android.googlequicksearchbox/com.google.android.voicesearch.ime" 62 + ".VoiceInputMethodService:com.google.android.apps.automotive.inputmethod/" 63 + ".InputMethodService"; 64 private static final String DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT = 65 "com.google.android.apps.automotive.inputmethod/.InputMethodService"; 66 private static final String DUMMY_ENABLED_INPUT_METHOD_ID = 67 "com.google.android.googlequicksearchbox/com.google.android.voicesearch.ime" 68 + ".VoiceInputMethodService"; 69 private static final String DUMMY_DISABLED_INPUT_METHOD_ID = "disabled input method id"; 70 private Context mContext; 71 private List<InputMethodInfo> mDummyEnabledInputMethodsListAllDefaultable; 72 private List<InputMethodInfo> mDummyEnabledInputMethodsListOneDefaultable; 73 74 @Mock 75 private PackageManager mPackageManager; 76 @Mock 77 private InputMethodManager mInputMethodManager; 78 @Mock 79 private Drawable mIcon; 80 81 @Before setUp()82 public void setUp() { 83 MockitoAnnotations.initMocks(this); 84 85 mContext = RuntimeEnvironment.application; 86 87 Settings.Secure.putString(mContext.getContentResolver(), 88 Settings.Secure.ENABLED_INPUT_METHODS, DUMMY_ENABLED_INPUT_METHODS); 89 Settings.Secure.putString(mContext.getContentResolver(), 90 Settings.Secure.DEFAULT_INPUT_METHOD, DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT); 91 92 mDummyEnabledInputMethodsListOneDefaultable = Arrays 93 .stream(DUMMY_ENABLED_INPUT_METHODS.split(String.valueOf(InputMethodUtil 94 .INPUT_METHOD_DELIMITER))).collect(Collectors.toList()).stream().map( 95 result -> { 96 InputMethodInfo info = createMockInputMethodInfo( 97 mPackageManager, DUMMY_PACKAGE_NAME); 98 when(info.getId()).thenReturn(result); 99 when(info.isDefault(mContext)).thenReturn(result.equals( 100 DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT)); 101 return info; 102 }).collect(Collectors.toList()); 103 mDummyEnabledInputMethodsListAllDefaultable = Arrays 104 .stream(DUMMY_ENABLED_INPUT_METHODS.split(String.valueOf(InputMethodUtil 105 .INPUT_METHOD_DELIMITER))).collect(Collectors.toList()).stream().map( 106 result -> { 107 InputMethodInfo info = createMockInputMethodInfo( 108 mPackageManager, DUMMY_PACKAGE_NAME); 109 when(info.getId()).thenReturn(result); 110 when(info.isDefault(mContext)).thenReturn(true); 111 return info; 112 }).collect(Collectors.toList()); 113 } 114 115 @Test getPackageIcon_hasApplicationIcon()116 public void getPackageIcon_hasApplicationIcon() throws NameNotFoundException { 117 InputMethodInfo info = createMockInputMethodInfoWithSubtypes(mPackageManager, 118 mInputMethodManager, DUMMY_PACKAGE_NAME); 119 when(mPackageManager.getApplicationIcon(eq(info.getPackageName()))).thenReturn(mIcon); 120 assertThat(InputMethodUtil.getPackageIcon(mPackageManager, info)).isEqualTo(mIcon); 121 } 122 123 @Test getPackageIcon_noApplicationIcon()124 public void getPackageIcon_noApplicationIcon() throws NameNotFoundException { 125 InputMethodInfo info = createMockInputMethodInfoWithSubtypes(mPackageManager, 126 mInputMethodManager, DUMMY_PACKAGE_NAME); 127 when(mPackageManager.getApplicationIcon(DUMMY_PACKAGE_NAME)).thenThrow( 128 new NameNotFoundException()); 129 assertThat(InputMethodUtil.getPackageIcon(mPackageManager, info)).isEqualTo( 130 InputMethodUtil.NO_ICON); 131 } 132 133 @Test getPackageLabel()134 public void getPackageLabel() { 135 InputMethodInfo info = createMockInputMethodInfoWithSubtypes(mPackageManager, 136 mInputMethodManager, DUMMY_PACKAGE_NAME); 137 assertThat(InputMethodUtil.getPackageLabel(mPackageManager, info)).isEqualTo( 138 DUMMY_LABEL); 139 } 140 141 @Test getSummaryString()142 public void getSummaryString() { 143 InputMethodInfo info = createMockInputMethodInfoWithSubtypes(mPackageManager, 144 mInputMethodManager, DUMMY_PACKAGE_NAME); 145 assertThat(InputMethodUtil.getSummaryString(mContext, mInputMethodManager, info)).isEqualTo( 146 SUBTYPES_STRING); 147 } 148 149 @Test isInputMethodEnabled_isDisabled_returnsFalse()150 public void isInputMethodEnabled_isDisabled_returnsFalse() { 151 InputMethodInfo info = createMockInputMethodInfo(mPackageManager, DUMMY_PACKAGE_NAME); 152 when(info.getId()).thenReturn(DUMMY_DISABLED_INPUT_METHOD_ID); 153 154 assertThat(InputMethodUtil.isInputMethodEnabled(mContext.getContentResolver(), info)) 155 .isFalse(); 156 } 157 158 @Test isInputMethodEnabled_isEnabled_returnsTrue()159 public void isInputMethodEnabled_isEnabled_returnsTrue() { 160 InputMethodInfo info = createMockInputMethodInfo(mPackageManager, DUMMY_PACKAGE_NAME); 161 when(info.getId()).thenReturn(DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT); 162 163 assertThat(InputMethodUtil.isInputMethodEnabled(mContext.getContentResolver(), info)) 164 .isTrue(); 165 } 166 167 @Test enableInputMethod_alreadyEnabled_remainsUnchanged()168 public void enableInputMethod_alreadyEnabled_remainsUnchanged() { 169 InputMethodInfo info = createMockInputMethodInfo(mPackageManager, DUMMY_PACKAGE_NAME); 170 when(info.getId()).thenReturn(DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT); 171 172 InputMethodUtil.enableInputMethod(mContext.getContentResolver(), info); 173 174 assertThat(Settings.Secure.getString(mContext.getContentResolver(), 175 Settings.Secure.ENABLED_INPUT_METHODS)).isEqualTo(DUMMY_ENABLED_INPUT_METHODS); 176 } 177 178 @Test enableInputMethod_noEnabledInputMethods_addsIME()179 public void enableInputMethod_noEnabledInputMethods_addsIME() { 180 Settings.Secure.putString(mContext.getContentResolver(), 181 Settings.Secure.ENABLED_INPUT_METHODS, ""); 182 InputMethodInfo info = createMockInputMethodInfo(mPackageManager, DUMMY_PACKAGE_NAME); 183 when(info.getId()).thenReturn(DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT); 184 185 InputMethodUtil.enableInputMethod(mContext.getContentResolver(), info); 186 187 assertThat(Settings.Secure.getString(mContext.getContentResolver(), 188 Settings.Secure.ENABLED_INPUT_METHODS)).isEqualTo( 189 DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT); 190 } 191 192 @Test enableInputMethod_someEnabledInputMethods_addsIME()193 public void enableInputMethod_someEnabledInputMethods_addsIME() { 194 InputMethodInfo info = createMockInputMethodInfo(mPackageManager, DUMMY_PACKAGE_NAME); 195 when(info.getId()).thenReturn(DUMMY_DISABLED_INPUT_METHOD_ID); 196 197 InputMethodUtil.enableInputMethod(mContext.getContentResolver(), info); 198 199 assertThat(Settings.Secure.getString(mContext.getContentResolver(), 200 Settings.Secure.ENABLED_INPUT_METHODS)).isEqualTo( 201 DUMMY_ENABLED_INPUT_METHODS + ":" 202 + DUMMY_DISABLED_INPUT_METHOD_ID); 203 } 204 205 @Test disableInputMethod_notEnabled_remainsUnchanged()206 public void disableInputMethod_notEnabled_remainsUnchanged() { 207 InputMethodInfo info = createMockInputMethodInfo(mPackageManager, DUMMY_PACKAGE_NAME); 208 when(info.getId()).thenReturn(DUMMY_DISABLED_INPUT_METHOD_ID); 209 when(mInputMethodManager.getEnabledInputMethodList()) 210 .thenReturn(mDummyEnabledInputMethodsListAllDefaultable); 211 212 InputMethodUtil.disableInputMethod(mContext, mInputMethodManager, info); 213 214 assertThat(Settings.Secure.getString(mContext.getContentResolver(), 215 Settings.Secure.ENABLED_INPUT_METHODS)).isEqualTo(DUMMY_ENABLED_INPUT_METHODS); 216 assertThat(Settings.Secure.getString(mContext.getContentResolver(), 217 Settings.Secure.DEFAULT_INPUT_METHOD)).isEqualTo( 218 DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT); 219 } 220 221 @Test disableInputMethod_notDefault_removesIMEWhileDefaultRemainsSame()222 public void disableInputMethod_notDefault_removesIMEWhileDefaultRemainsSame() { 223 InputMethodInfo info = createMockInputMethodInfo(mPackageManager, DUMMY_PACKAGE_NAME); 224 when(info.getId()).thenReturn(DUMMY_ENABLED_INPUT_METHOD_ID); 225 when(mInputMethodManager.getEnabledInputMethodList()) 226 .thenReturn(mDummyEnabledInputMethodsListAllDefaultable); 227 228 InputMethodUtil.disableInputMethod(mContext, mInputMethodManager, info); 229 230 assertThat(splitConcatenatedIdsIntoSet(Settings.Secure.getString(mContext 231 .getContentResolver(), Settings.Secure.ENABLED_INPUT_METHODS))).isEqualTo( 232 splitConcatenatedIdsIntoSet(DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT)); 233 assertThat(Settings.Secure.getString(mContext.getContentResolver(), 234 Settings.Secure.DEFAULT_INPUT_METHOD)).isEqualTo( 235 DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT); 236 } 237 238 @Test disableInputMethod_twoDefaultableIMEsEnabled_removesIMEAndChangesDefault()239 public void disableInputMethod_twoDefaultableIMEsEnabled_removesIMEAndChangesDefault() { 240 InputMethodInfo info = createMockInputMethodInfo(mPackageManager, DUMMY_PACKAGE_NAME); 241 when(info.getId()).thenReturn(DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT); 242 when(mInputMethodManager.getEnabledInputMethodList()) 243 .thenReturn(mDummyEnabledInputMethodsListAllDefaultable); 244 245 InputMethodUtil.disableInputMethod(mContext, mInputMethodManager, info); 246 247 assertThat(Settings.Secure.getString(mContext.getContentResolver(), 248 Settings.Secure.ENABLED_INPUT_METHODS)).isEqualTo( 249 DUMMY_ENABLED_INPUT_METHOD_ID); 250 assertThat(Settings.Secure.getString(mContext.getContentResolver(), 251 Settings.Secure.DEFAULT_INPUT_METHOD)).isEqualTo( 252 DUMMY_ENABLED_INPUT_METHOD_ID); 253 } 254 255 @Test disableInputMethod_isDefaultWithNoOtherDefaultableEnabled_remainsUnchanged()256 public void disableInputMethod_isDefaultWithNoOtherDefaultableEnabled_remainsUnchanged() { 257 InputMethodInfo info = createMockInputMethodInfo(mPackageManager, DUMMY_PACKAGE_NAME); 258 when(info.getId()).thenReturn(DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT); 259 when(mInputMethodManager.getEnabledInputMethodList()) 260 .thenReturn(mDummyEnabledInputMethodsListOneDefaultable); 261 262 InputMethodUtil.disableInputMethod(mContext, mInputMethodManager, info); 263 264 assertThat(splitConcatenatedIdsIntoSet(Settings.Secure.getString(mContext 265 .getContentResolver(), Settings.Secure.ENABLED_INPUT_METHODS))).isEqualTo( 266 splitConcatenatedIdsIntoSet(DUMMY_ENABLED_INPUT_METHODS)); 267 assertThat(Settings.Secure.getString(mContext.getContentResolver(), 268 Settings.Secure.DEFAULT_INPUT_METHOD)).isEqualTo( 269 DUMMY_ENABLED_INPUT_METHOD_ID_DEFAULT); 270 } 271 createMockInputMethodInfoWithSubtypes( PackageManager packageManager, InputMethodManager inputMethodManager, String packageName)272 private static InputMethodInfo createMockInputMethodInfoWithSubtypes( 273 PackageManager packageManager, InputMethodManager inputMethodManager, 274 String packageName) { 275 InputMethodInfo mockInfo = createMockInputMethodInfo(packageManager, packageName); 276 List<InputMethodSubtype> subtypes = createSubtypes(); 277 when(inputMethodManager.getEnabledInputMethodSubtypeList( 278 eq(mockInfo), anyBoolean())).thenReturn(subtypes); 279 return mockInfo; 280 } 281 createMockInputMethodInfo( PackageManager packageManager, String packageName)282 private static InputMethodInfo createMockInputMethodInfo( 283 PackageManager packageManager, String packageName) { 284 InputMethodInfo mockInfo = mock(InputMethodInfo.class); 285 when(mockInfo.getPackageName()).thenReturn(packageName); 286 when(mockInfo.loadLabel(packageManager)).thenReturn(DUMMY_LABEL); 287 when(mockInfo.getServiceInfo()).thenReturn(new ServiceInfo()); 288 when(mockInfo.getSettingsActivity()).thenReturn(DUMMY_SETTINGS_ACTIVITY); 289 return mockInfo; 290 } 291 createSubtypes()292 private static List<InputMethodSubtype> createSubtypes() { 293 List<InputMethodSubtype> subtypes = new ArrayList<>(); 294 subtypes.add(createSubtype(1, "en_US")); 295 subtypes.add(createSubtype(2, "de_BE")); 296 subtypes.add(createSubtype(3, "oc-FR")); 297 return subtypes; 298 } 299 createSubtype(int id, String locale)300 private static InputMethodSubtype createSubtype(int id, String locale) { 301 return new InputMethodSubtypeBuilder().setSubtypeId(id).setSubtypeLocale(locale) 302 .setIsAuxiliary(false).setIsAsciiCapable(true).build(); 303 } 304 splitConcatenatedIdsIntoSet(String ids)305 private Set<String> splitConcatenatedIdsIntoSet(String ids) { 306 Set<String> result = new HashSet<>(); 307 308 if (ids == null || ids.isEmpty()) { 309 return result; 310 } 311 312 InputMethodUtil.sInputMethodSplitter.setString(ids); 313 while (InputMethodUtil.sInputMethodSplitter.hasNext()) { 314 result.add(InputMethodUtil.sInputMethodSplitter.next()); 315 } 316 317 return result; 318 } 319 } 320