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

Commit b8aab1fe authored by Roy Chou's avatar Roy Chou Committed by Automerger Merge Worker
Browse files

Merge "feat(#AlwaysOnMagnifier): Add haptic and buffer zone when panning scale...

Merge "feat(#AlwaysOnMagnifier): Add haptic and buffer zone when panning scale to persisted scale" into udc-dev am: 42139e0e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21551981



Change-Id: I67e429e5e57916346cd02eea2b7c03444104da94
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents d7eed491 42139e0e
Loading
Loading
Loading
Loading
+73 −7
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.provider.Settings;
import android.util.Log;
import android.util.MathUtils;
@@ -279,7 +281,8 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
        return mTempPointerProperties;
    }

    private void transitionTo(State state) {
    @VisibleForTesting
    void transitionTo(State state) {
        if (DEBUG_STATE_TRANSITIONS) {
            Slog.i(mLogTag,
                    (State.nameOf(mCurrentState) + " -> " + State.nameOf(state)
@@ -287,6 +290,9 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
                    .replace(getClass().getName(), ""));
        }
        mPreviousState = mCurrentState;
        if (state == mPanningScalingState) {
            mPanningScalingState.prepareForState();
        }
        mCurrentState = state;
    }

@@ -317,18 +323,34 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
    final class PanningScalingState extends SimpleOnGestureListener
            implements OnScaleGestureListener, State {

        private final Context mContext;
        private final ScaleGestureDetector mScaleGestureDetector;
        private final GestureDetector mScrollGestureDetector;
        final float mScalingThreshold;

        float mInitialScaleFactor = -1;
        boolean mScaling;
        @VisibleForTesting boolean mScaling;

        /**
         * Whether it needs to detect the target scale passes
         * {@link FullScreenMagnificationController#getPersistedScale} during panning scale.
         */
        @VisibleForTesting boolean mDetectingPassPersistedScale;

        // The threshold for relative difference from given scale to persisted scale. If the
        // difference >= threshold, we can start detecting if the scale passes the persisted
        // scale during panning.
        @VisibleForTesting static final float CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD = 0.2f;
        // The threshold for relative difference from given scale to persisted scale. If the
        // difference < threshold, we can decide that the scale passes the persisted scale.
        @VisibleForTesting static final float PASSING_PERSISTED_SCALE_THRESHOLD = 0.01f;

        PanningScalingState(Context context) {
            final TypedValue scaleValue = new TypedValue();
            context.getResources().getValue(
                    R.dimen.config_screen_magnification_scaling_threshold,
                    scaleValue, false);
            mContext = context;
            mScalingThreshold = scaleValue.getFloat();
            mScaleGestureDetector = new ScaleGestureDetector(context, this, Handler.getMain());
            mScaleGestureDetector.setQuickScaleEnabled(false);
@@ -351,12 +373,59 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
            }
        }


        void prepareForState() {
            checkShouldDetectPassPersistedScale();
        }

        private void checkShouldDetectPassPersistedScale() {
            if (mDetectingPassPersistedScale) {
                return;
            }

            final float currentScale =
                    mFullScreenMagnificationController.getScale(mDisplayId);
            final float persistedScale =
                    mFullScreenMagnificationController.getPersistedScale(mDisplayId);

            mDetectingPassPersistedScale =
                    (abs(currentScale - persistedScale) / persistedScale)
                            >= CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
        }

        public void persistScaleAndTransitionTo(State state) {
            mFullScreenMagnificationController.persistScale(mDisplayId);
            clear();
            transitionTo(state);
        }

        @VisibleForTesting
        void setScaleAndClearIfNeeded(float scale, float pivotX, float pivotY) {
            if (mDetectingPassPersistedScale) {
                final float persistedScale =
                        mFullScreenMagnificationController.getPersistedScale(mDisplayId);
                // If the scale passes the persisted scale during panning, perform a vibration
                // feedback to user. Also, call {@link clear} to create a buffer zone so that
                // user needs to panning more than {@link mScalingThreshold} to change scale again.
                if (abs(scale - persistedScale) / persistedScale
                        < PASSING_PERSISTED_SCALE_THRESHOLD) {
                    scale = persistedScale;
                    final Vibrator vibrator = mContext.getSystemService(Vibrator.class);
                    if (vibrator != null) {
                        vibrator.vibrate(
                                VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK));
                    }
                    clear();
                }
            }

            if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
            mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);

            checkShouldDetectPassPersistedScale();
        }

        @Override
        public boolean onScroll(MotionEvent first, MotionEvent second,
                float distanceX, float distanceY) {
@@ -402,11 +471,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
                scale = targetScale;
            }

            final float pivotX = detector.getFocusX();
            final float pivotY = detector.getFocusY();
            if (DEBUG_PANNING_SCALING) Slog.i(mLogTag, "Scaled content to: " + scale + "x");
            mFullScreenMagnificationController.setScale(mDisplayId, scale, pivotX, pivotY, false,
                    AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
            setScaleAndClearIfNeeded(scale, detector.getFocusX(), detector.getFocusY());
            return /* handled: */ true;
        }

@@ -424,6 +489,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
        public void clear() {
            mInitialScaleFactor = -1;
            mScaling = false;
            mDetectingPassPersistedScale = false;
        }

        @Override
+87 −0
Original line number Diff line number Diff line
@@ -44,6 +44,8 @@ import android.annotation.NonNull;
import android.graphics.PointF;
import android.os.Handler;
import android.os.Message;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.testing.TestableContext;
import android.util.DebugUtils;
import android.view.InputDevice;
@@ -507,6 +509,91 @@ public class FullScreenMagnificationGestureHandlerTest {
        verify(mWindowMagnificationPromptController).showNotificationIfNeeded();
    }

    @Test
    public void testTransitToPanningState_scaleDifferenceOverThreshold_startDetecting() {
        final float scale = 2.0f;
        final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
                .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
        final float persistedScale = (1.0f + threshold) * scale + 1.0f;
        mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
                DEFAULT_Y, /* animate= */ false,
                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
        mFullScreenMagnificationController.persistScale(DISPLAY_0);
        mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
                DEFAULT_Y, /* animate= */ false,
                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);

        mMgh.transitionTo(mMgh.mPanningScalingState);

        assertTrue(mMgh.mPanningScalingState.mDetectingPassPersistedScale);

        mMgh.clearAndTransitionToStateDetecting();
        mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
    }

    @Test
    public void testTransitToPanningState_scaleDifferenceLessThanThreshold_doNotDetect() {
        final float scale = 2.0f;
        final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
                .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
        final float persistedScale = (1.0f + threshold) * scale - 0.1f;
        mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
                DEFAULT_Y, /* animate= */ false,
                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
        mFullScreenMagnificationController.persistScale(DISPLAY_0);
        mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X,
                DEFAULT_Y, /* animate= */ false,
                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);

        mMgh.transitionTo(mMgh.mPanningScalingState);

        assertFalse(mMgh.mPanningScalingState.mDetectingPassPersistedScale);

        mMgh.clearAndTransitionToStateDetecting();
        mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
    }

    @Test
    public void testPanningScaleToPersistedScale_detecting_vibrateAndClear() {
        Vibrator vibrator = mock(Vibrator.class);
        mContext.addMockSystemService(Vibrator.class, vibrator);

        mMgh.mPanningScalingState.mDetectingPassPersistedScale = true;

        final float persistedScale =
                mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);

        mMgh.transitionTo(mMgh.mPanningScalingState);
        mMgh.mPanningScalingState.setScaleAndClearIfNeeded(persistedScale, DEFAULT_X, DEFAULT_Y);

        verify(vibrator).vibrate(any(VibrationEffect.class));
        assertFalse(mMgh.mPanningScalingState.mScaling);

        mMgh.clearAndTransitionToStateDetecting();
        mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
    }

    @Test
    public void testPanningScaleOverThreshold_notDetecting_startDetecting() {
        final float persistedScale =
                mFullScreenMagnificationController.getPersistedScale(DISPLAY_0);

        mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X,
                DEFAULT_Y, /* animate= */ false,
                AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
        mMgh.transitionTo(mMgh.mPanningScalingState);

        final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState
                .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD;
        final float scale = (1.0f + threshold) * persistedScale + 1.0f;
        mMgh.mPanningScalingState.setScaleAndClearIfNeeded(scale, DEFAULT_X, DEFAULT_Y);

        assertTrue(mMgh.mPanningScalingState.mDetectingPassPersistedScale);

        mMgh.clearAndTransitionToStateDetecting();
        mFullScreenMagnificationController.reset(DISPLAY_0, /* animate= */ false);
    }

    private void assertActionsInOrder(List<MotionEvent> actualEvents,
            List<Integer> expectedActions) {
        assertTrue(actualEvents.size() == expectedActions.size());