• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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