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.settings.accessibility; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.Mockito.spy; 22 import static org.mockito.Mockito.verify; 23 import static org.mockito.Mockito.verifyNoMoreInteractions; 24 25 import android.accessibilityservice.AccessibilityServiceInfo; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.ResolveInfo; 30 import android.content.pm.ServiceInfo; 31 import android.os.Build; 32 import android.os.UserHandle; 33 import android.platform.test.annotations.DisableFlags; 34 import android.platform.test.annotations.EnableFlags; 35 import android.platform.test.flag.junit.SetFlagsRule; 36 import android.provider.Settings; 37 import android.view.accessibility.AccessibilityManager; 38 import android.view.accessibility.Flags; 39 40 import androidx.test.core.app.ApplicationProvider; 41 42 import com.android.internal.accessibility.util.ShortcutUtils; 43 import com.android.settings.R; 44 import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; 45 import com.android.settings.testutils.AccessibilityTestUtils; 46 47 import org.junit.Before; 48 import org.junit.Rule; 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.robolectric.RobolectricTestRunner; 52 import org.xmlpull.v1.XmlPullParserException; 53 54 import java.io.IOException; 55 import java.util.Set; 56 import java.util.StringJoiner; 57 58 @RunWith(RobolectricTestRunner.class) 59 public final class AccessibilityUtilTest { 60 private static final String SECURE_TEST_KEY = "secure_test_key"; 61 private static final String MOCK_PACKAGE_NAME = "com.mock.example"; 62 private static final String MOCK_CLASS_NAME = MOCK_PACKAGE_NAME + ".mock_a11y_service"; 63 private static final String MOCK_CLASS_NAME2 = MOCK_PACKAGE_NAME + ".mock_a11y_service2"; 64 private static final ComponentName MOCK_COMPONENT_NAME = new ComponentName(MOCK_PACKAGE_NAME, 65 MOCK_CLASS_NAME); 66 private static final ComponentName MOCK_COMPONENT_NAME2 = new ComponentName(MOCK_PACKAGE_NAME, 67 MOCK_CLASS_NAME2); 68 private static final String SOFTWARE_SHORTCUT_KEY = 69 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS; 70 private static final String HARDWARE_SHORTCUT_KEY = 71 Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; 72 private static final String QUICK_SETTINGS_SHORTCUT_KEY = 73 Settings.Secure.ACCESSIBILITY_QS_TARGETS; 74 75 private static final String PLACEHOLDER_SETTING_FEATURE = "placeholderSettingFeature"; 76 @Rule 77 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 78 79 private Context mContext; 80 81 @Before setUp()82 public void setUp() { 83 mContext = spy(ApplicationProvider.getApplicationContext()); 84 } 85 86 @Test capitalize_shouldReturnCapitalizedString()87 public void capitalize_shouldReturnCapitalizedString() { 88 assertThat(AccessibilityUtil.capitalize(null)).isNull(); 89 assertThat(AccessibilityUtil.capitalize("")).isEmpty(); 90 assertThat(AccessibilityUtil.capitalize("Hans")).isEqualTo("Hans"); 91 assertThat(AccessibilityUtil.capitalize("hans")).isEqualTo("Hans"); 92 assertThat(AccessibilityUtil.capitalize(",hans")).isEqualTo(",hans"); 93 assertThat(AccessibilityUtil.capitalize("Hans, Hans")).isEqualTo("Hans, hans"); 94 } 95 96 @Test getSummary_hasValueAndEqualsToOne_shouldReturnOnString()97 public void getSummary_hasValueAndEqualsToOne_shouldReturnOnString() { 98 setSettingsFeatureEnabled(SECURE_TEST_KEY, true); 99 100 final CharSequence result = AccessibilityUtil.getSummary(mContext, SECURE_TEST_KEY, 101 R.string.switch_on_text, R.string.switch_off_text); 102 103 assertThat(result) 104 .isEqualTo(mContext.getText(R.string.switch_on_text)); 105 } 106 107 @Test getSummary_hasValueAndEqualsToZero_shouldReturnOffString()108 public void getSummary_hasValueAndEqualsToZero_shouldReturnOffString() { 109 setSettingsFeatureEnabled(SECURE_TEST_KEY, false); 110 111 final CharSequence result = AccessibilityUtil.getSummary(mContext, SECURE_TEST_KEY, 112 R.string.switch_on_text, R.string.switch_off_text); 113 114 assertThat(result) 115 .isEqualTo(mContext.getText(R.string.switch_off_text)); 116 } 117 118 @Test getSummary_noValue_shouldReturnOffString()119 public void getSummary_noValue_shouldReturnOffString() { 120 final CharSequence result = AccessibilityUtil.getSummary(mContext, SECURE_TEST_KEY, 121 R.string.switch_on_text, R.string.switch_off_text); 122 123 assertThat(result) 124 .isEqualTo(mContext.getText(R.string.switch_off_text)); 125 } 126 127 @Test getAccessibilityServiceFragmentType_targetSdkQ_volumeShortcutType()128 public void getAccessibilityServiceFragmentType_targetSdkQ_volumeShortcutType() { 129 final AccessibilityServiceInfo info = getMockAccessibilityServiceInfo(); 130 131 info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q; 132 info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; 133 134 assertThat(AccessibilityUtil.getAccessibilityServiceFragmentType(info)).isEqualTo( 135 AccessibilityUtil.AccessibilityServiceFragmentType.VOLUME_SHORTCUT_TOGGLE); 136 } 137 138 @Test getAccessibilityServiceFragmentType_targetSdkR_HaveA11yButton_invisibleType()139 public void getAccessibilityServiceFragmentType_targetSdkR_HaveA11yButton_invisibleType() { 140 final AccessibilityServiceInfo info = getMockAccessibilityServiceInfo(); 141 142 info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; 143 info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; 144 145 assertThat(AccessibilityUtil.getAccessibilityServiceFragmentType(info)).isEqualTo( 146 AccessibilityUtil.AccessibilityServiceFragmentType.INVISIBLE_TOGGLE); 147 } 148 149 @Test getAccessibilityServiceFragmentType_targetSdkR_NoA11yButton_toggleType()150 public void getAccessibilityServiceFragmentType_targetSdkR_NoA11yButton_toggleType() { 151 final AccessibilityServiceInfo info = getMockAccessibilityServiceInfo(); 152 153 info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; 154 info.flags |= ~AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; 155 156 assertThat(AccessibilityUtil.getAccessibilityServiceFragmentType(info)).isEqualTo( 157 AccessibilityUtil.AccessibilityServiceFragmentType.TOGGLE); 158 } 159 160 @Test hasValueInSettings_putValue_hasValue()161 public void hasValueInSettings_putValue_hasValue() { 162 setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString()); 163 164 assertThat(AccessibilityUtil.hasValueInSettings(mContext, UserShortcutType.SOFTWARE, 165 MOCK_COMPONENT_NAME)).isTrue(); 166 } 167 168 @Test getUserShortcutTypeFromSettings_putOneValue_hasValue()169 public void getUserShortcutTypeFromSettings_putOneValue_hasValue() { 170 setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString()); 171 172 final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext, 173 MOCK_COMPONENT_NAME); 174 175 assertThat(shortcutTypes).isEqualTo( 176 UserShortcutType.SOFTWARE 177 ); 178 } 179 180 @Test getUserShortcutTypeFromSettings_putTwoValues_hasValue()181 public void getUserShortcutTypeFromSettings_putTwoValues_hasValue() { 182 setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString()); 183 setShortcut(UserShortcutType.HARDWARE, MOCK_COMPONENT_NAME.flattenToString()); 184 185 final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext, 186 MOCK_COMPONENT_NAME); 187 188 assertThat(shortcutTypes).isEqualTo( 189 UserShortcutType.SOFTWARE 190 | UserShortcutType.HARDWARE 191 ); 192 } 193 194 @Test 195 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) getUserShortcutTypeFromSettings_threeShortcutTypesChosen()196 public void getUserShortcutTypeFromSettings_threeShortcutTypesChosen() { 197 setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString()); 198 setShortcut(UserShortcutType.HARDWARE, MOCK_COMPONENT_NAME.flattenToString()); 199 setShortcut(UserShortcutType.QUICK_SETTINGS, MOCK_COMPONENT_NAME.flattenToString()); 200 201 final int shortcutTypes = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext, 202 MOCK_COMPONENT_NAME); 203 204 assertThat(shortcutTypes).isEqualTo( 205 UserShortcutType.SOFTWARE 206 | UserShortcutType.HARDWARE 207 | UserShortcutType.QUICK_SETTINGS 208 ); 209 } 210 211 @Test 212 @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) optInAllValuesToSettings_optInValue_haveMatchString()213 public void optInAllValuesToSettings_optInValue_haveMatchString() { 214 clearShortcuts(); 215 int shortcutTypes = UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE; 216 217 AccessibilityUtil.optInAllValuesToSettings(mContext, shortcutTypes, MOCK_COMPONENT_NAME); 218 219 assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo( 220 MOCK_COMPONENT_NAME.flattenToString()); 221 assertThat(getStringFromSettings(HARDWARE_SHORTCUT_KEY)).isEqualTo( 222 MOCK_COMPONENT_NAME.flattenToString()); 223 224 } 225 226 @Test 227 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) optInAllValuesToSettings_optInValue_callsA11yManager()228 public void optInAllValuesToSettings_optInValue_callsA11yManager() { 229 AccessibilityManager a11yManager = 230 AccessibilityTestUtils.setupMockAccessibilityManager(mContext); 231 Set<String> shortcutTargets = Set.of(MOCK_COMPONENT_NAME.flattenToString()); 232 int shortcutTypes = UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE 233 | UserShortcutType.QUICK_SETTINGS; 234 235 AccessibilityUtil.optInAllValuesToSettings(mContext, shortcutTypes, MOCK_COMPONENT_NAME); 236 237 verify(a11yManager).enableShortcutsForTargets( 238 /* enable= */ true, shortcutTypes, 239 shortcutTargets, UserHandle.myUserId()); 240 verifyNoMoreInteractions(a11yManager); 241 } 242 243 @Test 244 @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) optInValueToSettings_optInValue_haveMatchString()245 public void optInValueToSettings_optInValue_haveMatchString() { 246 setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString()); 247 248 AccessibilityUtil.optInValueToSettings(mContext, UserShortcutType.SOFTWARE, 249 MOCK_COMPONENT_NAME2); 250 251 assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo( 252 MOCK_COMPONENT_NAME.flattenToString() + ":" 253 + MOCK_COMPONENT_NAME2.flattenToString()); 254 } 255 256 @Test 257 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) optInValueToSettings_optInValue_callsA11yManager()258 public void optInValueToSettings_optInValue_callsA11yManager() { 259 AccessibilityManager a11yManager = 260 AccessibilityTestUtils.setupMockAccessibilityManager(mContext); 261 Set<String> shortcutTargets = Set.of(MOCK_COMPONENT_NAME2.flattenToString()); 262 263 AccessibilityUtil.optInValueToSettings( 264 mContext, UserShortcutType.HARDWARE, MOCK_COMPONENT_NAME2); 265 266 verify(a11yManager).enableShortcutsForTargets( 267 /* enable= */ true, UserShortcutType.HARDWARE, 268 shortcutTargets, UserHandle.myUserId()); 269 verifyNoMoreInteractions(a11yManager); 270 } 271 272 @Test 273 @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) optInValueToSettings_optInTwoValues_haveMatchString()274 public void optInValueToSettings_optInTwoValues_haveMatchString() { 275 setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString()); 276 277 AccessibilityUtil.optInValueToSettings(mContext, UserShortcutType.SOFTWARE, 278 MOCK_COMPONENT_NAME2); 279 AccessibilityUtil.optInValueToSettings(mContext, UserShortcutType.SOFTWARE, 280 MOCK_COMPONENT_NAME2); 281 282 assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo( 283 MOCK_COMPONENT_NAME.flattenToString() + ":" 284 + MOCK_COMPONENT_NAME2.flattenToString()); 285 } 286 287 @Test 288 @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) optOutAllValuesToSettings_optOutValue_emptyString()289 public void optOutAllValuesToSettings_optOutValue_emptyString() { 290 setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString()); 291 setShortcut(UserShortcutType.HARDWARE, MOCK_COMPONENT_NAME.flattenToString()); 292 int shortcutTypes = 293 UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE | UserShortcutType.TRIPLETAP; 294 295 AccessibilityUtil.optOutAllValuesFromSettings(mContext, shortcutTypes, 296 MOCK_COMPONENT_NAME); 297 298 assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEmpty(); 299 assertThat(getStringFromSettings(HARDWARE_SHORTCUT_KEY)).isEmpty(); 300 } 301 302 @Test 303 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) optOutAllValuesToSettings_optOutValue_callsA1yManager()304 public void optOutAllValuesToSettings_optOutValue_callsA1yManager() { 305 AccessibilityManager a11yManager = 306 AccessibilityTestUtils.setupMockAccessibilityManager(mContext); 307 int shortcutTypes = 308 UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE 309 | UserShortcutType.QUICK_SETTINGS; 310 Set<String> shortcutTargets = Set.of(MOCK_COMPONENT_NAME.flattenToString()); 311 312 AccessibilityUtil.optOutAllValuesFromSettings(mContext, shortcutTypes, 313 MOCK_COMPONENT_NAME); 314 315 verify(a11yManager).enableShortcutsForTargets( 316 /* enable= */ false, 317 shortcutTypes, 318 shortcutTargets, UserHandle.myUserId()); 319 verifyNoMoreInteractions(a11yManager); 320 } 321 322 @Test 323 @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) optOutValueFromSettings_optOutValue_emptyString()324 public void optOutValueFromSettings_optOutValue_emptyString() { 325 setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString()); 326 327 AccessibilityUtil.optOutValueFromSettings(mContext, UserShortcutType.SOFTWARE, 328 MOCK_COMPONENT_NAME); 329 330 assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEmpty(); 331 } 332 333 @Test 334 @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) optOutValueFromSettings_optOutValue_haveMatchString()335 public void optOutValueFromSettings_optOutValue_haveMatchString() { 336 setShortcut(UserShortcutType.SOFTWARE, MOCK_COMPONENT_NAME.flattenToString(), 337 MOCK_COMPONENT_NAME2.flattenToString()); 338 339 AccessibilityUtil.optOutValueFromSettings(mContext, UserShortcutType.SOFTWARE, 340 MOCK_COMPONENT_NAME2); 341 342 assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo( 343 MOCK_COMPONENT_NAME.flattenToString()); 344 } 345 346 @Test 347 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) optOutValueFromSettings_optOutValue_callsA11yManager()348 public void optOutValueFromSettings_optOutValue_callsA11yManager() { 349 AccessibilityManager a11yManager = 350 AccessibilityTestUtils.setupMockAccessibilityManager(mContext); 351 Set<String> shortcutTargets = Set.of(MOCK_COMPONENT_NAME.flattenToString()); 352 353 AccessibilityUtil.optOutValueFromSettings( 354 mContext, UserShortcutType.QUICK_SETTINGS, MOCK_COMPONENT_NAME); 355 356 verify(a11yManager).enableShortcutsForTargets( 357 /* enable= */ false, UserShortcutType.QUICK_SETTINGS, 358 shortcutTargets, UserHandle.myUserId()); 359 verifyNoMoreInteractions(a11yManager); 360 } 361 362 @Test convertKeyFromSettings_shortcutTypeSoftware()363 public void convertKeyFromSettings_shortcutTypeSoftware() { 364 assertThat(AccessibilityUtil.convertKeyFromSettings(UserShortcutType.SOFTWARE)) 365 .isEqualTo(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS); 366 } 367 368 @Test convertKeyFromSettings_shortcutTypeHardware()369 public void convertKeyFromSettings_shortcutTypeHardware() { 370 assertThat(AccessibilityUtil.convertKeyFromSettings(UserShortcutType.HARDWARE)) 371 .isEqualTo(Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); 372 } 373 374 @Test convertKeyFromSettings_shortcutTypeTripleTap()375 public void convertKeyFromSettings_shortcutTypeTripleTap() { 376 assertThat(AccessibilityUtil.convertKeyFromSettings(UserShortcutType.TRIPLETAP)) 377 .isEqualTo(Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED); 378 } 379 380 @Test 381 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) convertKeyFromSettings_shortcutTypeMultiFingersMultiTap()382 public void convertKeyFromSettings_shortcutTypeMultiFingersMultiTap() { 383 assertThat(AccessibilityUtil.convertKeyFromSettings(UserShortcutType.TWOFINGER_DOUBLETAP)) 384 .isEqualTo( 385 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_TWO_FINGER_TRIPLE_TAP_ENABLED); 386 } 387 388 @Test 389 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) convertKeyFromSettings_shortcutTypeQuickSettings()390 public void convertKeyFromSettings_shortcutTypeQuickSettings() { 391 assertThat(AccessibilityUtil.convertKeyFromSettings(UserShortcutType.QUICK_SETTINGS)) 392 .isEqualTo(Settings.Secure.ACCESSIBILITY_QS_TARGETS); 393 } 394 getMockAccessibilityServiceInfo()395 private AccessibilityServiceInfo getMockAccessibilityServiceInfo() { 396 final ApplicationInfo applicationInfo = new ApplicationInfo(); 397 final ServiceInfo serviceInfo = new ServiceInfo(); 398 applicationInfo.packageName = MOCK_PACKAGE_NAME; 399 serviceInfo.packageName = MOCK_PACKAGE_NAME; 400 serviceInfo.name = MOCK_CLASS_NAME; 401 serviceInfo.applicationInfo = applicationInfo; 402 403 final ResolveInfo resolveInfo = new ResolveInfo(); 404 resolveInfo.serviceInfo = serviceInfo; 405 406 try { 407 final AccessibilityServiceInfo info = new AccessibilityServiceInfo(resolveInfo, 408 mContext); 409 info.setComponentName(MOCK_COMPONENT_NAME); 410 return info; 411 } catch (XmlPullParserException | IOException e) { 412 // Do nothing 413 } 414 415 return null; 416 } 417 getStringFromSettings(String key)418 private String getStringFromSettings(String key) { 419 return Settings.Secure.getString(mContext.getContentResolver(), key); 420 } 421 setSettingsFeatureEnabled(String settingsKey, boolean enabled)422 private void setSettingsFeatureEnabled(String settingsKey, boolean enabled) { 423 Settings.Secure.putInt(mContext.getContentResolver(), 424 settingsKey, 425 enabled ? AccessibilityUtil.State.ON : AccessibilityUtil.State.OFF); 426 } 427 setShortcut(@serShortcutType int shortcutType, String... componentNames)428 private void setShortcut(@UserShortcutType int shortcutType, String... componentNames) { 429 StringJoiner shortcutComponents = new StringJoiner(":"); 430 for (String componentName : componentNames) { 431 shortcutComponents.add(componentName); 432 } 433 Settings.Secure.putString(mContext.getContentResolver(), 434 ShortcutUtils.convertToKey(shortcutType), shortcutComponents.toString()); 435 } 436 clearShortcuts()437 private void clearShortcuts() { 438 Settings.Secure.putString(mContext.getContentResolver(), SOFTWARE_SHORTCUT_KEY, ""); 439 Settings.Secure.putString(mContext.getContentResolver(), HARDWARE_SHORTCUT_KEY, ""); 440 Settings.Secure.putString(mContext.getContentResolver(), QUICK_SETTINGS_SHORTCUT_KEY, ""); 441 } 442 } 443