AccessibilityShortcutControllerTest.java revision 32ea37255dbb9c5d7d40b73e435d12df934a803a
1/* 2 * Copyright (C) 2016 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 17package com.android.server.policy; 18 19import android.accessibilityservice.AccessibilityServiceInfo; 20import android.app.AlertDialog; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.DialogInterface; 24import android.content.pm.ApplicationInfo; 25import android.content.pm.ResolveInfo; 26import android.content.res.Resources; 27import android.os.Handler; 28import android.os.Vibrator; 29import android.provider.Settings; 30import android.support.test.runner.AndroidJUnit4; 31 32import android.test.mock.MockContentResolver; 33import android.text.TextUtils; 34import android.view.Window; 35import android.view.WindowManager; 36import android.view.accessibility.AccessibilityManager; 37import android.view.accessibility.IAccessibilityManager; 38import android.widget.Toast; 39import com.android.internal.R; 40import com.android.internal.util.test.FakeSettingsProvider; 41import com.android.server.policy.AccessibilityShortcutController.FrameworkObjectProvider; 42 43import org.junit.After; 44import org.junit.Before; 45import org.junit.Test; 46import org.junit.runner.RunWith; 47import org.mockito.ArgumentCaptor; 48import org.mockito.Mock; 49import org.mockito.MockitoAnnotations; 50import org.mockito.internal.util.reflection.Whitebox; 51 52import java.util.Collections; 53 54import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN; 55import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; 56import static junit.framework.Assert.assertEquals; 57import static junit.framework.Assert.assertFalse; 58import static junit.framework.Assert.assertTrue; 59import static org.mockito.AdditionalMatchers.aryEq; 60import static org.mockito.Matchers.anyBoolean; 61import static org.mockito.Matchers.anyInt; 62import static org.mockito.Matchers.anyObject; 63import static org.mockito.Matchers.eq; 64import static org.mockito.Mockito.mock; 65import static org.mockito.Mockito.times; 66import static org.mockito.Mockito.verify; 67import static org.mockito.Mockito.verifyZeroInteractions; 68import static org.mockito.Mockito.when; 69 70@RunWith(AndroidJUnit4.class) 71public class AccessibilityShortcutControllerTest { 72 private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name"; 73 private static final long VIBRATOR_PATTERN_1 = 100L; 74 private static final long VIBRATOR_PATTERN_2 = 150L; 75 private static final int[] VIBRATOR_PATTERN_INT = {(int) VIBRATOR_PATTERN_1, 76 (int) VIBRATOR_PATTERN_2}; 77 private static final long[] VIBRATOR_PATTERN_LONG = {VIBRATOR_PATTERN_1, VIBRATOR_PATTERN_2}; 78 79 private @Mock Context mContext; 80 private @Mock FrameworkObjectProvider mFrameworkObjectProvider; 81 private @Mock IAccessibilityManager mAccessibilityManagerService; 82 private @Mock Handler mHandler; 83 private @Mock AlertDialog.Builder mAlertDialogBuilder; 84 private @Mock AlertDialog mAlertDialog; 85 private @Mock AccessibilityServiceInfo mServiceInfo; 86 private @Mock Resources mResources; 87 private @Mock Toast mToast; 88 private @Mock Vibrator mVibrator; 89 private @Mock ApplicationInfo mApplicationInfo; 90 91 private MockContentResolver mContentResolver; 92 private WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams(); 93 94 @Before 95 public void setUp() throws Exception { 96 MockitoAnnotations.initMocks(this); 97 98 when(mVibrator.hasVibrator()).thenReturn(true); 99 100 when(mContext.getResources()).thenReturn(mResources); 101 when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); 102 when(mContext.getSystemService(Context.VIBRATOR_SERVICE)).thenReturn(mVibrator); 103 104 mContentResolver = new MockContentResolver(mContext); 105 mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); 106 when(mContext.getContentResolver()).thenReturn(mContentResolver); 107 108 when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(anyInt())) 109 .thenReturn(Collections.singletonList(mServiceInfo)); 110 111 // Use the extra level of indirection in the object to mock framework objects 112 AccessibilityManager accessibilityManager = 113 new AccessibilityManager(mHandler, mAccessibilityManagerService, 0); 114 when(mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext)) 115 .thenReturn(accessibilityManager); 116 when(mFrameworkObjectProvider.getAlertDialogBuilder(mContext)) 117 .thenReturn(mAlertDialogBuilder); 118 when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), anyObject(), anyInt())) 119 .thenReturn(mToast); 120 121 when(mResources.getString(anyInt())).thenReturn("Howdy %s"); 122 when(mResources.getIntArray(anyInt())).thenReturn(VIBRATOR_PATTERN_INT); 123 124 ResolveInfo resolveInfo = mock(ResolveInfo.class); 125 when(resolveInfo.loadLabel(anyObject())).thenReturn("Service name"); 126 when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo); 127 when(mServiceInfo.getComponentName()) 128 .thenReturn(ComponentName.unflattenFromString(SERVICE_NAME_STRING)); 129 130 when(mAlertDialogBuilder.setTitle(anyInt())).thenReturn(mAlertDialogBuilder); 131 when(mAlertDialogBuilder.setCancelable(anyBoolean())).thenReturn(mAlertDialogBuilder); 132 when(mAlertDialogBuilder.setMessage(anyObject())).thenReturn(mAlertDialogBuilder); 133 when(mAlertDialogBuilder.setPositiveButton(anyInt(), anyObject())) 134 .thenReturn(mAlertDialogBuilder); 135 when(mAlertDialogBuilder.setNegativeButton(anyInt(), anyObject())) 136 .thenReturn(mAlertDialogBuilder); 137 when(mAlertDialogBuilder.setOnCancelListener(anyObject())).thenReturn(mAlertDialogBuilder); 138 when(mAlertDialogBuilder.create()).thenReturn(mAlertDialog); 139 140 mLayoutParams.privateFlags = 0; 141 when(mToast.getWindowParams()).thenReturn(mLayoutParams); 142 143 Window window = mock(Window.class); 144 Whitebox.setInternalState(window, "mWindowAttributes", new WindowManager.LayoutParams()); 145 when(mAlertDialog.getWindow()).thenReturn(window); 146 } 147 148 @After 149 public void tearDown() { 150 } 151 152 @Test 153 public void testShortcutAvailable_withNullServiceIdWhenCreated_shouldReturnFalse() { 154 configureShortcutDisabled(); 155 assertFalse(getController().isAccessibilityShortcutAvailable()); 156 } 157 158 @Test 159 public void testShortcutAvailable_withNonNullServiceIdWhenCreated_shouldReturnTrue() { 160 configureShortcutEnabled(); 161 assertTrue(getController().isAccessibilityShortcutAvailable()); 162 } 163 164 @Test 165 public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse() { 166 configureShortcutEnabled(); 167 AccessibilityShortcutController accessibilityShortcutController = getController(); 168 Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, ""); 169 accessibilityShortcutController.onSettingsChanged(); 170 assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable()); 171 } 172 173 @Test 174 public void testShortcutAvailable_whenServiceIdBecomesNonNull_shouldReturnTrue() { 175 configureShortcutDisabled(); 176 AccessibilityShortcutController accessibilityShortcutController = getController(); 177 configureShortcutEnabled(); 178 accessibilityShortcutController.onSettingsChanged(); 179 assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable()); 180 } 181 182 @Test 183 public void testOnAccessibilityShortcut_vibrates() { 184 configureShortcutEnabled(); 185 AccessibilityShortcutController accessibilityShortcutController = getController(); 186 accessibilityShortcutController.performAccessibilityShortcut(); 187 verify(mVibrator).vibrate(aryEq(VIBRATOR_PATTERN_LONG), eq(-1), anyObject()); 188 } 189 190 @Test 191 public void testOnAccessibilityShortcut_firstTime_showsWarningDialog() 192 throws Exception { 193 configureShortcutEnabled(); 194 AccessibilityShortcutController accessibilityShortcutController = getController(); 195 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 196 accessibilityShortcutController.performAccessibilityShortcut(); 197 198 assertEquals(1, Settings.Secure.getInt( 199 mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0)); 200 verify(mResources).getString(R.string.accessibility_shortcut_toogle_warning); 201 verify(mAlertDialog).show(); 202 verify(mAccessibilityManagerService).getInstalledAccessibilityServiceList(anyInt()); 203 verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(); 204 } 205 206 @Test 207 public void testOnAccessibilityShortcut_withDialogShowing_callsServer() 208 throws Exception { 209 configureShortcutEnabled(); 210 AccessibilityShortcutController accessibilityShortcutController = getController(); 211 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 212 accessibilityShortcutController.performAccessibilityShortcut(); 213 accessibilityShortcutController.performAccessibilityShortcut(); 214 verify(mToast).show(); 215 assertEquals(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS, 216 mLayoutParams.privateFlags 217 & WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS); 218 verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(); 219 } 220 221 @Test 222 public void testOnAccessibilityShortcut_ifCanceledFirstTime_showsWarningDialog() 223 throws Exception { 224 configureShortcutEnabled(); 225 AccessibilityShortcutController accessibilityShortcutController = getController(); 226 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 227 accessibilityShortcutController.performAccessibilityShortcut(); 228 ArgumentCaptor<AlertDialog.OnCancelListener> cancelListenerCaptor = 229 ArgumentCaptor.forClass(AlertDialog.OnCancelListener.class); 230 verify(mAlertDialogBuilder).setOnCancelListener(cancelListenerCaptor.capture()); 231 // Call the cancel callback 232 cancelListenerCaptor.getValue().onCancel(null); 233 234 accessibilityShortcutController.performAccessibilityShortcut(); 235 verify(mAlertDialog, times(2)).show(); 236 } 237 238 @Test 239 public void testClickingDisableButtonInDialog_shouldClearShortcutId() { 240 configureShortcutEnabled(); 241 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 242 getController().performAccessibilityShortcut(); 243 244 ArgumentCaptor<DialogInterface.OnClickListener> captor = 245 ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); 246 verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.disable_accessibility_shortcut), 247 captor.capture()); 248 // Call the button callback 249 captor.getValue().onClick(null, 0); 250 assertTrue(TextUtils.isEmpty( 251 Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))); 252 } 253 254 @Test 255 public void testClickingLeaveOnButtonInDialog_shouldLeaveShortcutReady() throws Exception { 256 configureShortcutEnabled(); 257 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 258 getController().performAccessibilityShortcut(); 259 260 ArgumentCaptor<DialogInterface.OnClickListener> captor = 261 ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); 262 verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.leave_accessibility_shortcut_on), 263 captor.capture()); 264 // Call the button callback, if one exists 265 if (captor.getValue() != null) { 266 captor.getValue().onClick(null, 0); 267 } 268 assertEquals(SERVICE_NAME_STRING, 269 Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)); 270 assertEquals(1, Settings.Secure.getInt( 271 mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)); 272 } 273 274 @Test 275 public void testOnAccessibilityShortcut_afterDialogShown_shouldCallServer() throws Exception { 276 configureShortcutEnabled(); 277 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); 278 getController().performAccessibilityShortcut(); 279 280 verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog); 281 verify(mToast).show(); 282 verify(mAccessibilityManagerService).performAccessibilityShortcut(); 283 } 284 285 private void configureShortcutDisabled() { 286 Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, ""); 287 } 288 289 private void configureShortcutEnabled() { 290 Settings.Secure.putString( 291 mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING); 292 } 293 294 private AccessibilityShortcutController getController() { 295 AccessibilityShortcutController accessibilityShortcutController = 296 new AccessibilityShortcutController(mContext, mHandler); 297 accessibilityShortcutController.mFrameworkObjectProvider = mFrameworkObjectProvider; 298 return accessibilityShortcutController; 299 } 300} 301