AccessibilityShortcutControllerTest.java revision 7192297bc9669f3705d2a18893889e9a113a342a
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; 48import org.mockito.internal.util.reflection.Whitebox; 49 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 Whitebox.setInternalState(window, "mWindowAttributes", new WindowManager.LayoutParams()); 128 when(mAlertDialog.getWindow()).thenReturn(window); 129 } 130 131 @After 132 public void tearDown() { 133 } 134 135 @Test 136 public void testShortcutAvailable_withNullServiceIdWhenCreated_shouldReturnFalse() { 137 configureShortcutDisabled(); 138 assertFalse(getController().isAccessibilityShortcutAvailable()); 139 } 140 141 @Test 142 public void testShortcutAvailable_withNonNullServiceIdWhenCreated_shouldReturnTrue() { 143 configureShortcutEnabled(); 144 assertTrue(getController().isAccessibilityShortcutAvailable()); 145 } 146 147 @Test 148 public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse() { 149 configureShortcutEnabled(); 150 AccessibilityShortcutController accessibilityShortcutController = getController(); 151 Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, ""); 152 accessibilityShortcutController.onSettingsChanged(); 153 assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable()); 154 } 155 156 @Test 157 public void testShortcutAvailable_whenServiceIdBecomesNonNull_shouldReturnTrue() { 158 configureShortcutDisabled(); 159 AccessibilityShortcutController accessibilityShortcutController = getController(); 160 configureShortcutEnabled(); 161 accessibilityShortcutController.onSettingsChanged(); 162 assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable()); 163 } 164 165 @Test 166 public void testOnAccessibilityShortcut_firstTime_showsWarningDialog() 167 throws Exception { 168 configureShortcutEnabled(); 169 AccessibilityShortcutController accessibilityShortcutController = getController(); 170 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 171 accessibilityShortcutController.performAccessibilityShortcut(); 172 173 assertEquals(1, Settings.Secure.getInt( 174 mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0)); 175 verify(mResources).getString(R.string.accessibility_shortcut_toogle_warning); 176 verify(mAlertDialog).show(); 177 verify(mAccessibilityManagerService).getInstalledAccessibilityServiceList(anyInt()); 178 verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(); 179 } 180 181 @Test 182 public void testOnAccessibilityShortcut_withDialogShowing_callsServer() 183 throws Exception { 184 configureShortcutEnabled(); 185 AccessibilityShortcutController accessibilityShortcutController = getController(); 186 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 187 accessibilityShortcutController.performAccessibilityShortcut(); 188 accessibilityShortcutController.performAccessibilityShortcut(); 189 verify(mToast).show(); 190 assertEquals(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS, 191 mLayoutParams.privateFlags 192 & WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS); 193 verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(); 194 } 195 196 @Test 197 public void testOnAccessibilityShortcut_ifCanceledFirstTime_showsWarningDialog() 198 throws Exception { 199 configureShortcutEnabled(); 200 AccessibilityShortcutController accessibilityShortcutController = getController(); 201 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 202 accessibilityShortcutController.performAccessibilityShortcut(); 203 ArgumentCaptor<AlertDialog.OnCancelListener> cancelListenerCaptor = 204 ArgumentCaptor.forClass(AlertDialog.OnCancelListener.class); 205 verify(mAlertDialogBuilder).setOnCancelListener(cancelListenerCaptor.capture()); 206 // Call the cancel callback 207 cancelListenerCaptor.getValue().onCancel(null); 208 209 accessibilityShortcutController.performAccessibilityShortcut(); 210 verify(mAlertDialog, times(2)).show(); 211 } 212 213 @Test 214 public void testClickingDisableButtonInDialog_shouldClearShortcutId() { 215 configureShortcutEnabled(); 216 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 217 getController().performAccessibilityShortcut(); 218 219 ArgumentCaptor<DialogInterface.OnClickListener> captor = 220 ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); 221 verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.disable_accessibility_shortcut), 222 captor.capture()); 223 // Call the button callback 224 captor.getValue().onClick(null, 0); 225 assertTrue(TextUtils.isEmpty( 226 Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))); 227 } 228 229 @Test 230 public void testClickingLeaveOnButtonInDialog_shouldLeaveShortcutReady() throws Exception { 231 configureShortcutEnabled(); 232 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 233 getController().performAccessibilityShortcut(); 234 235 ArgumentCaptor<DialogInterface.OnClickListener> captor = 236 ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); 237 verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.leave_accessibility_shortcut_on), 238 captor.capture()); 239 // Call the button callback, if one exists 240 if (captor.getValue() != null) { 241 captor.getValue().onClick(null, 0); 242 } 243 assertEquals(SERVICE_NAME_STRING, 244 Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)); 245 assertEquals(1, Settings.Secure.getInt( 246 mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)); 247 } 248 249 @Test 250 public void testOnAccessibilityShortcut_afterDialogShown_shouldCallServer() throws Exception { 251 configureShortcutEnabled(); 252 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); 253 getController().performAccessibilityShortcut(); 254 255 verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog); 256 verify(mToast).show(); 257 verify(mAccessibilityManagerService).performAccessibilityShortcut(); 258 } 259 260 private void configureShortcutDisabled() { 261 Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, ""); 262 } 263 264 private void configureShortcutEnabled() { 265 Settings.Secure.putString( 266 mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING); 267 } 268 269 private AccessibilityShortcutController getController() { 270 AccessibilityShortcutController accessibilityShortcutController = 271 new AccessibilityShortcutController(mContext, mHandler); 272 accessibilityShortcutController.mFrameworkObjectProvider = mFrameworkObjectProvider; 273 return accessibilityShortcutController; 274 } 275} 276