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 android.accessibilityservice.cts; 18 19 import static android.accessibilityservice.cts.utils.CtsTestUtils.runIfNotNull; 20 21 import static org.mockito.Mockito.any; 22 import static org.mockito.Mockito.anyFloat; 23 import static org.mockito.Mockito.eq; 24 import static org.mockito.Mockito.mock; 25 import static org.mockito.Mockito.timeout; 26 import static org.mockito.Mockito.verify; 27 28 import android.accessibility.cts.common.InstrumentedAccessibilityService; 29 import android.accessibility.cts.common.ShellCommandBuilder; 30 import android.accessibilityservice.AccessibilityService.MagnificationController; 31 import android.accessibilityservice.AccessibilityService.MagnificationController.OnMagnificationChangedListener; 32 import android.accessibilityservice.AccessibilityServiceInfo; 33 import android.app.Instrumentation; 34 import android.graphics.Rect; 35 import android.graphics.Region; 36 import android.platform.test.annotations.AppModeFull; 37 import android.test.InstrumentationTestCase; 38 39 import java.util.concurrent.atomic.AtomicBoolean; 40 41 /** 42 * Class for testing {@link AccessibilityServiceInfo}. 43 */ 44 @AppModeFull 45 public class AccessibilityMagnificationTest extends InstrumentationTestCase { 46 47 /** Maximum timeout when waiting for a magnification callback. */ 48 public static final int LISTENER_TIMEOUT_MILLIS = 500; 49 public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = 50 "accessibility_display_magnification_enabled"; 51 private StubMagnificationAccessibilityService mService; 52 private Instrumentation mInstrumentation; 53 54 @Override setUp()55 public void setUp() throws Exception { 56 super.setUp(); 57 ShellCommandBuilder.create(this.getInstrumentation()) 58 .deleteSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED) 59 .run(); 60 mInstrumentation = getInstrumentation(); 61 // Starting the service will force the accessibility subsystem to examine its settings, so 62 // it will update magnification in the process to disable it. 63 mService = StubMagnificationAccessibilityService.enableSelf(mInstrumentation); 64 } 65 66 @Override tearDown()67 protected void tearDown() throws Exception { 68 runIfNotNull(mService, service -> service.runOnServiceSync(service::disableSelfAndRemove)); 69 70 super.tearDown(); 71 } 72 testSetScale()73 public void testSetScale() { 74 final MagnificationController controller = mService.getMagnificationController(); 75 final float scale = 2.0f; 76 final AtomicBoolean result = new AtomicBoolean(); 77 78 mService.runOnServiceSync(() -> result.set(controller.setScale(scale, false))); 79 80 assertTrue("Failed to set scale", result.get()); 81 assertEquals("Failed to apply scale", scale, controller.getScale()); 82 83 mService.runOnServiceSync(() -> result.set(controller.reset(false))); 84 85 assertTrue("Failed to reset", result.get()); 86 assertEquals("Failed to apply reset", 1.0f, controller.getScale()); 87 } 88 testSetScaleAndCenter()89 public void testSetScaleAndCenter() { 90 final MagnificationController controller = mService.getMagnificationController(); 91 final Region region = controller.getMagnificationRegion(); 92 final Rect bounds = region.getBounds(); 93 final float scale = 2.0f; 94 final float x = bounds.left + (bounds.width() / 4.0f); 95 final float y = bounds.top + (bounds.height() / 4.0f); 96 final AtomicBoolean setScale = new AtomicBoolean(); 97 final AtomicBoolean setCenter = new AtomicBoolean(); 98 final AtomicBoolean result = new AtomicBoolean(); 99 100 mService.runOnServiceSync(() -> { 101 setScale.set(controller.setScale(scale, false)); 102 setCenter.set(controller.setCenter(x, y, false)); 103 }); 104 105 assertTrue("Failed to set scale", setScale.get()); 106 assertEquals("Failed to apply scale", scale, controller.getScale()); 107 108 assertTrue("Failed to set center", setCenter.get()); 109 assertEquals("Failed to apply center X", x, controller.getCenterX(), 5.0f); 110 assertEquals("Failed to apply center Y", y, controller.getCenterY(), 5.0f); 111 112 mService.runOnServiceSync(() -> result.set(controller.reset(false))); 113 114 assertTrue("Failed to reset", result.get()); 115 assertEquals("Failed to apply reset", 1.0f, controller.getScale()); 116 } 117 testListener()118 public void testListener() { 119 final MagnificationController controller = mService.getMagnificationController(); 120 final OnMagnificationChangedListener listener = mock(OnMagnificationChangedListener.class); 121 controller.addListener(listener); 122 123 try { 124 final float scale = 2.0f; 125 final AtomicBoolean result = new AtomicBoolean(); 126 127 mService.runOnServiceSync(() -> result.set(controller.setScale(scale, false))); 128 129 assertTrue("Failed to set scale", result.get()); 130 verify(listener, timeout(LISTENER_TIMEOUT_MILLIS).atLeastOnce()).onMagnificationChanged( 131 eq(controller), any(Region.class), eq(scale), anyFloat(), anyFloat()); 132 133 mService.runOnServiceSync(() -> result.set(controller.reset(false))); 134 135 assertTrue("Failed to reset", result.get()); 136 verify(listener, timeout(LISTENER_TIMEOUT_MILLIS).atLeastOnce()).onMagnificationChanged( 137 eq(controller), any(Region.class), eq(1.0f), anyFloat(), anyFloat()); 138 } finally { 139 controller.removeListener(listener); 140 } 141 } 142 testMagnificationServiceShutsDownWhileMagnifying_shouldReturnTo1x()143 public void testMagnificationServiceShutsDownWhileMagnifying_shouldReturnTo1x() { 144 final MagnificationController controller = mService.getMagnificationController(); 145 mService.runOnServiceSync(() -> controller.setScale(2.0f, false)); 146 147 mService.runOnServiceSync(() -> mService.disableSelf()); 148 mService = null; 149 InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService( 150 mInstrumentation, InstrumentedAccessibilityService.class); 151 final MagnificationController controller2 = service.getMagnificationController(); 152 try { 153 assertEquals("Magnification must reset when a service dies", 154 1.0f, controller2.getScale()); 155 } finally { 156 service.runOnServiceSync(() -> service.disableSelf()); 157 } 158 } 159 testGetMagnificationRegion_whenCanControlMagnification_shouldNotBeEmpty()160 public void testGetMagnificationRegion_whenCanControlMagnification_shouldNotBeEmpty() { 161 final MagnificationController controller = mService.getMagnificationController(); 162 Region magnificationRegion = controller.getMagnificationRegion(); 163 assertFalse("Magnification region should not be empty when " 164 + "magnification is being actively controlled", magnificationRegion.isEmpty()); 165 } 166 testGetMagnificationRegion_whenCantControlMagnification_shouldBeEmpty()167 public void testGetMagnificationRegion_whenCantControlMagnification_shouldBeEmpty() { 168 mService.runOnServiceSync(() -> mService.disableSelf()); 169 mService = null; 170 InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService( 171 mInstrumentation, InstrumentedAccessibilityService.class); 172 try { 173 final MagnificationController controller = service.getMagnificationController(); 174 Region magnificationRegion = controller.getMagnificationRegion(); 175 assertTrue("Magnification region should be empty when magnification " 176 + "is not being actively controlled", magnificationRegion.isEmpty()); 177 } finally { 178 service.runOnServiceSync(() -> service.disableSelf()); 179 } 180 } 181 testGetMagnificationRegion_whenMagnificationGesturesEnabled_shouldNotBeEmpty()182 public void testGetMagnificationRegion_whenMagnificationGesturesEnabled_shouldNotBeEmpty() { 183 ShellCommandBuilder.create(mInstrumentation) 184 .putSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, "1") 185 .run(); 186 mService.runOnServiceSync(() -> mService.disableSelf()); 187 mService = null; 188 InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService( 189 mInstrumentation, InstrumentedAccessibilityService.class); 190 try { 191 final MagnificationController controller = service.getMagnificationController(); 192 Region magnificationRegion = controller.getMagnificationRegion(); 193 assertFalse("Magnification region should not be empty when magnification " 194 + "gestures are active", magnificationRegion.isEmpty()); 195 } finally { 196 service.runOnServiceSync(() -> service.disableSelf()); 197 ShellCommandBuilder.create(mInstrumentation) 198 .deleteSecureSetting(ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED) 199 .run(); 200 } 201 } 202 testAnimatingMagnification()203 public void testAnimatingMagnification() throws InterruptedException { 204 final MagnificationController controller = mService.getMagnificationController(); 205 final int timeBetweenAnimationChanges = 100; 206 207 final float scale1 = 5.0f; 208 final float x1 = 500; 209 final float y1 = 1000; 210 211 final float scale2 = 4.0f; 212 final float x2 = 500; 213 final float y2 = 1500; 214 215 final float scale3 = 2.1f; 216 final float x3 = 700; 217 final float y3 = 700; 218 219 for (int i = 0; i < 5; i++) { 220 mService.runOnServiceSync(() -> { 221 controller.setScale(scale1, true); 222 controller.setCenter(x1, y1, true); 223 }); 224 225 Thread.sleep(timeBetweenAnimationChanges); 226 227 mService.runOnServiceSync(() -> { 228 controller.setScale(scale2, true); 229 controller.setCenter(x2, y2, true); 230 }); 231 232 Thread.sleep(timeBetweenAnimationChanges); 233 234 mService.runOnServiceSync(() -> { 235 controller.setScale(scale3, true); 236 controller.setCenter(x3, y3, true); 237 }); 238 239 Thread.sleep(timeBetweenAnimationChanges); 240 } 241 } 242 } 243