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