1 /* 2 * Copyright (C) 2023 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.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; 20 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.homeScreenOrBust; 21 import static android.accessibilityservice.cts.utils.ActivityLaunchUtils.launchActivityAndWaitForItToBeOnscreen; 22 import static android.accessibilityservice.cts.utils.CtsTestUtils.isAutomotive; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.junit.Assert.assertTrue; 27 import static org.junit.Assume.assumeFalse; 28 29 import android.accessibility.cts.common.AccessibilityDumpOnFailureRule; 30 import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule; 31 import android.accessibilityservice.AccessibilityService; 32 import android.accessibilityservice.AccessibilityService.MagnificationController; 33 import android.accessibilityservice.AccessibilityServiceInfo; 34 import android.accessibilityservice.MagnificationConfig; 35 import android.accessibilityservice.cts.activities.AccessibilityWindowQueryActivity; 36 import android.app.Instrumentation; 37 import android.app.UiAutomation; 38 import android.graphics.Rect; 39 import android.platform.test.annotations.AppModeFull; 40 import android.platform.test.annotations.Presubmit; 41 42 import androidx.test.rule.ActivityTestRule; 43 import androidx.test.runner.AndroidJUnit4; 44 45 import com.android.compatibility.common.util.CddTest; 46 import com.android.compatibility.common.util.TestUtils; 47 48 import org.junit.AfterClass; 49 import org.junit.Before; 50 import org.junit.BeforeClass; 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.rules.RuleChain; 54 import org.junit.runner.RunWith; 55 56 import java.util.concurrent.atomic.AtomicBoolean; 57 58 /** 59 * Class for testing {@See FullScreenMagnificationController}. 60 */ 61 @AppModeFull 62 @RunWith(AndroidJUnit4.class) 63 @CddTest(requirements = {"3.10/C-1-1,C-1-2"}) 64 @Presubmit 65 public class FullScreenMagnificationControllerTest { 66 67 /** Maximum timeout while waiting for a config to be updated */ 68 private static final int TIMEOUT_CONFIG_SECONDS = 15; 69 private static final int BOUNDS_TOLERANCE = 1; 70 71 private static Instrumentation sInstrumentation; 72 private static UiAutomation sUiAutomation; 73 private StubMagnificationAccessibilityService mService; 74 75 private final ActivityTestRule<AccessibilityWindowQueryActivity> mActivityRule = 76 new ActivityTestRule<>(AccessibilityWindowQueryActivity.class, false, false); 77 78 private InstrumentedAccessibilityServiceTestRule<StubMagnificationAccessibilityService> 79 mMagnificationAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>( 80 StubMagnificationAccessibilityService.class, false); 81 82 private AccessibilityDumpOnFailureRule mDumpOnFailureRule = 83 new AccessibilityDumpOnFailureRule(); 84 85 @Rule 86 public final RuleChain mRuleChain = RuleChain 87 .outerRule(mActivityRule) 88 .around(mMagnificationAccessibilityServiceRule) 89 .around(mDumpOnFailureRule); 90 91 @BeforeClass oneTimeSetup()92 public static void oneTimeSetup() { 93 sInstrumentation = androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(); 94 sUiAutomation = sInstrumentation.getUiAutomation(); 95 AccessibilityServiceInfo info = sUiAutomation.getServiceInfo(); 96 info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS; 97 sUiAutomation.setServiceInfo(info); 98 } 99 100 @AfterClass postTestTearDown()101 public static void postTestTearDown() { 102 sUiAutomation.destroy(); 103 } 104 105 @Before setUp()106 public void setUp() throws Exception { 107 assumeFalse("Magnification is not supported on Automotive.", 108 isAutomotive(sInstrumentation.getTargetContext())); 109 110 mService = mMagnificationAccessibilityServiceRule.enableService(); 111 } 112 113 @Test testActivityTransitions_fullscreenMagnifierMagnifying_zoomOut()114 public void testActivityTransitions_fullscreenMagnifierMagnifying_zoomOut() throws Exception { 115 // wait for the activity to be on screen 116 launchActivityAndWaitForItToBeOnscreen(sInstrumentation, sUiAutomation, mActivityRule); 117 118 zoomIn(/* scale= */ 2.0f); 119 // transition to home screen 120 homeScreenOrBust(sInstrumentation.getContext(), sUiAutomation); 121 122 // we cannot identify if the always on feature flag is enabled here, so we cannot ensure if 123 // the magnifier should deactivate itself when transitions. At least we can still verify 124 // the fullscreen magnifier zooming out here. 125 assertThat(currentScale()).isEqualTo(1f); 126 } 127 zoomIn(float scale)128 private void zoomIn(float scale) throws Exception { 129 final MagnificationController controller = mService.getMagnificationController(); 130 final Rect rect = controller.getMagnificationRegion().getBounds(); 131 final float x = rect.centerX(); 132 final float y = rect.centerY(); 133 final AtomicBoolean setConfig = new AtomicBoolean(); 134 135 final MagnificationConfig config = new MagnificationConfig.Builder() 136 .setMode(MAGNIFICATION_MODE_FULLSCREEN) 137 .setScale(scale) 138 .setCenterX(x) 139 .setCenterY(y).build(); 140 141 mService.runOnServiceSync(() -> { 142 setConfig.set(controller.setMagnificationConfig(config, false)); 143 }); 144 waitUntilMagnificationConfigEquals(controller, config); 145 146 assertTrue("Failed to set config", setConfig.get()); 147 } 148 currentScale()149 private float currentScale() { 150 final MagnificationController controller = mService.getMagnificationController(); 151 final MagnificationConfig config = controller.getMagnificationConfig(); 152 153 assertThat(config).isNotNull(); 154 155 return config.getScale(); 156 } 157 waitUntilMagnificationConfigEquals( AccessibilityService.MagnificationController controller, MagnificationConfig config)158 private void waitUntilMagnificationConfigEquals( 159 AccessibilityService.MagnificationController controller, 160 MagnificationConfig config) throws Exception { 161 TestUtils.waitUntil( 162 "Failed to apply the config. expected: " + config + " , actual: " 163 + controller.getMagnificationConfig(), TIMEOUT_CONFIG_SECONDS, 164 () -> { 165 final MagnificationConfig actualConfig = controller.getMagnificationConfig(); 166 // If expected config activated is false, we just need to verify the activated 167 // value is the same. Otherwise, we need to check all the actual values are 168 // equal to the expected values. 169 if (config.isActivated()) { 170 return actualConfig.getMode() == config.getMode() 171 && actualConfig.isActivated() == config.isActivated() 172 && Float.compare(actualConfig.getScale(), config.getScale()) == 0 173 && (Math.abs(actualConfig.getCenterX() - config.getCenterX()) 174 <= BOUNDS_TOLERANCE) 175 && (Math.abs(actualConfig.getCenterY() - config.getCenterY()) 176 <= BOUNDS_TOLERANCE); 177 } else { 178 return actualConfig.isActivated() == config.isActivated(); 179 } 180 }); 181 } 182 } 183