Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 618e191d authored by Roy Chou's avatar Roy Chou
Browse files

feat(onefingerpan): disable overscroll handelr on non-watch devices

The single finger panning feature is first designed for wear devices and
has overscroll handler to increase the one finger operability. But some
of the manipulations are not suitable on phone or other larger screen
devices, like the horizontal overscroll to change the magnification
scale. On large screen devices we would like to delegate the touch
events to display content when overscroll happens. Therefore, we add a new
config config_support_a11y_fullscreen_magnification_overscroll_handler
and make it false by default, so the FullScreenMagnificationGestureHandler
will not use the OverscrollHandler. Also, we overlay the
config to true on watches, so watches will still have overscroll handler
and keep the original behavior.

NO_IFTTT=checked

Bug: 330192193
Flag: ACONFIG com.android.server.accessibility.enable_magnification_one_finger_panning_gesture DEVELOPMENT
Test: manually
      atest FullScreenMagnificationGestureHandlerTest
Change-Id: Ib1cd7ff8b5d04f4cef11165836e9748262814011
parent a17b9681
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -6969,9 +6969,17 @@
         Note that, indefinitely repeating vibrations are not allowed as shutdown vibrations. -->
    <string name="config_defaultShutdownVibrationFile" />

    <!-- Whether single finger panning is enabled when magnification is on -->
    <!-- Whether single finger panning is enabled by default when magnification is on -->
    <bool name="config_enable_a11y_magnification_single_panning">false</bool>

    <!-- Whether the overscroll handler is enabled when fullscreen magnification is on. When true,
         the magnification will change the scale if the user pans the magnifier horizontally past
         the edge of the screen, or delegate the touch events to the app if the user pans vertically
         past the edge. When false, the magnification will delegate the touch events to the app only
         when the users uses single finger to pan the magnifier past the edge of the screen,
         otherwise there are no extra actions. -->
    <bool name="config_enable_a11y_fullscreen_magnification_overscroll_handler">false</bool>

    <!-- The file path in which custom vibrations are provided for haptic feedbacks.
         If the device does not specify any such file path here, if the file path specified here
         does not exist, or if the contents of the file does not make up a valid customization
+3 −0
Original line number Diff line number Diff line
@@ -620,6 +620,9 @@
    <!-- width of the border of the magnification thumbnail -->
    <dimen name="accessibility_magnification_thumbnail_container_stroke_width">4dp</dimen>

    <!-- The distance from the edge within which the gesture is considered to be at the edge -->
    <dimen name="accessibility_fullscreen_magnification_gesture_edge_slop">12dp</dimen>

    <!-- The padding ratio of the Accessibility icon foreground drawable -->
    <item name="accessibility_icon_foreground_padding_ratio" type="dimen">21.88%</item>

+2 −0
Original line number Diff line number Diff line
@@ -5426,6 +5426,8 @@
  <java-symbol type="string" name="lockscreen_too_many_failed_attempts_countdown" />

  <java-symbol type="bool" name="config_enable_a11y_magnification_single_panning" />
  <java-symbol type="bool" name="config_enable_a11y_fullscreen_magnification_overscroll_handler" />
  <java-symbol type="dimen" name="accessibility_fullscreen_magnification_gesture_edge_slop" />

  <java-symbol type="string" name="config_hapticFeedbackCustomizationFile" />

+35 −26
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.accessibility.magnification;

import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
import static android.util.MathUtils.abs;
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static android.view.accessibility.MagnificationAnimationCallback.STUB_ANIMATION_CALLBACK;

@@ -263,27 +264,27 @@ public class FullScreenMagnificationController implements

        @GuardedBy("mLock")
        boolean isAtEdge() {
            return isAtLeftEdge() || isAtRightEdge() || isAtTopEdge() || isAtBottomEdge();
            return isAtLeftEdge(0f) || isAtRightEdge(0f) || isAtTopEdge(0f) || isAtBottomEdge(0f);
        }

        @GuardedBy("mLock")
        boolean isAtLeftEdge() {
            return getOffsetX() == getMaxOffsetXLocked();
        boolean isAtLeftEdge(float slop) {
            return abs(getOffsetX() - getMaxOffsetXLocked()) <= slop;
        }

        @GuardedBy("mLock")
        boolean isAtRightEdge() {
            return getOffsetX() == getMinOffsetXLocked();
        boolean isAtRightEdge(float slop) {
            return abs(getOffsetX() - getMinOffsetXLocked()) <= slop;
        }

        @GuardedBy("mLock")
        boolean isAtTopEdge() {
            return getOffsetY() == getMaxOffsetYLocked();
        boolean isAtTopEdge(float slop) {
            return abs(getOffsetY() - getMaxOffsetYLocked()) <= slop;
        }

        @GuardedBy("mLock")
        boolean isAtBottomEdge() {
            return getOffsetY() == getMinOffsetYLocked();
        boolean isAtBottomEdge(float slop) {
            return abs(getOffsetY() - getMinOffsetYLocked()) <= slop;
        }

        @GuardedBy("mLock")
@@ -1298,7 +1299,7 @@ public class FullScreenMagnificationController implements
     * Returns whether the user is at one of the edges (left, right, top, bottom)
     * of the magnification viewport
     *
     * @param displayId
     * @param displayId The logical display id.
     * @return if user is at the edge of the view
     */
    public boolean isAtEdge(int displayId) {
@@ -1314,64 +1315,72 @@ public class FullScreenMagnificationController implements
    /**
     * Returns whether the user is at the left edge of the viewport
     *
     * @param displayId
     * @return if user is at left edge of view
     * @param displayId The logical display id.
     * @param slop The buffer distance in pixels from the left edge within that will be considered
     *             to be at the edge.
     * @return if user is considered at left edge of view
     */
    public boolean isAtLeftEdge(int displayId) {
    public boolean isAtLeftEdge(int displayId, float slop) {
        synchronized (mLock) {
            final DisplayMagnification display = mDisplays.get(displayId);
            if (display == null) {
                return false;
            }
            return display.isAtLeftEdge();
            return display.isAtLeftEdge(slop);
        }
    }

    /**
     * Returns whether the user is at the right edge of the viewport
     *
     * @param displayId
     * @return if user is at right edge of view
     * @param displayId The logical display id.
     * @param slop The buffer distance in pixels from the right edge within that will be considered
     *             to be at the edge.
     * @return if user is considered at right edge of view
     */
    public boolean isAtRightEdge(int displayId) {
    public boolean isAtRightEdge(int displayId, float slop) {
        synchronized (mLock) {
            final DisplayMagnification display = mDisplays.get(displayId);
            if (display == null) {
                return false;
            }
            return display.isAtRightEdge();
            return display.isAtRightEdge(slop);
        }
    }

    /**
     * Returns whether the user is at the top edge of the viewport
     *
     * @param displayId
     * @return if user is at top edge of view
     * @param displayId The logical display id.
     * @param slop The buffer distance in pixels from the top edge within that will be considered
     *             to be at the edge.
     * @return if user is considered at top edge of view
     */
    public boolean isAtTopEdge(int displayId) {
    public boolean isAtTopEdge(int displayId, float slop) {
        synchronized (mLock) {
            final DisplayMagnification display = mDisplays.get(displayId);
            if (display == null) {
                return false;
            }
            return display.isAtTopEdge();
            return display.isAtTopEdge(slop);
        }
    }

    /**
     * Returns whether the user is at the bottom edge of the viewport
     *
     * @param displayId
     * @return if user is at bottom edge of view
     * @param displayId The logical display id.
     * @param slop The buffer distance in pixels from the bottom edge within that will be considered
     *             to be at the edge.
     * @return if user is considered at bottom edge of view
     */
    public boolean isAtBottomEdge(int displayId) {
    public boolean isAtBottomEdge(int displayId, float slop) {
        synchronized (mLock) {
            final DisplayMagnification display = mDisplays.get(displayId);
            if (display == null) {
                return false;
            }
            return display.isAtBottomEdge();
            return display.isAtBottomEdge(slop);
        }
    }

+52 −34
Original line number Diff line number Diff line
@@ -171,7 +171,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH

    private final FullScreenMagnificationVibrationHelper mFullScreenMagnificationVibrationHelper;

    @VisibleForTesting final OverscrollHandler mOverscrollHandler;
    @VisibleForTesting
    @Nullable
    final OverscrollHandler mOverscrollHandler;

    private final float mOverscrollEdgeSlop;

    private final boolean mIsWatch;

@@ -308,7 +312,11 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
        mSinglePanningState = new SinglePanningState(context);
        mFullScreenMagnificationVibrationHelper = fullScreenMagnificationVibrationHelper;
        mOneFingerPanningSettingsProvider = oneFingerPanningSettingsProvider;
        mOverscrollHandler = new OverscrollHandler();
        boolean overscrollHandlerSupported = context.getResources().getBoolean(
                R.bool.config_enable_a11y_fullscreen_magnification_overscroll_handler);
        mOverscrollHandler = overscrollHandlerSupported ? new OverscrollHandler() : null;
        mOverscrollEdgeSlop = context.getResources().getDimensionPixelSize(
                R.dimen.accessibility_fullscreen_magnification_gesture_edge_slop);
        mIsWatch = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);

        if (mDetectShortcutTrigger) {
@@ -523,16 +531,14 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
            if (action == ACTION_POINTER_UP
                    && event.getPointerCount() == 2 // includes the pointer currently being released
                    && mPreviousState == mViewportDraggingState) {
                // if feature flag is enabled, currently only true on watches
                if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
                if (mOverscrollHandler != null) {
                    mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
                    mOverscrollHandler.clearEdgeState();
                }
                persistScaleAndTransitionTo(mViewportDraggingState);
            } else if (action == ACTION_UP || action == ACTION_CANCEL) {
                onPanningFinished(event);
                // if feature flag is enabled, currently only true on watches
                if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
                if (mOverscrollHandler != null) {
                    mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
                    mOverscrollHandler.clearEdgeState();
                }
@@ -540,7 +546,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
            }
        }


        void prepareForState() {
            checkShouldDetectPassPersistedScale();
        }
@@ -611,7 +616,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
            onPan(second);
            mFullScreenMagnificationController.offsetMagnifiedRegion(mDisplayId, distanceX,
                    distanceY, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
            if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()) {
            if (mOverscrollHandler != null) {
                mOverscrollHandler.onScrollStateChanged(first, second);
            }
            return /* event consumed: */ true;
@@ -1000,7 +1005,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
                                && event.getPointerCount() == 2) {
                            transitionToViewportDraggingStateAndClear(event);
                        } else if (isActivated() && event.getPointerCount() == 2) {
                            if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
                            if (mOverscrollHandler != null
                                    && overscrollState(event, mFirstPointerDownLocation)
                                    == OVERSCROLL_VERTICAL_EDGE) {
                                transitionToDelegatingStateAndClear();
@@ -1011,9 +1016,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
                        } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
                                && isActivated()
                                && event.getPointerCount() == 1) {
                            if (overscrollState(event, mFirstPointerDownLocation)
                            if (mOverscrollHandler != null
                                    && overscrollState(event, mFirstPointerDownLocation)
                                    == OVERSCROLL_VERTICAL_EDGE) {
                                transitionToDelegatingStateAndClear();
                            } else if (overscrollState(event, mFirstPointerDownLocation)
                                    != OVERSCROLL_NONE) {
                                transitionToDelegatingStateAndClear();
                            } else {
                                transitToSinglePanningStateAndClear();
                            }
@@ -1255,7 +1264,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
                        if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
                            transitionToViewportDraggingStateAndClear(event);
                        } else if (isActivated() && event.getPointerCount() == 2) {
                            if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
                            if (mOverscrollHandler != null
                                    && overscrollState(event, mFirstPointerDownLocation)
                                    == OVERSCROLL_VERTICAL_EDGE) {
                                transitionToDelegatingStateAndClear();
@@ -1266,9 +1275,13 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
                        } else if (mOneFingerPanningSettingsProvider.isOneFingerPanningEnabled()
                                && isActivated()
                                && event.getPointerCount() == 1) {
                            if (overscrollState(event, mFirstPointerDownLocation)
                            if (mOverscrollHandler != null
                                    && overscrollState(event, mFirstPointerDownLocation)
                                    == OVERSCROLL_VERTICAL_EDGE) {
                                transitionToDelegatingStateAndClear();
                            } else if (overscrollState(event, mFirstPointerDownLocation)
                                    != OVERSCROLL_NONE) {
                                transitionToDelegatingStateAndClear();
                            } else {
                                transitToSinglePanningStateAndClear();
                            }
@@ -1645,22 +1658,36 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
        }
        float dX = event.getX() - firstPointerDownLocation.x;
        float dY = event.getY() - firstPointerDownLocation.y;
        if (mFullScreenMagnificationController.isAtLeftEdge(mDisplayId) && dX > 0) {
        if (isAtLeftEdge() && dX > 0) {
            return OVERSCROLL_LEFT_EDGE;
        } else if (mFullScreenMagnificationController.isAtRightEdge(mDisplayId) && dX < 0) {
        } else if (isAtRightEdge() && dX < 0) {
            return OVERSCROLL_RIGHT_EDGE;
        } else if (mFullScreenMagnificationController.isAtTopEdge(mDisplayId) && dY > 0
                || mFullScreenMagnificationController.isAtBottomEdge(mDisplayId) && dY < 0) {
        } else if ((isAtTopEdge() && dY > 0) || (isAtBottomEdge() && dY < 0)) {
            return OVERSCROLL_VERTICAL_EDGE;
        }
        return OVERSCROLL_NONE;
    }

    private boolean isAtLeftEdge() {
        return mFullScreenMagnificationController.isAtLeftEdge(mDisplayId, mOverscrollEdgeSlop);
    }

    private boolean isAtRightEdge() {
        return mFullScreenMagnificationController.isAtRightEdge(mDisplayId, mOverscrollEdgeSlop);
    }

    private boolean isAtTopEdge() {
        return mFullScreenMagnificationController.isAtTopEdge(mDisplayId, mOverscrollEdgeSlop);
    }

    private boolean isAtBottomEdge() {
        return mFullScreenMagnificationController.isAtBottomEdge(mDisplayId, mOverscrollEdgeSlop);
    }

    private boolean pointerValid(PointF pointerDownLocation) {
        return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(pointerDownLocation.y));
    }


    private static final class MotionEventInfo {

        private static final int MAX_POOL_SIZE = 10;
@@ -1845,7 +1872,6 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH

    final class SinglePanningState extends SimpleOnGestureListener implements State {


        private final GestureDetector mScrollGestureDetector;
        private MotionEventInfo mEvent;
        SinglePanningState(Context context) {
@@ -1860,8 +1886,10 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
                    onPanningFinished(event);
                    // fall-through!
                case ACTION_CANCEL:
                    if (mOverscrollHandler != null) {
                        mOverscrollHandler.setScaleAndCenterToEdgeIfNeeded();
                        mOverscrollHandler.clearEdgeState();
                    }
                    transitionTo(mDetectingState);
                    break;
            }
@@ -1889,26 +1917,18 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
                                + " isAtEdge: "
                                + mFullScreenMagnificationController.isAtEdge(mDisplayId));
            }
            if (mOverscrollHandler != null) {
                mOverscrollHandler.onScrollStateChanged(first, second);
            return /* event consumed: */ true;
        }

        private void vibrateIfNeeded() {
            if ((mFullScreenMagnificationController.isAtLeftEdge(mDisplayId)
                    || mFullScreenMagnificationController.isAtRightEdge(mDisplayId))) {
                mFullScreenMagnificationVibrationHelper.vibrateIfSettingEnabled();
            }
            return /* event consumed: */ true;
        }



        @Override
        public String toString() {
            return "SinglePanningState{"
                    + "isEdgeOfView="
                    + mFullScreenMagnificationController.isAtEdge(mDisplayId);
        }

    }

    /** Overscroll Handler handles the logic when user is at the edge and scrolls past an edge */
@@ -2007,9 +2027,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
            if (mOverscrollState != OVERSCROLL_NONE) {
                return;
            }
            if ((mFullScreenMagnificationController.isAtLeftEdge(mDisplayId)
                            || mFullScreenMagnificationController.isAtRightEdge(mDisplayId))
                    && !mEdgeCooldown) {
            if ((isAtLeftEdge() || isAtRightEdge()) && !mEdgeCooldown) {
                mFullScreenMagnificationVibrationHelper.vibrateIfSettingEnabled();
            }
        }
Loading