blob: 8393012eaf7362990fd834e6e374d7357fbacc2a [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.accessibilityservice.cts;
import static android.accessibilityservice.cts.utils.AsyncUtils.await;
import static android.accessibilityservice.cts.utils.AsyncUtils.waitOn;
import static android.accessibilityservice.cts.utils.GestureUtils.add;
import static android.accessibilityservice.cts.utils.GestureUtils.click;
import static android.accessibilityservice.cts.utils.GestureUtils.dispatchGesture;
import static android.accessibilityservice.cts.utils.GestureUtils.distance;
import static android.accessibilityservice.cts.utils.GestureUtils.doubleTap;
import static android.accessibilityservice.cts.utils.GestureUtils.drag;
import static android.accessibilityservice.cts.utils.GestureUtils.endTimeOf;
import static android.accessibilityservice.cts.utils.GestureUtils.lastPointOf;
import static android.accessibilityservice.cts.utils.GestureUtils.longClick;
import static android.accessibilityservice.cts.utils.GestureUtils.pointerDown;
import static android.accessibilityservice.cts.utils.GestureUtils.pointerUp;
import static android.accessibilityservice.cts.utils.GestureUtils.startingAt;
import static android.accessibilityservice.cts.utils.GestureUtils.swipe;
import static android.accessibilityservice.cts.utils.GestureUtils.tripleTap;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_UP;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
import android.accessibilityservice.GestureDescription;
import android.accessibilityservice.GestureDescription.StrokeDescription;
import android.accessibilityservice.cts.AccessibilityGestureDispatchTest.GestureDispatchActivity;
import android.accessibilityservice.cts.utils.EventCapturingTouchListener;
import android.app.Instrumentation;
import android.content.pm.PackageManager;
import android.graphics.PointF;
import android.platform.test.annotations.AppModeFull;
import android.provider.Settings;
import android.widget.TextView;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
import org.junit.runner.RunWith;
/**
* Class for testing magnification.
*/
@RunWith(AndroidJUnit4.class)
@AppModeFull
public class MagnificationGestureHandlerTest {
private static final double MIN_SCALE = 1.2;
private InstrumentedAccessibilityService mService;
private Instrumentation mInstrumentation;
private EventCapturingTouchListener mTouchListener = new EventCapturingTouchListener();
float mCurrentScale = 1f;
PointF mCurrentZoomCenter = null;
PointF mTapLocation;
PointF mTapLocation2;
float mPan;
private boolean mHasTouchscreen;
private boolean mOriginalIsMagnificationEnabled;
private final Object mZoomLock = new Object();
private ActivityTestRule<GestureDispatchActivity> mActivityRule =
new ActivityTestRule<>(GestureDispatchActivity.class);
private InstrumentedAccessibilityServiceTestRule<StubMagnificationAccessibilityService>
mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
StubMagnificationAccessibilityService.class, false);
private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
new AccessibilityDumpOnFailureRule();
@Rule
public final RuleChain mRuleChain = RuleChain
.outerRule(mActivityRule)
.around(mServiceRule)
.around(mDumpOnFailureRule);
@Before
public void setUp() throws Exception {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
PackageManager pm = mInstrumentation.getContext().getPackageManager();
mHasTouchscreen = pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
|| pm.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
if (!mHasTouchscreen) return;
mOriginalIsMagnificationEnabled =
Settings.Secure.getInt(mInstrumentation.getContext().getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, 0) == 1;
setMagnificationEnabled(true);
mService = mServiceRule.enableService();
mService.getMagnificationController().addListener(
(controller, region, scale, centerX, centerY) -> {
mCurrentScale = scale;
mCurrentZoomCenter = isZoomed() ? new PointF(centerX, centerY) : null;
synchronized (mZoomLock) {
mZoomLock.notifyAll();
}
});
TextView view = mActivityRule.getActivity().findViewById(R.id.full_screen_text_view);
mInstrumentation.runOnMainSync(() -> {
view.setOnTouchListener(mTouchListener);
int[] xy = new int[2];
view.getLocationOnScreen(xy);
mTapLocation = new PointF(xy[0] + view.getWidth() / 2, xy[1] + view.getHeight() / 2);
mTapLocation2 = add(mTapLocation, 31, 29);
mPan = view.getWidth() / 4;
});
}
@After
public void tearDown() throws Exception {
if (!mHasTouchscreen) return;
setMagnificationEnabled(mOriginalIsMagnificationEnabled);
}
@Test
public void testZoomOnOff() {
if (!mHasTouchscreen) return;
assertFalse(isZoomed());
assertGesturesPropagateToView();
assertFalse(isZoomed());
setZoomByTripleTapping(true);
assertGesturesPropagateToView();
assertTrue(isZoomed());
setZoomByTripleTapping(false);
}
@Test
public void testViewportDragging() {
if (!mHasTouchscreen) return;
assertFalse(isZoomed());
tripleTapAndDragViewport();
waitOn(mZoomLock, () -> !isZoomed());
setZoomByTripleTapping(true);
tripleTapAndDragViewport();
assertTrue(isZoomed());
setZoomByTripleTapping(false);
}
@Test
public void testPanning() {
if (!mHasTouchscreen) return;
assertFalse(isZoomed());
setZoomByTripleTapping(true);
PointF oldCenter = mCurrentZoomCenter;
dispatch(
swipe(mTapLocation, add(mTapLocation, -mPan, 0)),
swipe(mTapLocation2, add(mTapLocation2, -mPan, 0)));
waitOn(mZoomLock,
() -> (mCurrentZoomCenter.x - oldCenter.x >= mPan / mCurrentScale * 0.9));
setZoomByTripleTapping(false);
}
private void setZoomByTripleTapping(boolean desiredZoomState) {
if (isZoomed() == desiredZoomState) return;
dispatch(tripleTap(mTapLocation));
waitOn(mZoomLock, () -> isZoomed() == desiredZoomState);
mTouchListener.assertNonePropagated();
}
private void tripleTapAndDragViewport() {
StrokeDescription down = tripleTapAndHold();
PointF oldCenter = mCurrentZoomCenter;
StrokeDescription drag = drag(down, add(lastPointOf(down), mPan, 0f));
dispatch(drag);
waitOn(mZoomLock, () -> distance(mCurrentZoomCenter, oldCenter) >= mPan / 5);
assertTrue(isZoomed());
mTouchListener.assertNonePropagated();
dispatch(pointerUp(drag));
mTouchListener.assertNonePropagated();
}
private StrokeDescription tripleTapAndHold() {
StrokeDescription tap1 = click(mTapLocation);
StrokeDescription tap2 = startingAt(endTimeOf(tap1) + 20, click(mTapLocation2));
StrokeDescription down = startingAt(endTimeOf(tap2) + 20, pointerDown(mTapLocation));
dispatch(tap1, tap2, down);
waitOn(mZoomLock, () -> isZoomed());
return down;
}
private void assertGesturesPropagateToView() {
dispatch(click(mTapLocation));
mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP);
dispatch(longClick(mTapLocation));
mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP);
dispatch(doubleTap(mTapLocation));
mTouchListener.assertPropagated(ACTION_DOWN, ACTION_UP, ACTION_DOWN, ACTION_UP);
dispatch(swipe(
mTapLocation,
add(mTapLocation, 0, 29)));
mTouchListener.assertPropagated(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
}
private void setMagnificationEnabled(boolean enabled) {
Settings.Secure.putInt(mInstrumentation.getContext().getContentResolver(),
Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, enabled ? 1 : 0);
}
private boolean isZoomed() {
return mCurrentScale >= MIN_SCALE;
}
public void dispatch(StrokeDescription firstStroke, StrokeDescription... rest) {
GestureDescription.Builder builder =
new GestureDescription.Builder().addStroke(firstStroke);
for (StrokeDescription stroke : rest) {
builder.addStroke(stroke);
}
dispatch(builder.build());
}
public void dispatch(GestureDescription gesture) {
await(dispatchGesture(mService, gesture));
}
}