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 android.accessibilityservice.AccessibilityServiceInfo;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.ResolveInfo;
26 import android.content.pm.ServiceInfo;
27 import android.os.Build;
28 import android.provider.Settings;
29 
30 import com.android.settings.R;
31 import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
32 
33 import org.junit.Before;
34 import org.junit.Test;
35 import org.junit.runner.RunWith;
36 import org.robolectric.RobolectricTestRunner;
37 import org.robolectric.RuntimeEnvironment;
38 import org.xmlpull.v1.XmlPullParserException;
39 
40 import java.io.IOException;
41 
42 @RunWith(RobolectricTestRunner.class)
43 public final class AccessibilityUtilTest {
44     private static final int ON = 1;
45     private static final int OFF = 0;
46     private static final String SECURE_TEST_KEY = "secure_test_key";
47     private static final String DUMMY_PACKAGE_NAME = "com.dummy.example";
48     private static final String DUMMY_CLASS_NAME = DUMMY_PACKAGE_NAME + ".dummy_a11y_service";
49     private static final String DUMMY_CLASS_NAME2 = DUMMY_PACKAGE_NAME + ".dummy_a11y_service2";
50     private static final ComponentName DUMMY_COMPONENT_NAME = new ComponentName(DUMMY_PACKAGE_NAME,
51             DUMMY_CLASS_NAME);
52     private static final ComponentName DUMMY_COMPONENT_NAME2 = new ComponentName(DUMMY_PACKAGE_NAME,
53             DUMMY_CLASS_NAME2);
54     private static final String SOFTWARE_SHORTCUT_KEY =
55             Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
56     private static final String HARDWARE_SHORTCUT_KEY =
57             Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
58 
59     private Context mContext;
60 
61     @Before
setUp()62     public void setUp() {
63         mContext = RuntimeEnvironment.application;
64     }
65 
66     @Test
capitalize_shouldReturnCapitalizedString()67     public void capitalize_shouldReturnCapitalizedString() {
68         assertThat(AccessibilityUtil.capitalize(null)).isNull();
69         assertThat(AccessibilityUtil.capitalize("")).isEmpty();
70         assertThat(AccessibilityUtil.capitalize("Hans")).isEqualTo("Hans");
71         assertThat(AccessibilityUtil.capitalize("hans")).isEqualTo("Hans");
72         assertThat(AccessibilityUtil.capitalize(",hans")).isEqualTo(",hans");
73         assertThat(AccessibilityUtil.capitalize("Hans, Hans")).isEqualTo("Hans, hans");
74     }
75 
76     @Test
getSummary_hasValueAndEqualsToOne_shouldReturnOnString()77     public void getSummary_hasValueAndEqualsToOne_shouldReturnOnString() {
78         Settings.Secure.putInt(mContext.getContentResolver(), SECURE_TEST_KEY, ON);
79 
80         final CharSequence result = AccessibilityUtil.getSummary(mContext, SECURE_TEST_KEY);
81 
82         assertThat(result)
83                 .isEqualTo(mContext.getText(R.string.accessibility_feature_state_on));
84     }
85 
86     @Test
getSummary_hasValueAndEqualsToZero_shouldReturnOffString()87     public void getSummary_hasValueAndEqualsToZero_shouldReturnOffString() {
88         Settings.Secure.putInt(mContext.getContentResolver(), SECURE_TEST_KEY, OFF);
89 
90         final CharSequence result = AccessibilityUtil.getSummary(mContext, SECURE_TEST_KEY);
91 
92         assertThat(result)
93                 .isEqualTo(mContext.getText(R.string.accessibility_feature_state_off));
94     }
95 
96     @Test
getSummary_noValue_shouldReturnOffString()97     public void getSummary_noValue_shouldReturnOffString() {
98         final CharSequence result = AccessibilityUtil.getSummary(mContext, SECURE_TEST_KEY);
99 
100         assertThat(result)
101                 .isEqualTo(mContext.getText(R.string.accessibility_feature_state_off));
102     }
103 
104     @Test
getAccessibilityServiceFragmentType_targetSdkQ_volumeShortcutType()105     public void getAccessibilityServiceFragmentType_targetSdkQ_volumeShortcutType() {
106         final AccessibilityServiceInfo info = getMockAccessibilityServiceInfo();
107 
108         info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
109         info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
110 
111         assertThat(AccessibilityUtil.getAccessibilityServiceFragmentType(info)).isEqualTo(
112                 AccessibilityUtil.AccessibilityServiceFragmentType.VOLUME_SHORTCUT_TOGGLE);
113     }
114 
115     @Test
getAccessibilityServiceFragmentType_targetSdkR_HaveA11yButton_invisibleType()116     public void getAccessibilityServiceFragmentType_targetSdkR_HaveA11yButton_invisibleType() {
117         final AccessibilityServiceInfo info = getMockAccessibilityServiceInfo();
118 
119         info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
120         info.flags |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
121 
122         assertThat(AccessibilityUtil.getAccessibilityServiceFragmentType(info)).isEqualTo(
123                 AccessibilityUtil.AccessibilityServiceFragmentType.INVISIBLE_TOGGLE);
124     }
125 
126     @Test
getAccessibilityServiceFragmentType_targetSdkR_NoA11yButton_toggleType()127     public void getAccessibilityServiceFragmentType_targetSdkR_NoA11yButton_toggleType() {
128         final AccessibilityServiceInfo info = getMockAccessibilityServiceInfo();
129 
130         info.getResolveInfo().serviceInfo.applicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
131         info.flags |= ~AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
132 
133         assertThat(AccessibilityUtil.getAccessibilityServiceFragmentType(info)).isEqualTo(
134                 AccessibilityUtil.AccessibilityServiceFragmentType.TOGGLE);
135     }
136 
137     @Test
hasValueInSettings_putValue_hasValue()138     public void hasValueInSettings_putValue_hasValue() {
139         putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, DUMMY_COMPONENT_NAME.flattenToString());
140 
141         assertThat(AccessibilityUtil.hasValueInSettings(mContext, UserShortcutType.SOFTWARE,
142                 DUMMY_COMPONENT_NAME)).isTrue();
143     }
144 
145     @Test
getUserShortcutTypeFromSettings_putOneValue_hasValue()146     public void getUserShortcutTypeFromSettings_putOneValue_hasValue() {
147         putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, DUMMY_COMPONENT_NAME.flattenToString());
148 
149         final int shortcutType = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext,
150                 DUMMY_COMPONENT_NAME);
151         assertThat(
152                 (shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE).isTrue();
153     }
154 
155     @Test
getUserShortcutTypeFromSettings_putTwoValues_hasValue()156     public void getUserShortcutTypeFromSettings_putTwoValues_hasValue() {
157         putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, DUMMY_COMPONENT_NAME.flattenToString());
158         putStringIntoSettings(HARDWARE_SHORTCUT_KEY, DUMMY_COMPONENT_NAME.flattenToString());
159 
160         final int shortcutType = AccessibilityUtil.getUserShortcutTypesFromSettings(mContext,
161                 DUMMY_COMPONENT_NAME);
162         assertThat(
163                 (shortcutType & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE).isTrue();
164         assertThat(
165                 (shortcutType & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE).isTrue();
166     }
167 
168     @Test
optInAllValuesToSettings_optInValue_haveMatchString()169     public void optInAllValuesToSettings_optInValue_haveMatchString() {
170         int shortcutTypes = UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE;
171 
172         AccessibilityUtil.optInAllValuesToSettings(mContext, shortcutTypes, DUMMY_COMPONENT_NAME);
173 
174         assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo(
175                 DUMMY_COMPONENT_NAME.flattenToString());
176         assertThat(getStringFromSettings(HARDWARE_SHORTCUT_KEY)).isEqualTo(
177                 DUMMY_COMPONENT_NAME.flattenToString());
178 
179     }
180 
181     @Test
optInValueToSettings_optInValue_haveMatchString()182     public void optInValueToSettings_optInValue_haveMatchString() {
183         putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, DUMMY_COMPONENT_NAME.flattenToString());
184         AccessibilityUtil.optInValueToSettings(mContext, UserShortcutType.SOFTWARE,
185                 DUMMY_COMPONENT_NAME2);
186 
187         assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo(
188                 DUMMY_COMPONENT_NAME.flattenToString() + ":"
189                         + DUMMY_COMPONENT_NAME2.flattenToString());
190     }
191 
192     @Test
optInValueToSettings_optInTwoValues_haveMatchString()193     public void optInValueToSettings_optInTwoValues_haveMatchString() {
194         putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, DUMMY_COMPONENT_NAME.flattenToString());
195         AccessibilityUtil.optInValueToSettings(mContext, UserShortcutType.SOFTWARE,
196                 DUMMY_COMPONENT_NAME2);
197         AccessibilityUtil.optInValueToSettings(mContext, UserShortcutType.SOFTWARE,
198                 DUMMY_COMPONENT_NAME2);
199 
200         assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo(
201                 DUMMY_COMPONENT_NAME.flattenToString() + ":"
202                         + DUMMY_COMPONENT_NAME2.flattenToString());
203     }
204 
205     @Test
optOutAllValuesToSettings_optOutValue_emptyString()206     public void optOutAllValuesToSettings_optOutValue_emptyString() {
207         putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, DUMMY_COMPONENT_NAME.flattenToString());
208         putStringIntoSettings(HARDWARE_SHORTCUT_KEY, DUMMY_COMPONENT_NAME.flattenToString());
209         int shortcutTypes =
210                 UserShortcutType.SOFTWARE | UserShortcutType.HARDWARE | UserShortcutType.TRIPLETAP;
211 
212         AccessibilityUtil.optOutAllValuesFromSettings(mContext, shortcutTypes,
213                 DUMMY_COMPONENT_NAME);
214 
215         assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEmpty();
216         assertThat(getStringFromSettings(HARDWARE_SHORTCUT_KEY)).isEmpty();
217     }
218 
219     @Test
optOutValueFromSettings_optOutValue_emptyString()220     public void optOutValueFromSettings_optOutValue_emptyString() {
221         putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, DUMMY_COMPONENT_NAME.flattenToString());
222         AccessibilityUtil.optOutValueFromSettings(mContext, UserShortcutType.SOFTWARE,
223                 DUMMY_COMPONENT_NAME);
224 
225         assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEmpty();
226     }
227 
228     @Test
optOutValueFromSettings_optOutValue_haveMatchString()229     public void optOutValueFromSettings_optOutValue_haveMatchString() {
230         putStringIntoSettings(SOFTWARE_SHORTCUT_KEY, DUMMY_COMPONENT_NAME.flattenToString() + ":"
231                 + DUMMY_COMPONENT_NAME2.flattenToString());
232         AccessibilityUtil.optOutValueFromSettings(mContext, UserShortcutType.SOFTWARE,
233                 DUMMY_COMPONENT_NAME2);
234 
235         assertThat(getStringFromSettings(SOFTWARE_SHORTCUT_KEY)).isEqualTo(
236                 DUMMY_COMPONENT_NAME.flattenToString());
237     }
238 
getMockAccessibilityServiceInfo()239     private AccessibilityServiceInfo getMockAccessibilityServiceInfo() {
240         final ApplicationInfo applicationInfo = new ApplicationInfo();
241         final ServiceInfo serviceInfo = new ServiceInfo();
242         applicationInfo.packageName = DUMMY_PACKAGE_NAME;
243         serviceInfo.packageName = DUMMY_PACKAGE_NAME;
244         serviceInfo.name = DUMMY_CLASS_NAME;
245         serviceInfo.applicationInfo = applicationInfo;
246 
247         final ResolveInfo resolveInfo = new ResolveInfo();
248         resolveInfo.serviceInfo = serviceInfo;
249 
250         try {
251             final AccessibilityServiceInfo info = new AccessibilityServiceInfo(resolveInfo,
252                     mContext);
253             info.setComponentName(DUMMY_COMPONENT_NAME);
254             return info;
255         } catch (XmlPullParserException | IOException e) {
256             // Do nothing
257         }
258 
259         return null;
260     }
261 
putStringIntoSettings(String key, String componentName)262     private void putStringIntoSettings(String key, String componentName) {
263         Settings.Secure.putString(mContext.getContentResolver(), key, componentName);
264     }
265 
getStringFromSettings(String key)266     private String getStringFromSettings(String key) {
267         return Settings.Secure.getString(mContext.getContentResolver(), key);
268     }
269 }
270