1 /* 2 * Copyright (C) 2022 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 static androidx.test.espresso.Espresso.onView; 20 import static androidx.test.espresso.action.ViewActions.click; 21 import static androidx.test.espresso.action.ViewActions.doubleClick; 22 import static androidx.test.espresso.action.ViewActions.scrollTo; 23 import static androidx.test.espresso.action.ViewActions.swipeUp; 24 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist; 25 import static androidx.test.espresso.assertion.ViewAssertions.matches; 26 import static androidx.test.espresso.matcher.RootMatchers.isDialog; 27 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; 28 import static androidx.test.espresso.matcher.ViewMatchers.withClassName; 29 import static androidx.test.espresso.matcher.ViewMatchers.withId; 30 import static androidx.test.espresso.matcher.ViewMatchers.withText; 31 32 import static org.hamcrest.Matchers.allOf; 33 import static org.hamcrest.Matchers.endsWith; 34 import static org.mockito.ArgumentMatchers.any; 35 import static org.mockito.ArgumentMatchers.anyInt; 36 import static org.mockito.ArgumentMatchers.anyString; 37 import static org.mockito.ArgumentMatchers.eq; 38 import static org.mockito.Mockito.verify; 39 import static org.mockito.Mockito.when; 40 41 import android.accessibilityservice.AccessibilityServiceInfo; 42 import android.content.ComponentName; 43 import android.content.Context; 44 import android.content.pm.ApplicationInfo; 45 import android.content.pm.PackageManager; 46 import android.content.pm.ResolveInfo; 47 import android.content.pm.ServiceInfo; 48 import android.os.Handler; 49 import android.support.test.uiautomator.By; 50 import android.support.test.uiautomator.UiDevice; 51 import android.support.test.uiautomator.UiObject2; 52 import android.support.test.uiautomator.Until; 53 import android.view.accessibility.AccessibilityManager; 54 import android.view.accessibility.IAccessibilityManager; 55 56 import androidx.lifecycle.Lifecycle; 57 import androidx.test.core.app.ActivityScenario; 58 import androidx.test.platform.app.InstrumentationRegistry; 59 import androidx.test.runner.AndroidJUnit4; 60 61 import com.android.internal.R; 62 import com.android.internal.accessibility.dialog.AccessibilityShortcutChooserActivity; 63 64 import org.junit.After; 65 import org.junit.Before; 66 import org.junit.Rule; 67 import org.junit.Test; 68 import org.junit.runner.RunWith; 69 import org.mockito.Mock; 70 import org.mockito.junit.MockitoJUnit; 71 import org.mockito.junit.MockitoRule; 72 73 import java.util.Collections; 74 75 /** 76 * Tests for {@link AccessibilityShortcutChooserActivity}. 77 */ 78 @RunWith(AndroidJUnit4.class) 79 public class AccessibilityShortcutChooserActivityTest { 80 private static final String ONE_HANDED_MODE = "One-Handed mode"; 81 private static final String DENY_LABEL = "Deny"; 82 private static final String EDIT_LABEL = "Edit shortcuts"; 83 private static final String LIST_TITLE_LABEL = "Choose features to use"; 84 private static final String TEST_LABEL = "TEST_LABEL"; 85 private static final ComponentName TEST_COMPONENT_NAME = new ComponentName("package", "class"); 86 private static final long UI_TIMEOUT_MS = 1000; 87 private UiDevice mDevice; 88 private ActivityScenario<TestAccessibilityShortcutChooserActivity> mScenario; 89 private TestAccessibilityShortcutChooserActivity mActivity; 90 91 @Rule 92 public final MockitoRule mMockitoRule = MockitoJUnit.rule(); 93 @Mock 94 private AccessibilityServiceInfo mAccessibilityServiceInfo; 95 @Mock 96 private ResolveInfo mResolveInfo; 97 @Mock 98 private ServiceInfo mServiceInfo; 99 @Mock 100 private ApplicationInfo mApplicationInfo; 101 @Mock 102 private IAccessibilityManager mAccessibilityManagerService; 103 104 @Before setUp()105 public void setUp() throws Exception { 106 mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 107 mDevice.wakeUp(); 108 when(mAccessibilityServiceInfo.getResolveInfo()).thenReturn(mResolveInfo); 109 mResolveInfo.serviceInfo = mServiceInfo; 110 mServiceInfo.applicationInfo = mApplicationInfo; 111 when(mResolveInfo.loadLabel(any(PackageManager.class))).thenReturn(TEST_LABEL); 112 when(mAccessibilityServiceInfo.getComponentName()).thenReturn(TEST_COMPONENT_NAME); 113 when(mAccessibilityManagerService.getInstalledAccessibilityServiceList( 114 anyInt())).thenReturn(Collections.singletonList(mAccessibilityServiceInfo)); 115 when(mAccessibilityManagerService.isAccessibilityTargetAllowed( 116 anyString(), anyInt(), anyInt())).thenReturn(true); 117 TestAccessibilityShortcutChooserActivity.setupForTesting(mAccessibilityManagerService); 118 mScenario = ActivityScenario.launch(TestAccessibilityShortcutChooserActivity.class); 119 mScenario.onActivity(activity -> mActivity = activity); 120 mScenario.moveToState(Lifecycle.State.CREATED); 121 mScenario.moveToState(Lifecycle.State.STARTED); 122 mScenario.moveToState(Lifecycle.State.RESUMED); 123 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 124 } 125 126 @After cleanUp()127 public void cleanUp() { 128 mScenario.moveToState(Lifecycle.State.DESTROYED); 129 } 130 131 @Test doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist()132 public void doubleClickTestServiceAndClickDenyButton_permissionDialogDoesNotExist() { 133 openShortcutsList(); 134 135 // Performing the double-click is flaky so retry if needed. 136 for (int attempt = 1; attempt <= 2; attempt++) { 137 onView(withText(TEST_LABEL)).perform(scrollTo(), doubleClick()); 138 if (mDevice.wait(Until.hasObject(By.text(DENY_LABEL)), UI_TIMEOUT_MS)) { 139 break; 140 } 141 } 142 143 onView(withText(DENY_LABEL)).perform(scrollTo(), click()); 144 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 145 146 onView(withId(R.id.accessibility_permissionDialog_title)).inRoot(isDialog()).check( 147 doesNotExist()); 148 } 149 150 @Test clickServiceTarget_notPermittedByAdmin_sendRestrictedDialogIntent()151 public void clickServiceTarget_notPermittedByAdmin_sendRestrictedDialogIntent() 152 throws Exception { 153 when(mAccessibilityManagerService.isAccessibilityTargetAllowed( 154 eq(TEST_COMPONENT_NAME.getPackageName()), anyInt(), anyInt())).thenReturn(false); 155 openShortcutsList(); 156 157 onView(withText(TEST_LABEL)).perform(scrollTo(), click()); 158 159 verify(mAccessibilityManagerService).sendRestrictedDialogIntent( 160 eq(TEST_COMPONENT_NAME.getPackageName()), anyInt(), anyInt()); 161 } 162 163 @Test popEditShortcutMenuList_oneHandedModeEnabled_shouldBeInListView()164 public void popEditShortcutMenuList_oneHandedModeEnabled_shouldBeInListView() { 165 TestUtils.setOneHandedModeEnabled(this, /* enabled= */ true); 166 openShortcutsList(); 167 168 onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp()); 169 mDevice.wait(Until.hasObject(By.text(ONE_HANDED_MODE)), UI_TIMEOUT_MS); 170 171 onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(matches(isDisplayed())); 172 } 173 174 @Test popEditShortcutMenuList_oneHandedModeDisabled_shouldNotBeInListView()175 public void popEditShortcutMenuList_oneHandedModeDisabled_shouldNotBeInListView() { 176 TestUtils.setOneHandedModeEnabled(this, /* enabled= */ false); 177 openShortcutsList(); 178 179 onView(allOf(withClassName(endsWith("ListView")), isDisplayed())).perform(swipeUp()); 180 InstrumentationRegistry.getInstrumentation().waitForIdleSync(); 181 182 onView(withText(ONE_HANDED_MODE)).inRoot(isDialog()).check(doesNotExist()); 183 } 184 openShortcutsList()185 private void openShortcutsList() { 186 UiObject2 editButton = mDevice.findObject(By.text(EDIT_LABEL)); 187 if (editButton != null) { 188 editButton.click(); 189 } 190 mDevice.wait(Until.hasObject(By.textStartsWith(LIST_TITLE_LABEL)), UI_TIMEOUT_MS); 191 } 192 193 /** 194 * Used for testing. 195 */ 196 public static class TestAccessibilityShortcutChooserActivity extends 197 AccessibilityShortcutChooserActivity { 198 private static IAccessibilityManager sAccessibilityManagerService; 199 setupForTesting(IAccessibilityManager accessibilityManagerService)200 public static void setupForTesting(IAccessibilityManager accessibilityManagerService) { 201 sAccessibilityManagerService = accessibilityManagerService; 202 } 203 204 @Override getSystemService(String name)205 public Object getSystemService(String name) { 206 if (Context.ACCESSIBILITY_SERVICE.equals(name) 207 && sAccessibilityManagerService != null) { 208 return new AccessibilityManager(this, new Handler(getMainLooper()), 209 sAccessibilityManagerService, /* userId= */ 0, /* serviceConnect= */ true); 210 } 211 212 return super.getSystemService(name); 213 } 214 } 215 } 216