1 /* 2 * Copyright (C) 2022 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.ArgumentMatchers.any; 22 import static org.mockito.Mockito.doNothing; 23 import static org.mockito.Mockito.doReturn; 24 import static org.mockito.Mockito.spy; 25 import static org.mockito.Mockito.verify; 26 import static org.mockito.Mockito.when; 27 28 import android.accessibilityservice.AccessibilityServiceInfo; 29 import android.content.ComponentName; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.pm.ApplicationInfo; 33 import android.content.pm.PackageManager; 34 import android.content.pm.ResolveInfo; 35 import android.content.pm.ServiceInfo; 36 import android.os.Bundle; 37 import android.platform.test.annotations.DisableFlags; 38 import android.platform.test.annotations.EnableFlags; 39 import android.platform.test.flag.junit.SetFlagsRule; 40 import android.provider.Settings; 41 import android.service.quicksettings.TileService; 42 import android.view.accessibility.AccessibilityManager; 43 import android.view.accessibility.Flags; 44 45 import androidx.annotation.NonNull; 46 import androidx.preference.PreferenceManager; 47 import androidx.preference.PreferenceScreen; 48 import androidx.test.core.app.ApplicationProvider; 49 50 import com.android.internal.accessibility.common.ShortcutConstants; 51 import com.android.settings.R; 52 import com.android.settings.SettingsActivity; 53 import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType; 54 import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment; 55 import com.android.settings.widget.SettingsMainSwitchPreference; 56 57 import org.junit.Before; 58 import org.junit.Rule; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 import org.mockito.Answers; 62 import org.mockito.ArgumentCaptor; 63 import org.mockito.Mock; 64 import org.mockito.MockitoAnnotations; 65 import org.robolectric.RobolectricTestRunner; 66 import org.robolectric.Shadows; 67 import org.robolectric.shadow.api.Shadow; 68 import org.robolectric.shadows.ShadowAccessibilityManager; 69 import org.robolectric.shadows.ShadowPackageManager; 70 71 import java.util.List; 72 import java.util.Set; 73 74 /** Tests for {@link ToggleAccessibilityServicePreferenceFragment} */ 75 @RunWith(RobolectricTestRunner.class) 76 public class ToggleAccessibilityServicePreferenceFragmentTest { 77 78 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 79 80 private static final String PLACEHOLDER_PACKAGE_NAME = "com.placeholder.example"; 81 private static final String PLACEHOLDER_PACKAGE_NAME2 = "com.placeholder.example2"; 82 private static final String PLACEHOLDER_SERVICE_CLASS_NAME = "a11yservice1"; 83 private static final String PLACEHOLDER_SERVICE_CLASS_NAME2 = "a11yservice2"; 84 private static final String PLACEHOLDER_TILE_CLASS_NAME = 85 PLACEHOLDER_PACKAGE_NAME + "tile.placeholder"; 86 private static final String PLACEHOLDER_TILE_CLASS_NAME2 = 87 PLACEHOLDER_PACKAGE_NAME + "tile.placeholder2"; 88 private static final ComponentName PLACEHOLDER_TILE_COMPONENT_NAME = new ComponentName( 89 PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME); 90 private static final String PLACEHOLDER_TILE_NAME = 91 PLACEHOLDER_PACKAGE_NAME + "tile.placeholder"; 92 private static final String PLACEHOLDER_TILE_NAME2 = 93 PLACEHOLDER_PACKAGE_NAME + "tile.placeholder2"; 94 private static final int NO_DIALOG = -1; 95 96 private TestToggleAccessibilityServicePreferenceFragment mFragment; 97 private PreferenceScreen mScreen; 98 private Context mContext; 99 100 private ShadowAccessibilityManager mShadowAccessibilityManager; 101 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 102 private PreferenceManager mPreferenceManager; 103 @Mock 104 private AccessibilityManager mMockAccessibilityManager; 105 106 @Before setUpTestFragment()107 public void setUpTestFragment() { 108 MockitoAnnotations.initMocks(this); 109 110 mContext = spy(ApplicationProvider.getApplicationContext()); 111 mFragment = spy(new TestToggleAccessibilityServicePreferenceFragment()); 112 mFragment.setArguments(new Bundle()); 113 when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager); 114 when(mFragment.getPreferenceManager().getContext()).thenReturn(mContext); 115 when(mFragment.getContext()).thenReturn(mContext); 116 mScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null)); 117 when(mScreen.getPreferenceManager()).thenReturn(mPreferenceManager); 118 doReturn(mScreen).when(mFragment).getPreferenceScreen(); 119 mShadowAccessibilityManager = Shadow.extract(AccessibilityManager.getInstance(mContext)); 120 } 121 122 @Test getTileTooltipContent_noTileServiceAssigned_returnNull()123 public void getTileTooltipContent_noTileServiceAssigned_returnNull() { 124 final CharSequence tileTooltipContent = 125 mFragment.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_EDIT); 126 127 assertThat(tileTooltipContent).isNull(); 128 } 129 130 @Test getTileTooltipContent_hasOneTileService_guideToEdit_haveMatchString()131 public void getTileTooltipContent_hasOneTileService_guideToEdit_haveMatchString() { 132 setupTileService(PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME, 133 PLACEHOLDER_TILE_NAME); 134 135 final CharSequence tileTooltipContent = 136 mFragment.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_EDIT); 137 final CharSequence tileName = 138 mFragment.loadTileLabel(mContext, mFragment.getTileComponentName()); 139 assertThat(tileTooltipContent.toString()).isEqualTo( 140 mContext.getString(R.string.accessibility_service_qs_tooltip_content, tileName)); 141 } 142 143 @Test getTileTooltipContent_hasOneTileService_guideToDirectUse_haveMatchString()144 public void getTileTooltipContent_hasOneTileService_guideToDirectUse_haveMatchString() { 145 setupTileService(PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME, 146 PLACEHOLDER_TILE_NAME); 147 148 final CharSequence tileTooltipContent = 149 mFragment.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE); 150 final CharSequence tileName = 151 mFragment.loadTileLabel(mContext, mFragment.getTileComponentName()); 152 assertThat(tileTooltipContent.toString()).isEqualTo( 153 mContext.getString( 154 R.string.accessibility_service_auto_added_qs_tooltip_content, tileName)); 155 } 156 157 @Test getTileTooltipContent_hasTwoTileServices_guideToEdit_haveMatchString()158 public void getTileTooltipContent_hasTwoTileServices_guideToEdit_haveMatchString() { 159 setupTileService(PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME, 160 PLACEHOLDER_TILE_NAME); 161 setupTileService(PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME2, 162 PLACEHOLDER_TILE_NAME2); 163 164 final CharSequence tileTooltipContent = 165 mFragment.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_EDIT); 166 final CharSequence tileName = 167 mFragment.loadTileLabel(mContext, mFragment.getTileComponentName()); 168 assertThat(tileTooltipContent.toString()).isEqualTo( 169 mContext.getString(R.string.accessibility_service_qs_tooltip_content, tileName)); 170 } 171 172 @Test getTileTooltipContent_hasTwoTileServices_guideToDirectUse_haveMatchString()173 public void getTileTooltipContent_hasTwoTileServices_guideToDirectUse_haveMatchString() { 174 setupTileService(PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME, 175 PLACEHOLDER_TILE_NAME); 176 setupTileService(PLACEHOLDER_PACKAGE_NAME, PLACEHOLDER_TILE_CLASS_NAME2, 177 PLACEHOLDER_TILE_NAME2); 178 179 final CharSequence tileTooltipContent = 180 mFragment.getTileTooltipContent(QuickSettingsTooltipType.GUIDE_TO_DIRECT_USE); 181 final CharSequence tileName = 182 mFragment.loadTileLabel(mContext, mFragment.getTileComponentName()); 183 assertThat(tileTooltipContent.toString()).isEqualTo( 184 mContext.getString( 185 R.string.accessibility_service_auto_added_qs_tooltip_content, tileName)); 186 } 187 188 @Test getAccessibilityServiceInfo()189 public void getAccessibilityServiceInfo() throws Throwable { 190 final AccessibilityServiceInfo info1 = getFakeAccessibilityServiceInfo( 191 PLACEHOLDER_PACKAGE_NAME, 192 PLACEHOLDER_SERVICE_CLASS_NAME); 193 final AccessibilityServiceInfo info2 = getFakeAccessibilityServiceInfo( 194 PLACEHOLDER_PACKAGE_NAME, 195 PLACEHOLDER_SERVICE_CLASS_NAME2); 196 final AccessibilityServiceInfo info3 = getFakeAccessibilityServiceInfo( 197 PLACEHOLDER_PACKAGE_NAME2, 198 PLACEHOLDER_SERVICE_CLASS_NAME); 199 final AccessibilityServiceInfo info4 = getFakeAccessibilityServiceInfo( 200 PLACEHOLDER_PACKAGE_NAME2, 201 PLACEHOLDER_SERVICE_CLASS_NAME2); 202 mShadowAccessibilityManager.setInstalledAccessibilityServiceList( 203 List.of(info1, info2, info3, info4)); 204 205 mFragment.mComponentName = info3.getComponentName(); 206 207 assertThat(mFragment.getAccessibilityServiceInfo()).isEqualTo(info3); 208 } 209 210 @Test getAccessibilityServiceInfo_notFound()211 public void getAccessibilityServiceInfo_notFound() throws Throwable { 212 mShadowAccessibilityManager.setInstalledAccessibilityServiceList(List.of()); 213 214 mFragment.mComponentName = getFakeAccessibilityServiceInfo(PLACEHOLDER_PACKAGE_NAME, 215 PLACEHOLDER_SERVICE_CLASS_NAME).getComponentName(); 216 217 assertThat(mFragment.getAccessibilityServiceInfo()).isNull(); 218 } 219 220 @Test serviceSupportsAccessibilityButton()221 public void serviceSupportsAccessibilityButton() throws Throwable { 222 final AccessibilityServiceInfo infoWithA11yButton = getFakeAccessibilityServiceInfo( 223 PLACEHOLDER_PACKAGE_NAME, 224 PLACEHOLDER_SERVICE_CLASS_NAME); 225 infoWithA11yButton.flags = AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; 226 final AccessibilityServiceInfo infoWithoutA11yButton = getFakeAccessibilityServiceInfo( 227 PLACEHOLDER_PACKAGE_NAME2, 228 PLACEHOLDER_SERVICE_CLASS_NAME2); 229 infoWithoutA11yButton.flags = 0; 230 mShadowAccessibilityManager.setInstalledAccessibilityServiceList( 231 List.of(infoWithA11yButton, infoWithoutA11yButton)); 232 233 mFragment.mComponentName = infoWithA11yButton.getComponentName(); 234 assertThat(mFragment.serviceSupportsAccessibilityButton()).isTrue(); 235 mFragment.mComponentName = infoWithoutA11yButton.getComponentName(); 236 assertThat(mFragment.serviceSupportsAccessibilityButton()).isFalse(); 237 } 238 239 @Test enableService_warningRequired_showWarning()240 public void enableService_warningRequired_showWarning() throws Throwable { 241 setupServiceWarningRequired(true); 242 mFragment.mToggleServiceSwitchPreference = 243 new SettingsMainSwitchPreference(mContext, /* attrs= */null); 244 245 mFragment.onCheckedChanged(null, true); 246 247 assertThat(mFragment.mLastShownDialogId).isEqualTo( 248 AccessibilityDialogUtils.DialogEnums.ENABLE_WARNING_FROM_TOGGLE); 249 } 250 251 @Test enableService_warningNotRequired_dontShowWarning()252 public void enableService_warningNotRequired_dontShowWarning() throws Throwable { 253 final AccessibilityServiceInfo info = setupServiceWarningRequired(false); 254 mFragment.mToggleServiceSwitchPreference = 255 new SettingsMainSwitchPreference(mContext, /* attrs= */null); 256 mFragment.mPreferenceKey = info.getComponentName().flattenToString(); 257 258 mFragment.onCheckedChanged(null, true); 259 260 assertThat(mFragment.mLastShownDialogId).isEqualTo( 261 AccessibilityDialogUtils.DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 262 } 263 264 @Test toggleShortcutPreference_warningRequired_showWarning()265 public void toggleShortcutPreference_warningRequired_showWarning() throws Throwable { 266 setupServiceWarningRequired(true); 267 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null); 268 269 mFragment.mShortcutPreference.setChecked(true); 270 mFragment.onToggleClicked(mFragment.mShortcutPreference); 271 272 assertThat(mFragment.mLastShownDialogId).isEqualTo( 273 AccessibilityDialogUtils.DialogEnums.ENABLE_WARNING_FROM_SHORTCUT_TOGGLE); 274 assertThat(mFragment.mShortcutPreference.isChecked()).isFalse(); 275 } 276 277 @Test toggleShortcutPreference_warningNotRequired_dontShowWarning()278 public void toggleShortcutPreference_warningNotRequired_dontShowWarning() throws Throwable { 279 setupServiceWarningRequired(false); 280 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null); 281 282 mFragment.mShortcutPreference.setChecked(true); 283 mFragment.onToggleClicked(mFragment.mShortcutPreference); 284 285 assertThat(mFragment.mLastShownDialogId).isEqualTo( 286 AccessibilityDialogUtils.DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); 287 assertThat(mFragment.mShortcutPreference.isChecked()).isTrue(); 288 } 289 290 @Test clickShortcutSettingsPreference_warningRequired_showWarning()291 public void clickShortcutSettingsPreference_warningRequired_showWarning() throws Throwable { 292 setupServiceWarningRequired(true); 293 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null); 294 295 mFragment.onSettingsClicked(mFragment.mShortcutPreference); 296 297 assertThat(mFragment.mLastShownDialogId).isEqualTo( 298 AccessibilityDialogUtils.DialogEnums.ENABLE_WARNING_FROM_SHORTCUT); 299 } 300 301 @Test 302 @DisableFlags( 303 com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN) clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_showDialog()304 public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_showDialog() 305 throws Throwable { 306 setupServiceWarningRequired(false); 307 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null); 308 309 mFragment.onSettingsClicked(mFragment.mShortcutPreference); 310 311 assertThat(mFragment.mLastShownDialogId).isEqualTo( 312 AccessibilityDialogUtils.DialogEnums.EDIT_SHORTCUT); 313 } 314 315 @Test 316 @EnableFlags(com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN) clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_launchActivity()317 public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_launchActivity() 318 throws Throwable { 319 setupServiceWarningRequired(false); 320 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null); 321 doNothing().when(mContext).startActivity(any()); 322 323 mFragment.onSettingsClicked(mFragment.mShortcutPreference); 324 325 ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); 326 verify(mContext).startActivity(captor.capture()); 327 assertThat(captor.getValue().getExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) 328 .isEqualTo(EditShortcutsPreferenceFragment.class.getName()); 329 } 330 331 @Test 332 @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) getDefaultShortcutTypes_noAssociatedTile_softwareTypeIsDefault()333 public void getDefaultShortcutTypes_noAssociatedTile_softwareTypeIsDefault() throws Throwable { 334 PreferredShortcuts.clearPreferredShortcuts(mContext); 335 setupAccessibilityServiceInfoForFragment( 336 /* isAccessibilityTool= */ true, 337 /* tileService= */ null 338 /* warningRequired= */); 339 340 assertThat(mFragment.getDefaultShortcutTypes()) 341 .isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE); 342 } 343 344 @Test 345 @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) getDefaultShortcutTypes_hasAssociatedTile_softwareTypeIsDefault()346 public void getDefaultShortcutTypes_hasAssociatedTile_softwareTypeIsDefault() { 347 PreferredShortcuts.clearPreferredShortcuts(mContext); 348 when(mFragment.getTileComponentName()).thenReturn(PLACEHOLDER_TILE_COMPONENT_NAME); 349 350 assertThat(mFragment.getDefaultShortcutTypes()) 351 .isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE); 352 } 353 354 @Test 355 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) getDefaultShortcutTypes_isAccessibilityTool_hasAssociatedTile_qsTypeIsDefault()356 public void getDefaultShortcutTypes_isAccessibilityTool_hasAssociatedTile_qsTypeIsDefault() 357 throws Throwable { 358 PreferredShortcuts.clearPreferredShortcuts(mContext); 359 setupAccessibilityServiceInfoForFragment( 360 /* isAccessibilityTool= */ true, 361 /* tileService= */ PLACEHOLDER_TILE_COMPONENT_NAME 362 /* warningRequired= */); 363 364 assertThat(mFragment.getDefaultShortcutTypes()) 365 .isEqualTo(ShortcutConstants.UserShortcutType.QUICK_SETTINGS); 366 } 367 368 @Test 369 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) getDefaultShortcutTypes_isNotAccessibilityTool_hasAssociatedTile_softwareTypeIsDefault()370 public void getDefaultShortcutTypes_isNotAccessibilityTool_hasAssociatedTile_softwareTypeIsDefault() 371 throws Throwable { 372 PreferredShortcuts.clearPreferredShortcuts(mContext); 373 setupAccessibilityServiceInfoForFragment( 374 /* isAccessibilityTool= */ false, 375 /* tileService= */ PLACEHOLDER_TILE_COMPONENT_NAME 376 /* warningRequired= */); 377 378 assertThat(mFragment.getDefaultShortcutTypes()) 379 .isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE); 380 } 381 382 @Test 383 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) getDefaultShortcutTypes_isAccessibilityTool_noAssociatedTile_softwareTypeIsDefault()384 public void getDefaultShortcutTypes_isAccessibilityTool_noAssociatedTile_softwareTypeIsDefault() 385 throws Throwable { 386 PreferredShortcuts.clearPreferredShortcuts(mContext); 387 setupAccessibilityServiceInfoForFragment( 388 /* isAccessibilityTool= */ true, 389 /* tileService= */ null 390 /* warningRequired= */); 391 392 assertThat(mFragment.getDefaultShortcutTypes()) 393 .isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE); 394 } 395 396 @Test 397 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) getDefaultShortcutTypes_isNotAccessibilityTool_noAssociatedTile_softwareTypeIsDefault()398 public void getDefaultShortcutTypes_isNotAccessibilityTool_noAssociatedTile_softwareTypeIsDefault() 399 throws Throwable { 400 PreferredShortcuts.clearPreferredShortcuts(mContext); 401 setupAccessibilityServiceInfoForFragment( 402 /* isAccessibilityTool= */ false, 403 /* tileService= */ null 404 /* warningRequired= */); 405 406 assertThat(mFragment.getDefaultShortcutTypes()) 407 .isEqualTo(ShortcutConstants.UserShortcutType.SOFTWARE); 408 } 409 410 @Test 411 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) toggleShortcutPreference_noUserPreferredShortcut_hasQsTile_enableQsShortcut()412 public void toggleShortcutPreference_noUserPreferredShortcut_hasQsTile_enableQsShortcut() 413 throws Throwable { 414 PreferredShortcuts.clearPreferredShortcuts(mContext); 415 setupAccessibilityServiceInfoForFragment( 416 /* isAccessibilityTool= */ true, 417 /* tileService= */ PLACEHOLDER_TILE_COMPONENT_NAME 418 /* warningRequired= */); 419 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null); 420 421 mFragment.mShortcutPreference.setChecked(true); 422 mFragment.onToggleClicked(mFragment.mShortcutPreference); 423 424 verify(mMockAccessibilityManager) 425 .enableShortcutsForTargets(true, 426 ShortcutConstants.UserShortcutType.QUICK_SETTINGS, 427 Set.of(mFragment.mComponentName.flattenToString()), mContext.getUserId()); 428 } 429 430 @Test 431 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) toggleShortcutPreference_noUserPreferredShortcut_noQsTile_enableSoftwareShortcut()432 public void toggleShortcutPreference_noUserPreferredShortcut_noQsTile_enableSoftwareShortcut() 433 throws Throwable { 434 PreferredShortcuts.clearPreferredShortcuts(mContext); 435 setupAccessibilityServiceInfoForFragment( 436 /* isAccessibilityTool= */ true, 437 /* tileService= */ null 438 /* warningRequired= */); 439 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null); 440 441 mFragment.mShortcutPreference.setChecked(true); 442 mFragment.onToggleClicked(mFragment.mShortcutPreference); 443 444 verify(mMockAccessibilityManager) 445 .enableShortcutsForTargets(true, 446 ShortcutConstants.UserShortcutType.SOFTWARE, 447 Set.of(mFragment.mComponentName.flattenToString()), mContext.getUserId()); 448 } 449 450 @Test 451 @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) toggleShortcutPreference_noUserPreferredShortcut_hasQsTile_flagOff_enableSoftwareShortcut()452 public void toggleShortcutPreference_noUserPreferredShortcut_hasQsTile_flagOff_enableSoftwareShortcut() 453 throws Throwable { 454 PreferredShortcuts.clearPreferredShortcuts(mContext); 455 setupAccessibilityServiceInfoForFragment( 456 /* isAccessibilityTool= */ true, 457 /* tileService= */ PLACEHOLDER_TILE_COMPONENT_NAME 458 /* warningRequired= */); 459 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null); 460 461 mFragment.mShortcutPreference.setChecked(true); 462 mFragment.onToggleClicked(mFragment.mShortcutPreference); 463 464 assertThat( 465 Settings.Secure.getString(mContext.getContentResolver(), 466 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)) 467 .contains(mFragment.mComponentName.flattenToString()); 468 } 469 470 @Test 471 @DisableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) toggleShortcutPreference_noUserPreferredShortcut_noQsTile_flagOff_enableSoftwareShortcut()472 public void toggleShortcutPreference_noUserPreferredShortcut_noQsTile_flagOff_enableSoftwareShortcut() 473 throws Throwable { 474 PreferredShortcuts.clearPreferredShortcuts(mContext); 475 setupAccessibilityServiceInfoForFragment( 476 /* isAccessibilityTool= */ true, 477 /* tileService= */ null 478 /* warningRequired= */); 479 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null); 480 481 mFragment.mShortcutPreference.setChecked(true); 482 mFragment.onToggleClicked(mFragment.mShortcutPreference); 483 484 assertThat( 485 Settings.Secure.getString(mContext.getContentResolver(), 486 Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS)) 487 .contains(mFragment.mComponentName.flattenToString()); 488 } 489 490 @Test 491 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) toggleShortcutPreference_userPreferVolumeKeysShortcut_noQsTile_enableVolumeKeysShortcut()492 public void toggleShortcutPreference_userPreferVolumeKeysShortcut_noQsTile_enableVolumeKeysShortcut() 493 throws Throwable { 494 setupAccessibilityServiceInfoForFragment( 495 /* isAccessibilityTool= */ true, 496 /* tileService= */ null 497 /* warningRequired= */); 498 String componentName = mFragment.mComponentName.flattenToString(); 499 PreferredShortcuts.saveUserShortcutType( 500 mContext, 501 new PreferredShortcut(componentName, ShortcutConstants.UserShortcutType.HARDWARE)); 502 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null); 503 504 mFragment.mShortcutPreference.setChecked(true); 505 mFragment.onToggleClicked(mFragment.mShortcutPreference); 506 507 verify(mMockAccessibilityManager) 508 .enableShortcutsForTargets( 509 true, 510 ShortcutConstants.UserShortcutType.HARDWARE, 511 Set.of(componentName), 512 mContext.getUserId()); 513 } 514 515 @Test 516 @EnableFlags(Flags.FLAG_A11Y_QS_SHORTCUT) toggleShortcutPreference_userPreferVolumeKeysShortcut_hasQsTile_enableVolumeKeysShortcut()517 public void toggleShortcutPreference_userPreferVolumeKeysShortcut_hasQsTile_enableVolumeKeysShortcut() 518 throws Throwable { 519 setupAccessibilityServiceInfoForFragment( 520 /* isAccessibilityTool= */ true, 521 /* tileService= */ PLACEHOLDER_TILE_COMPONENT_NAME 522 /* warningRequired= */); 523 String componentName = mFragment.mComponentName.flattenToString(); 524 PreferredShortcuts.saveUserShortcutType( 525 mContext, 526 new PreferredShortcut(componentName, ShortcutConstants.UserShortcutType.HARDWARE)); 527 mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null); 528 529 mFragment.mShortcutPreference.setChecked(true); 530 mFragment.onToggleClicked(mFragment.mShortcutPreference); 531 532 verify(mMockAccessibilityManager) 533 .enableShortcutsForTargets( 534 true, 535 ShortcutConstants.UserShortcutType.HARDWARE, 536 Set.of(componentName), 537 mContext.getUserId()); 538 } 539 setupTileService(String packageName, String name, String tileName)540 private void setupTileService(String packageName, String name, String tileName) { 541 final Intent tileProbe = new Intent(TileService.ACTION_QS_TILE); 542 final ResolveInfo info = new ResolveInfo(); 543 info.serviceInfo = new FakeServiceInfo(packageName, name, tileName); 544 final ShadowPackageManager shadowPackageManager = 545 Shadows.shadowOf(mContext.getPackageManager()); 546 shadowPackageManager.addResolveInfoForIntent(tileProbe, info); 547 } 548 setupServiceWarningRequired(boolean required)549 private AccessibilityServiceInfo setupServiceWarningRequired(boolean required) 550 throws Throwable { 551 final AccessibilityServiceInfo info = getFakeAccessibilityServiceInfo( 552 PLACEHOLDER_PACKAGE_NAME, 553 PLACEHOLDER_SERVICE_CLASS_NAME); 554 info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON; 555 mFragment.mComponentName = info.getComponentName(); 556 when(mContext.getSystemService(AccessibilityManager.class)) 557 .thenReturn(mMockAccessibilityManager); 558 when(mMockAccessibilityManager.isAccessibilityServiceWarningRequired(any())) 559 .thenReturn(required); 560 when(mFragment.getAccessibilityServiceInfo()).thenReturn(info); 561 return info; 562 } 563 setupAccessibilityServiceInfoForFragment( boolean isAccessibilityTool, ComponentName tileService)564 private void setupAccessibilityServiceInfoForFragment( 565 boolean isAccessibilityTool, ComponentName tileService) throws Throwable { 566 AccessibilityServiceInfo info = setupServiceWarningRequired(false); 567 info.setAccessibilityTool(isAccessibilityTool); 568 mShadowAccessibilityManager.setInstalledAccessibilityServiceList(List.of(info)); 569 mFragment.mComponentName = info.getComponentName(); 570 when(mFragment.getTileComponentName()).thenReturn(tileService); 571 when(mFragment.getAccessibilityServiceInfo()).thenReturn(info); 572 } 573 574 private static class FakeServiceInfo extends ServiceInfo { 575 private String mTileName; 576 FakeServiceInfo(String packageName, String name, String tileName)577 FakeServiceInfo(String packageName, String name, String tileName) { 578 this.packageName = packageName; 579 this.name = name; 580 mTileName = tileName; 581 } 582 loadLabel(PackageManager mgr)583 public String loadLabel(PackageManager mgr) { 584 return mTileName; 585 } 586 } 587 588 @NonNull getFakeAccessibilityServiceInfo(String packageName, String className)589 private AccessibilityServiceInfo getFakeAccessibilityServiceInfo(String packageName, 590 String className) throws Throwable { 591 final ApplicationInfo applicationInfo = new ApplicationInfo(); 592 final ServiceInfo serviceInfo = new ServiceInfo(); 593 applicationInfo.packageName = packageName; 594 serviceInfo.packageName = packageName; 595 serviceInfo.name = className; 596 serviceInfo.applicationInfo = applicationInfo; 597 final ResolveInfo resolveInfo = new ResolveInfo(); 598 resolveInfo.serviceInfo = serviceInfo; 599 final AccessibilityServiceInfo info = new AccessibilityServiceInfo(resolveInfo, mContext); 600 ComponentName componentName = ComponentName.createRelative(packageName, className); 601 info.setComponentName(componentName); 602 return info; 603 } 604 605 private static class TestToggleAccessibilityServicePreferenceFragment 606 extends ToggleAccessibilityServicePreferenceFragment { 607 int mLastShownDialogId = NO_DIALOG; 608 609 @Override getTileComponentName()610 protected ComponentName getTileComponentName() { 611 return PLACEHOLDER_TILE_COMPONENT_NAME; 612 } 613 614 @Override showDialog(int dialogId)615 protected void showDialog(int dialogId) { 616 mLastShownDialogId = dialogId; 617 } 618 } 619 } 620