1 package org.robolectric.shadows; 2 3 import static android.content.Context.ACCESSIBILITY_SERVICE; 4 import static android.os.Build.VERSION_CODES.O; 5 import static android.os.Build.VERSION_CODES.O_MR1; 6 import static android.os.Build.VERSION_CODES.Q; 7 import static com.google.common.truth.Truth.assertThat; 8 import static org.robolectric.Shadows.shadowOf; 9 10 import android.accessibilityservice.AccessibilityServiceInfo; 11 import android.app.Activity; 12 import android.content.Context; 13 import android.content.pm.ServiceInfo; 14 import android.view.accessibility.AccessibilityEvent; 15 import android.view.accessibility.AccessibilityManager; 16 import androidx.test.core.app.ApplicationProvider; 17 import androidx.test.ext.junit.runners.AndroidJUnit4; 18 import java.util.ArrayList; 19 import java.util.Collections; 20 import java.util.List; 21 import java.util.concurrent.atomic.AtomicBoolean; 22 import org.junit.Before; 23 import org.junit.Test; 24 import org.junit.runner.RunWith; 25 import org.robolectric.Robolectric; 26 import org.robolectric.annotation.Config; 27 import org.robolectric.util.ReflectionHelpers; 28 29 @RunWith(AndroidJUnit4.class) 30 public class ShadowAccessibilityManagerTest { 31 32 private AccessibilityManager accessibilityManager; 33 34 @Before setUp()35 public void setUp() throws Exception { 36 accessibilityManager = 37 (AccessibilityManager) 38 ApplicationProvider.getApplicationContext().getSystemService(ACCESSIBILITY_SERVICE); 39 } 40 41 @Test shouldReturnTrueWhenEnabled()42 public void shouldReturnTrueWhenEnabled() { 43 shadowOf(accessibilityManager).setEnabled(true); 44 assertThat(accessibilityManager.isEnabled()).isTrue(); 45 assertThat(getAccessibilityManagerInstance().isEnabled()).isTrue(); 46 } 47 48 // Emulates Android framework behavior, e.g., 49 // AccessibilityManager.getInstance(context).isEnabled(). getAccessibilityManagerInstance()50 private static AccessibilityManager getAccessibilityManagerInstance() { 51 return ReflectionHelpers.callStaticMethod( 52 AccessibilityManager.class, 53 "getInstance", 54 ReflectionHelpers.ClassParameter.from( 55 Context.class, ApplicationProvider.getApplicationContext())); 56 } 57 58 @Test shouldReturnTrueForTouchExplorationWhenEnabled()59 public void shouldReturnTrueForTouchExplorationWhenEnabled() { 60 shadowOf(accessibilityManager).setTouchExplorationEnabled(true); 61 assertThat(accessibilityManager.isTouchExplorationEnabled()).isTrue(); 62 } 63 64 @Test shouldReturnExpectedEnabledServiceList()65 public void shouldReturnExpectedEnabledServiceList() { 66 List<AccessibilityServiceInfo> expected = 67 new ArrayList<>(Collections.singletonList(new AccessibilityServiceInfo())); 68 shadowOf(accessibilityManager).setEnabledAccessibilityServiceList(expected); 69 assertThat(accessibilityManager.getEnabledAccessibilityServiceList(0)).isEqualTo(expected); 70 } 71 72 @Test shouldReturnExpectedInstalledServiceList()73 public void shouldReturnExpectedInstalledServiceList() { 74 List<AccessibilityServiceInfo> expected = 75 new ArrayList<>(Collections.singletonList(new AccessibilityServiceInfo())); 76 shadowOf(accessibilityManager).setInstalledAccessibilityServiceList(expected); 77 assertThat(accessibilityManager.getInstalledAccessibilityServiceList()).isEqualTo(expected); 78 } 79 80 @Test shouldReturnExpectedAccessibilityServiceList()81 public void shouldReturnExpectedAccessibilityServiceList() { 82 List<ServiceInfo> expected = new ArrayList<>(Collections.singletonList(new ServiceInfo())); 83 shadowOf(accessibilityManager).setAccessibilityServiceList(expected); 84 assertThat(accessibilityManager.getAccessibilityServiceList()).isEqualTo(expected); 85 } 86 87 @Test 88 @Config(minSdk = O_MR1) isAccessibilityButtonSupported()89 public void isAccessibilityButtonSupported() { 90 assertThat(AccessibilityManager.isAccessibilityButtonSupported()).isTrue(); 91 92 ShadowAccessibilityManager.setAccessibilityButtonSupported(false); 93 assertThat(AccessibilityManager.isAccessibilityButtonSupported()).isFalse(); 94 95 ShadowAccessibilityManager.setAccessibilityButtonSupported(true); 96 assertThat(AccessibilityManager.isAccessibilityButtonSupported()).isTrue(); 97 } 98 99 @Test 100 @Config(minSdk = O) performAccessibilityShortcut_shouldEnableAccessibilityAndTouchExploration()101 public void performAccessibilityShortcut_shouldEnableAccessibilityAndTouchExploration() { 102 accessibilityManager.performAccessibilityShortcut(); 103 104 assertThat(accessibilityManager.isEnabled()).isTrue(); 105 assertThat(accessibilityManager.isTouchExplorationEnabled()).isTrue(); 106 } 107 108 @Test getSentAccessibilityEvents_returnsEmptyInitially()109 public void getSentAccessibilityEvents_returnsEmptyInitially() { 110 assertThat(shadowOf(accessibilityManager).getSentAccessibilityEvents()).isEmpty(); 111 } 112 113 @Test getSentAccessibilityEvents_returnsAllSentAccessibilityEventsInOrder()114 public void getSentAccessibilityEvents_returnsAllSentAccessibilityEventsInOrder() { 115 AccessibilityEvent event1 = AccessibilityEvent.obtain(); 116 event1.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED); 117 118 AccessibilityEvent event2 = AccessibilityEvent.obtain(); 119 event2.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED); 120 121 AccessibilityEvent event3 = AccessibilityEvent.obtain(); 122 event3.setEventType(AccessibilityEvent.TYPE_ANNOUNCEMENT); 123 124 shadowOf(accessibilityManager).setEnabled(true); 125 accessibilityManager.sendAccessibilityEvent(event1); 126 accessibilityManager.sendAccessibilityEvent(event2); 127 accessibilityManager.sendAccessibilityEvent(event3); 128 129 assertThat(shadowOf(accessibilityManager).getSentAccessibilityEvents()) 130 .containsExactly(event1, event2, event3) 131 .inOrder(); 132 } 133 134 @Test addAccessibilityStateChangeListener_shouldAddListener()135 public void addAccessibilityStateChangeListener_shouldAddListener() { 136 TestAccessibilityStateChangeListener listener1 = new TestAccessibilityStateChangeListener(); 137 TestAccessibilityStateChangeListener listener2 = new TestAccessibilityStateChangeListener(); 138 139 accessibilityManager.addAccessibilityStateChangeListener(listener1); 140 accessibilityManager.addAccessibilityStateChangeListener(listener2); 141 142 shadowOf(accessibilityManager).setEnabled(true); 143 144 assertThat(listener1.isEnabled()).isTrue(); 145 assertThat(listener2.isEnabled()).isTrue(); 146 } 147 148 @Test removeAccessibilityStateChangeListener_shouldRemoveListeners()149 public void removeAccessibilityStateChangeListener_shouldRemoveListeners() { 150 // Add two different callbacks. 151 TestAccessibilityStateChangeListener listener1 = new TestAccessibilityStateChangeListener(); 152 TestAccessibilityStateChangeListener listener2 = new TestAccessibilityStateChangeListener(); 153 accessibilityManager.addAccessibilityStateChangeListener(listener1); 154 accessibilityManager.addAccessibilityStateChangeListener(listener2); 155 156 shadowOf(accessibilityManager).setEnabled(true); 157 158 assertThat(listener1.isEnabled()).isTrue(); 159 assertThat(listener2.isEnabled()).isTrue(); 160 // Remove one at the time. 161 accessibilityManager.removeAccessibilityStateChangeListener(listener2); 162 163 shadowOf(accessibilityManager).setEnabled(false); 164 165 assertThat(listener1.isEnabled()).isFalse(); 166 assertThat(listener2.isEnabled()).isTrue(); 167 168 accessibilityManager.removeAccessibilityStateChangeListener(listener1); 169 170 shadowOf(accessibilityManager).setEnabled(true); 171 172 assertThat(listener1.isEnabled()).isFalse(); 173 assertThat(listener2.isEnabled()).isTrue(); 174 } 175 176 @Test removeAccessibilityStateChangeListener_returnsTrueIfRemoved()177 public void removeAccessibilityStateChangeListener_returnsTrueIfRemoved() { 178 TestAccessibilityStateChangeListener listener = new TestAccessibilityStateChangeListener(); 179 accessibilityManager.addAccessibilityStateChangeListener(listener); 180 181 assertThat(accessibilityManager.removeAccessibilityStateChangeListener(listener)).isTrue(); 182 } 183 184 @Test removeAccessibilityStateChangeListener_returnsFalseIfNotRegistered()185 public void removeAccessibilityStateChangeListener_returnsFalseIfNotRegistered() { 186 assertThat( 187 accessibilityManager.removeAccessibilityStateChangeListener( 188 new TestAccessibilityStateChangeListener())) 189 .isFalse(); 190 assertThat(accessibilityManager.removeAccessibilityStateChangeListener(null)).isFalse(); 191 } 192 193 @Test setTouchExplorationEnabled_invokesCallbacks()194 public void setTouchExplorationEnabled_invokesCallbacks() { 195 AtomicBoolean enabled = new AtomicBoolean(false); 196 accessibilityManager.addTouchExplorationStateChangeListener(val -> enabled.set(val)); 197 shadowOf(accessibilityManager).setTouchExplorationEnabled(true); 198 assertThat(enabled.get()).isEqualTo(true); 199 shadowOf(accessibilityManager).setTouchExplorationEnabled(false); 200 assertThat(enabled.get()).isEqualTo(false); 201 } 202 203 @Test 204 @Config(minSdk = Q) getRecommendedTimeoutMillis_default()205 public void getRecommendedTimeoutMillis_default() { 206 int flags = 207 AccessibilityManager.FLAG_CONTENT_ICONS 208 | AccessibilityManager.FLAG_CONTENT_TEXT 209 | AccessibilityManager.FLAG_CONTENT_CONTROLS; 210 211 assertThat(accessibilityManager.getRecommendedTimeoutMillis(1, flags)).isEqualTo(1); 212 } 213 214 @Test 215 @Config(minSdk = Q) getRecommendedTimeoutMillis_interactive()216 public void getRecommendedTimeoutMillis_interactive() { 217 int flags = 218 AccessibilityManager.FLAG_CONTENT_ICONS 219 | AccessibilityManager.FLAG_CONTENT_TEXT 220 | AccessibilityManager.FLAG_CONTENT_CONTROLS; 221 shadowOf(accessibilityManager).setNonInteractiveUiTimeout(2); 222 shadowOf(accessibilityManager).setInteractiveUiTimeout(3); 223 224 assertThat(accessibilityManager.getRecommendedTimeoutMillis(1, flags)).isEqualTo(3); 225 } 226 227 @Test 228 @Config(minSdk = Q) getRecommendedTimeoutMillis_nonInteractive()229 public void getRecommendedTimeoutMillis_nonInteractive() { 230 int flags = AccessibilityManager.FLAG_CONTENT_ICONS | AccessibilityManager.FLAG_CONTENT_TEXT; 231 shadowOf(accessibilityManager).setNonInteractiveUiTimeout(2); 232 shadowOf(accessibilityManager).setInteractiveUiTimeout(3); 233 234 assertThat(accessibilityManager.getRecommendedTimeoutMillis(1, flags)).isEqualTo(2); 235 } 236 237 @Test 238 @Config(minSdk = Q) getRecommendedTimeoutMillis_empty()239 public void getRecommendedTimeoutMillis_empty() { 240 int flags = 0; 241 shadowOf(accessibilityManager).setNonInteractiveUiTimeout(2); 242 shadowOf(accessibilityManager).setInteractiveUiTimeout(3); 243 244 assertThat(accessibilityManager.getRecommendedTimeoutMillis(1, flags)).isEqualTo(1); 245 } 246 247 private static class TestAccessibilityStateChangeListener 248 implements AccessibilityManager.AccessibilityStateChangeListener { 249 250 private boolean enabled = false; 251 252 @Override onAccessibilityStateChanged(boolean enabled)253 public void onAccessibilityStateChanged(boolean enabled) { 254 this.enabled = enabled; 255 } 256 isEnabled()257 public boolean isEnabled() { 258 return enabled; 259 } 260 } 261 262 @Test getAccessibilityServiceList_doesNotNPE()263 public void getAccessibilityServiceList_doesNotNPE() { 264 assertThat(accessibilityManager.getAccessibilityServiceList()).isEmpty(); 265 assertThat(accessibilityManager.getInstalledAccessibilityServiceList()).isEmpty(); 266 assertThat( 267 accessibilityManager.getEnabledAccessibilityServiceList( 268 AccessibilityServiceInfo.FEEDBACK_SPOKEN)) 269 .isEmpty(); 270 } 271 272 @Test 273 @Config(minSdk = O) accessibilityManager_activityContextEnabled_differentInstancesHaveSameServices()274 public void accessibilityManager_activityContextEnabled_differentInstancesHaveSameServices() { 275 String originalProperty = System.getProperty("robolectric.createActivityContexts", ""); 276 System.setProperty("robolectric.createActivityContexts", "true"); 277 Activity activity = null; 278 try { 279 AccessibilityManager applicationAccessibilityManager = 280 (AccessibilityManager) 281 ApplicationProvider.getApplicationContext() 282 .getSystemService(Context.ACCESSIBILITY_SERVICE); 283 activity = Robolectric.setupActivity(Activity.class); 284 AccessibilityManager activityAccessibilityManager = 285 (AccessibilityManager) activity.getSystemService(Context.ACCESSIBILITY_SERVICE); 286 287 assertThat(applicationAccessibilityManager).isSameInstanceAs(activityAccessibilityManager); 288 289 List<AccessibilityServiceInfo> applicationServices = 290 applicationAccessibilityManager.getInstalledAccessibilityServiceList(); 291 List<AccessibilityServiceInfo> activityServices = 292 activityAccessibilityManager.getInstalledAccessibilityServiceList(); 293 294 assertThat(activityServices).isEqualTo(applicationServices); 295 } finally { 296 if (activity != null) { 297 activity.finish(); 298 } 299 System.setProperty("robolectric.createActivityContexts", originalProperty); 300 } 301 } 302 } 303