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 17 package com.android.internal.accessibility; 18 19 import android.accessibilityservice.AccessibilityServiceInfo; 20 import android.app.AlertDialog; 21 import android.content.ComponentName; 22 import android.content.Context; 23 import android.content.DialogInterface; 24 import android.content.pm.ApplicationInfo; 25 import android.content.pm.PackageManager; 26 import android.content.pm.ResolveInfo; 27 import android.content.res.Resources; 28 import android.os.Handler; 29 import android.os.Vibrator; 30 import android.provider.Settings; 31 import android.support.test.runner.AndroidJUnit4; 32 33 import android.test.mock.MockContentResolver; 34 import android.text.TextUtils; 35 import android.view.Window; 36 import android.view.WindowManager; 37 import android.view.accessibility.AccessibilityManager; 38 import android.view.accessibility.IAccessibilityManager; 39 import android.widget.Toast; 40 import com.android.internal.R; 41 import com.android.internal.util.test.FakeSettingsProvider; 42 import com.android.internal.accessibility.AccessibilityShortcutController.FrameworkObjectProvider; 43 44 import org.junit.After; 45 import org.junit.Before; 46 import org.junit.Test; 47 import org.junit.runner.RunWith; 48 import org.mockito.ArgumentCaptor; 49 import org.mockito.Mock; 50 import org.mockito.MockitoAnnotations; 51 52 import java.lang.reflect.Field; 53 import java.util.Collections; 54 import java.util.Map; 55 56 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN; 57 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED; 58 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN; 59 import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE; 60 import static junit.framework.Assert.assertEquals; 61 import static junit.framework.Assert.assertFalse; 62 import static junit.framework.Assert.assertTrue; 63 64 import static org.junit.Assert.fail; 65 import static org.mockito.AdditionalMatchers.aryEq; 66 import static org.mockito.ArgumentMatchers.any; 67 import static org.mockito.Matchers.anyBoolean; 68 import static org.mockito.Matchers.anyInt; 69 import static org.mockito.Matchers.anyObject; 70 import static org.mockito.Matchers.eq; 71 import static org.mockito.Mockito.mock; 72 import static org.mockito.Mockito.times; 73 import static org.mockito.Mockito.verify; 74 import static org.mockito.Mockito.verifyZeroInteractions; 75 import static org.mockito.Mockito.when; 76 77 @RunWith(AndroidJUnit4.class) 78 public class AccessibilityShortcutControllerTest { 79 private static final String SERVICE_NAME_STRING = "fake.package/fake.service.name"; 80 private static final String SERVICE_NAME_SUMMARY = "Summary"; 81 private static final long VIBRATOR_PATTERN_1 = 100L; 82 private static final long VIBRATOR_PATTERN_2 = 150L; 83 private static final int[] VIBRATOR_PATTERN_INT = {(int) VIBRATOR_PATTERN_1, 84 (int) VIBRATOR_PATTERN_2}; 85 private static final long[] VIBRATOR_PATTERN_LONG = {VIBRATOR_PATTERN_1, VIBRATOR_PATTERN_2}; 86 87 // Convenience values for enabling/disabling to make code more readable 88 private static final int DISABLED = 0; 89 private static final int ENABLED_EXCEPT_LOCK_SCREEN = 1; 90 private static final int ENABLED_INCLUDING_LOCK_SCREEN = 2; 91 private static final int DISABLED_BUT_LOCK_SCREEN_ON = 3; 92 93 private @Mock Context mContext; 94 private @Mock FrameworkObjectProvider mFrameworkObjectProvider; 95 private @Mock IAccessibilityManager mAccessibilityManagerService; 96 private @Mock Handler mHandler; 97 private @Mock AlertDialog.Builder mAlertDialogBuilder; 98 private @Mock AlertDialog mAlertDialog; 99 private @Mock AccessibilityServiceInfo mServiceInfo; 100 private @Mock Resources mResources; 101 private @Mock Toast mToast; 102 private @Mock Vibrator mVibrator; 103 private @Mock ApplicationInfo mApplicationInfo; 104 private @Mock PackageManager mPackageManager; 105 106 private MockContentResolver mContentResolver; 107 private WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams(); 108 109 @Before 110 public void setUp() throws Exception { 111 MockitoAnnotations.initMocks(this); 112 113 when(mVibrator.hasVibrator()).thenReturn(true); 114 115 when(mContext.getResources()).thenReturn(mResources); 116 when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo); 117 when(mContext.getSystemService(Context.VIBRATOR_SERVICE)).thenReturn(mVibrator); 118 when(mContext.getPackageManager()).thenReturn(mPackageManager); 119 120 // We're not checking the text. Just prevent us crashing when getting text. 121 when(mPackageManager.getText(any(), anyInt(), any())).thenReturn("text"); 122 123 mContentResolver = new MockContentResolver(mContext); 124 mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); 125 when(mContext.getContentResolver()).thenReturn(mContentResolver); 126 127 when(mAccessibilityManagerService.getInstalledAccessibilityServiceList(anyInt())) 128 .thenReturn(Collections.singletonList(mServiceInfo)); 129 130 // Use the extra level of indirection in the object to mock framework objects 131 AccessibilityManager accessibilityManager = 132 new AccessibilityManager(mHandler, mAccessibilityManagerService, 0); 133 when(mFrameworkObjectProvider.getAccessibilityManagerInstance(mContext)) 134 .thenReturn(accessibilityManager); 135 when(mFrameworkObjectProvider.getAlertDialogBuilder(mContext)) 136 .thenReturn(mAlertDialogBuilder); 137 when(mFrameworkObjectProvider.makeToastFromText(eq(mContext), anyObject(), anyInt())) 138 .thenReturn(mToast); 139 when(mFrameworkObjectProvider.getSystemUiContext()).thenReturn(mContext); 140 141 when(mResources.getString(anyInt())).thenReturn("Howdy %s"); 142 when(mResources.getIntArray(anyInt())).thenReturn(VIBRATOR_PATTERN_INT); 143 144 ResolveInfo resolveInfo = mock(ResolveInfo.class); 145 when(resolveInfo.loadLabel(anyObject())).thenReturn("Service name"); 146 when(mServiceInfo.getResolveInfo()).thenReturn(resolveInfo); 147 when(mServiceInfo.getComponentName()) 148 .thenReturn(ComponentName.unflattenFromString(SERVICE_NAME_STRING)); 149 when(mServiceInfo.loadSummary(any())).thenReturn(SERVICE_NAME_SUMMARY); 150 151 when(mAlertDialogBuilder.setTitle(anyInt())).thenReturn(mAlertDialogBuilder); 152 when(mAlertDialogBuilder.setCancelable(anyBoolean())).thenReturn(mAlertDialogBuilder); 153 when(mAlertDialogBuilder.setMessage(anyObject())).thenReturn(mAlertDialogBuilder); 154 when(mAlertDialogBuilder.setPositiveButton(anyInt(), anyObject())) 155 .thenReturn(mAlertDialogBuilder); 156 when(mAlertDialogBuilder.setNegativeButton(anyInt(), anyObject())) 157 .thenReturn(mAlertDialogBuilder); 158 when(mAlertDialogBuilder.setOnCancelListener(anyObject())).thenReturn(mAlertDialogBuilder); 159 when(mAlertDialogBuilder.create()).thenReturn(mAlertDialog); 160 161 mLayoutParams.privateFlags = 0; 162 when(mToast.getWindowParams()).thenReturn(mLayoutParams); 163 164 Window window = mock(Window.class); 165 // Initialize the mWindowAttributes field which was not properly initialized during mock 166 // creation. 167 try { 168 Field field = Window.class.getDeclaredField("mWindowAttributes"); 169 field.setAccessible(true); 170 field.set(window, new WindowManager.LayoutParams()); 171 } catch (Exception e) { 172 throw new RuntimeException("Unable to set mWindowAttributes", e); 173 } 174 when(mAlertDialog.getWindow()).thenReturn(window); 175 } 176 177 @After 178 public void tearDown() { 179 } 180 181 @Test 182 public void testShortcutAvailable_enabledButNoServiceWhenCreated_shouldReturnFalse() { 183 configureNoShortcutService(); 184 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 185 assertFalse(getController().isAccessibilityShortcutAvailable(false)); 186 } 187 188 @Test 189 public void testShortcutAvailable_enabledWithValidServiceWhenCreated_shouldReturnTrue() { 190 configureValidShortcutService(); 191 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 192 assertTrue(getController().isAccessibilityShortcutAvailable(false)); 193 } 194 195 @Test 196 public void testShortcutAvailable_disabledWithValidServiceWhenCreated_shouldReturnFalse() { 197 configureValidShortcutService(); 198 configureShortcutEnabled(DISABLED_BUT_LOCK_SCREEN_ON); 199 assertFalse(getController().isAccessibilityShortcutAvailable(false)); 200 } 201 202 @Test 203 public void testShortcutAvailable_onLockScreenButDisabledThere_shouldReturnFalse() { 204 configureValidShortcutService(); 205 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 206 assertFalse(getController().isAccessibilityShortcutAvailable(true)); 207 } 208 209 @Test 210 public void testShortcutAvailable_onLockScreenAndEnabledThere_shouldReturnTrue() { 211 configureValidShortcutService(); 212 configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN); 213 assertTrue(getController().isAccessibilityShortcutAvailable(true)); 214 } 215 216 @Test 217 public void testShortcutAvailable_onLockScreenAndLockScreenPreferenceUnset() { 218 // When the user hasn't specified a lock screen preference, we allow from the lock screen 219 // as long as the user has agreed to enable the shortcut 220 configureValidShortcutService(); 221 configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN); 222 Settings.Secure.putString( 223 mContentResolver, ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, null); 224 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 225 assertFalse(getController().isAccessibilityShortcutAvailable(true)); 226 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); 227 assertTrue(getController().isAccessibilityShortcutAvailable(true)); 228 } 229 230 @Test 231 public void testShortcutAvailable_whenServiceIdBecomesNull_shouldReturnFalse() { 232 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 233 configureValidShortcutService(); 234 AccessibilityShortcutController accessibilityShortcutController = getController(); 235 Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, ""); 236 accessibilityShortcutController.onSettingsChanged(); 237 assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable(false)); 238 } 239 240 @Test 241 public void testShortcutAvailable_whenServiceIdBecomesNonNull_shouldReturnTrue() { 242 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 243 configureNoShortcutService(); 244 AccessibilityShortcutController accessibilityShortcutController = getController(); 245 configureValidShortcutService(); 246 accessibilityShortcutController.onSettingsChanged(); 247 assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable(false)); 248 } 249 250 @Test 251 public void testShortcutAvailable_whenShortcutBecomesDisabled_shouldReturnFalse() { 252 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 253 configureValidShortcutService(); 254 AccessibilityShortcutController accessibilityShortcutController = getController(); 255 configureShortcutEnabled(DISABLED); 256 accessibilityShortcutController.onSettingsChanged(); 257 assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable(false)); 258 } 259 260 @Test 261 public void testShortcutAvailable_whenShortcutBecomesEnabled_shouldReturnTrue() { 262 configureShortcutEnabled(DISABLED); 263 configureValidShortcutService(); 264 AccessibilityShortcutController accessibilityShortcutController = getController(); 265 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 266 accessibilityShortcutController.onSettingsChanged(); 267 assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable(false)); 268 } 269 270 @Test 271 public void testShortcutAvailable_whenLockscreenBecomesDisabled_shouldReturnFalse() { 272 configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN); 273 configureValidShortcutService(); 274 AccessibilityShortcutController accessibilityShortcutController = getController(); 275 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 276 accessibilityShortcutController.onSettingsChanged(); 277 assertFalse(accessibilityShortcutController.isAccessibilityShortcutAvailable(true)); 278 } 279 280 @Test 281 public void testShortcutAvailable_whenLockscreenBecomesEnabled_shouldReturnTrue() { 282 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 283 configureValidShortcutService(); 284 AccessibilityShortcutController accessibilityShortcutController = getController(); 285 configureShortcutEnabled(ENABLED_INCLUDING_LOCK_SCREEN); 286 accessibilityShortcutController.onSettingsChanged(); 287 assertTrue(accessibilityShortcutController.isAccessibilityShortcutAvailable(true)); 288 } 289 290 @Test 291 public void testOnAccessibilityShortcut_vibrates() { 292 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 293 AccessibilityShortcutController accessibilityShortcutController = getController(); 294 accessibilityShortcutController.performAccessibilityShortcut(); 295 verify(mVibrator).vibrate(aryEq(VIBRATOR_PATTERN_LONG), eq(-1), anyObject()); 296 } 297 298 @Test 299 public void testOnAccessibilityShortcut_firstTime_showsWarningDialog() 300 throws Exception { 301 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 302 configureValidShortcutService(); 303 AccessibilityShortcutController accessibilityShortcutController = getController(); 304 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 305 accessibilityShortcutController.performAccessibilityShortcut(); 306 307 assertEquals(1, Settings.Secure.getInt( 308 mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0)); 309 verify(mResources).getString(R.string.accessibility_shortcut_toogle_warning); 310 verify(mAlertDialog).show(); 311 verify(mAccessibilityManagerService).getInstalledAccessibilityServiceList(anyInt()); 312 verify(mAccessibilityManagerService, times(0)).performAccessibilityShortcut(); 313 } 314 315 @Test 316 public void testOnAccessibilityShortcut_withDialogShowing_callsServer() 317 throws Exception { 318 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 319 configureValidShortcutService(); 320 AccessibilityShortcutController accessibilityShortcutController = getController(); 321 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 322 accessibilityShortcutController.performAccessibilityShortcut(); 323 accessibilityShortcutController.performAccessibilityShortcut(); 324 verify(mToast).show(); 325 assertEquals(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS, 326 mLayoutParams.privateFlags 327 & WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS); 328 verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(); 329 } 330 331 @Test 332 public void testOnAccessibilityShortcut_ifCanceledFirstTime_showsWarningDialog() 333 throws Exception { 334 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 335 configureValidShortcutService(); 336 AccessibilityShortcutController accessibilityShortcutController = getController(); 337 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 338 accessibilityShortcutController.performAccessibilityShortcut(); 339 ArgumentCaptor<AlertDialog.OnCancelListener> cancelListenerCaptor = 340 ArgumentCaptor.forClass(AlertDialog.OnCancelListener.class); 341 verify(mAlertDialogBuilder).setOnCancelListener(cancelListenerCaptor.capture()); 342 // Call the cancel callback 343 cancelListenerCaptor.getValue().onCancel(null); 344 345 accessibilityShortcutController.performAccessibilityShortcut(); 346 verify(mAlertDialog, times(2)).show(); 347 } 348 349 @Test 350 public void testClickingDisableButtonInDialog_shouldClearShortcutId() { 351 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 352 configureValidShortcutService(); 353 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 354 getController().performAccessibilityShortcut(); 355 356 ArgumentCaptor<DialogInterface.OnClickListener> captor = 357 ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); 358 verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.disable_accessibility_shortcut), 359 captor.capture()); 360 // Call the button callback 361 captor.getValue().onClick(null, 0); 362 assertTrue(TextUtils.isEmpty( 363 Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE))); 364 } 365 366 @Test 367 public void testClickingLeaveOnButtonInDialog_shouldLeaveShortcutReady() throws Exception { 368 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 369 configureValidShortcutService(); 370 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 0); 371 getController().performAccessibilityShortcut(); 372 373 ArgumentCaptor<DialogInterface.OnClickListener> captor = 374 ArgumentCaptor.forClass(DialogInterface.OnClickListener.class); 375 verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.leave_accessibility_shortcut_on), 376 captor.capture()); 377 // Call the button callback, if one exists 378 if (captor.getValue() != null) { 379 captor.getValue().onClick(null, 0); 380 } 381 assertEquals(SERVICE_NAME_STRING, 382 Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)); 383 assertEquals(1, Settings.Secure.getInt( 384 mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)); 385 } 386 387 @Test 388 public void testOnAccessibilityShortcut_afterDialogShown_shouldCallServer() throws Exception { 389 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 390 configureValidShortcutService(); 391 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); 392 getController().performAccessibilityShortcut(); 393 394 verifyZeroInteractions(mAlertDialogBuilder, mAlertDialog); 395 verify(mToast).show(); 396 verify(mAccessibilityManagerService).performAccessibilityShortcut(); 397 } 398 399 @Test 400 public void getFrameworkFeatureMap_shouldBeNonNullAndUnmodifiable() { 401 Map<ComponentName, AccessibilityShortcutController.ToggleableFrameworkFeatureInfo> 402 frameworkFeatureMap = 403 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); 404 assertTrue("Framework features not supported", frameworkFeatureMap.size() > 0); 405 406 try { 407 frameworkFeatureMap.clear(); 408 fail("Framework feature map should be unmodifieable"); 409 } catch (UnsupportedOperationException e) { 410 // Expected 411 } 412 } 413 414 @Test 415 public void testOnAccessibilityShortcut_forServiceWithNoSummary_doesNotCrash() 416 throws Exception { 417 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 418 configureValidShortcutService(); 419 when(mServiceInfo.loadSummary(any())).thenReturn(null); 420 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); 421 getController().performAccessibilityShortcut(); 422 verify(mAccessibilityManagerService).performAccessibilityShortcut(); 423 } 424 425 @Test 426 public void testOnAccessibilityShortcut_forFrameworkFeature_callsServiceWithNoToast() 427 throws Exception { 428 configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN); 429 configureFirstFrameworkFeature(); 430 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN, 1); 431 getController().performAccessibilityShortcut(); 432 433 verifyZeroInteractions(mToast); 434 verify(mAccessibilityManagerService).performAccessibilityShortcut(); 435 } 436 437 private void configureNoShortcutService() { 438 Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, ""); 439 } 440 441 private void configureValidShortcutService() { 442 Settings.Secure.putString( 443 mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, SERVICE_NAME_STRING); 444 } 445 446 private void configureFirstFrameworkFeature() { 447 ComponentName featureComponentName = 448 (ComponentName) AccessibilityShortcutController.getFrameworkShortcutFeaturesMap() 449 .keySet().toArray()[0]; 450 Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, 451 featureComponentName.flattenToString()); 452 } 453 454 private void configureShortcutEnabled(int enabledValue) { 455 final boolean enabled; 456 final boolean lockscreen; 457 458 switch (enabledValue) { 459 case DISABLED: 460 enabled = false; 461 lockscreen = false; 462 break; 463 case DISABLED_BUT_LOCK_SCREEN_ON: 464 enabled = false; 465 lockscreen = true; 466 break; 467 case ENABLED_INCLUDING_LOCK_SCREEN: 468 enabled = true; 469 lockscreen = true; 470 break; 471 case ENABLED_EXCEPT_LOCK_SCREEN: 472 enabled = true; 473 lockscreen = false; 474 break; 475 default: 476 throw new IllegalArgumentException(); 477 } 478 479 Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_ENABLED, enabled ? 1 : 0); 480 Settings.Secure.putInt( 481 mContentResolver, ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN, lockscreen ? 1 : 0); 482 } 483 484 private AccessibilityShortcutController getController() { 485 AccessibilityShortcutController accessibilityShortcutController = 486 new AccessibilityShortcutController(mContext, mHandler, 0); 487 accessibilityShortcutController.mFrameworkObjectProvider = mFrameworkObjectProvider; 488 return accessibilityShortcutController; 489 } 490 } 491