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