Loading services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java +5 −3 Original line number Original line Diff line number Diff line Loading @@ -98,8 +98,7 @@ public final class GesturesObserver implements GestureMatcher.StateChangeListene } } mProcessMotionEvent = true; mProcessMotionEvent = true; for (int i = 0; i < mGestureMatchers.size(); i++) { for (int i = 0; i < mGestureMatchers.size(); i++) { final GestureMatcher matcher = final GestureMatcher matcher = mGestureMatchers.get(i); mGestureMatchers.get(i); matcher.onMotionEvent(event, rawEvent, policyFlags); matcher.onMotionEvent(event, rawEvent, policyFlags); if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) { if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) { clear(); clear(); Loading Loading @@ -128,7 +127,10 @@ public final class GesturesObserver implements GestureMatcher.StateChangeListene MotionEvent rawEvent, int policyFlags) { MotionEvent rawEvent, int policyFlags) { if (state == GestureMatcher.STATE_GESTURE_COMPLETED) { if (state == GestureMatcher.STATE_GESTURE_COMPLETED) { mListener.onGestureCompleted(gestureId, event, rawEvent, policyFlags); mListener.onGestureCompleted(gestureId, event, rawEvent, policyFlags); //Clear the states in onMotionEvent(). // Ideally we clear the states in onMotionEvent(), this case is for hold gestures. // If we clear before processing up event , then MultiTap matcher cancels the gesture // due to incorrect state. It ends up listener#onGestureCancelled is called even // the gesture is detected. if (!mProcessMotionEvent) { if (!mProcessMotionEvent) { clear(); clear(); } } Loading services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java +10 −4 Original line number Original line Diff line number Diff line Loading @@ -33,7 +33,7 @@ import java.lang.annotation.RetentionPolicy; class MagnificationGestureMatcher { class MagnificationGestureMatcher { private static final int GESTURE_BASE = 100; private static final int GESTURE_BASE = 100; public static final int GESTURE_TWO_FINGER_DOWN = GESTURE_BASE + 1; public static final int GESTURE_TWO_FINGERS_DOWN_OR_SWIPE = GESTURE_BASE + 1; public static final int GESTURE_SWIPE = GESTURE_BASE + 2; public static final int GESTURE_SWIPE = GESTURE_BASE + 2; public static final int GESTURE_SINGLE_TAP = GESTURE_BASE + 3; public static final int GESTURE_SINGLE_TAP = GESTURE_BASE + 3; public static final int GESTURE_SINGLE_TAP_AND_HOLD = GESTURE_BASE + 4; public static final int GESTURE_SINGLE_TAP_AND_HOLD = GESTURE_BASE + 4; Loading @@ -41,7 +41,7 @@ class MagnificationGestureMatcher { public static final int GESTURE_TRIPLE_TAP_AND_HOLD = GESTURE_BASE + 6; public static final int GESTURE_TRIPLE_TAP_AND_HOLD = GESTURE_BASE + 6; @IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = { @IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = { GESTURE_TWO_FINGER_DOWN, GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, GESTURE_SWIPE GESTURE_SWIPE }) }) @Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE) Loading @@ -57,8 +57,8 @@ class MagnificationGestureMatcher { switch (gestureId) { switch (gestureId) { case GESTURE_SWIPE: case GESTURE_SWIPE: return "GESTURE_SWIPE"; return "GESTURE_SWIPE"; case GESTURE_TWO_FINGER_DOWN: case GESTURE_TWO_FINGERS_DOWN_OR_SWIPE: return "GESTURE_TWO_FINGER_DOWN"; return "GESTURE_TWO_FINGERS_DOWN_OR_SWIPE"; case GESTURE_SINGLE_TAP: case GESTURE_SINGLE_TAP: return "GESTURE_SINGLE_TAP"; return "GESTURE_SINGLE_TAP"; case GESTURE_SINGLE_TAP_AND_HOLD: case GESTURE_SINGLE_TAP_AND_HOLD: Loading @@ -71,6 +71,12 @@ class MagnificationGestureMatcher { return "none"; return "none"; } } /** * @param context * @return the duration in milliseconds between the first tap's down event and * the second tap's down event to be considered that the user is going to performing * panning/scaling gesture. */ static int getMagnificationMultiTapTimeout(Context context) { static int getMagnificationMultiTapTimeout(Context context) { return ViewConfiguration.getDoubleTapTimeout() + context.getResources().getInteger( return ViewConfiguration.getDoubleTapTimeout() + context.getResources().getInteger( R.integer.config_screen_magnification_multi_tap_adjustment); R.integer.config_screen_magnification_multi_tap_adjustment); Loading services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -65,7 +65,7 @@ class MagnificationGesturesObserver implements GesturesObserver.Listener { * the last event before timeout. * the last event before timeout. * * * @see MagnificationGestureMatcher#GESTURE_SWIPE * @see MagnificationGestureMatcher#GESTURE_SWIPE * @see MagnificationGestureMatcher#GESTURE_TWO_FINGER_DOWN * @see MagnificationGestureMatcher#GESTURE_TWO_FINGERS_DOWN_OR_SWIPE */ */ void onGestureCompleted(@GestureId int gestureId, long lastDownEventTime, void onGestureCompleted(@GestureId int gestureId, long lastDownEventTime, List<MotionEventInfo> delayedEventQueue, MotionEvent event); List<MotionEventInfo> delayedEventQueue, MotionEvent event); Loading services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java +6 −1 Original line number Original line Diff line number Diff line Loading @@ -48,6 +48,11 @@ class SimpleSwipe extends GestureMatcher { cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags); cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags); } } @Override protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { cancelGesture(event, rawEvent, policyFlags); } @Override @Override protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) { protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (gestureMatched(event, rawEvent, policyFlags)) { if (gestureMatched(event, rawEvent, policyFlags)) { Loading @@ -65,7 +70,7 @@ class SimpleSwipe extends GestureMatcher { } } private boolean gestureMatched(MotionEvent event, MotionEvent rawEvent, int policyFlags) { private boolean gestureMatched(MotionEvent event, MotionEvent rawEvent, int policyFlags) { return mLastDown != null && (distance(mLastDown, event) >= mSwipeMinDistance); return mLastDown != null && (distance(mLastDown, event) > mSwipeMinDistance); } } @Override @Override Loading services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDown.java→services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDownOrSwipe.java +123 −0 Original line number Original line Diff line number Diff line /* /* * Copyright (C) 2020 The Android Open Source Project * Copyright (C) 2021 The Android Open Source Project * * * Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License. Loading @@ -16,42 +16,76 @@ package com.android.server.accessibility.magnification; package com.android.server.accessibility.magnification; import android.annotation.NonNull; import android.content.Context; import android.content.Context; import android.os.Handler; import android.os.Handler; import android.util.MathUtils; import android.view.MotionEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; import com.android.server.accessibility.gestures.GestureMatcher; import com.android.server.accessibility.gestures.GestureMatcher; /** /** * * This class is responsible for detecting that the user is using two fingers to perform * This class is responsible for matching two fingers down gestures. The gesture matching * swiping gestures or just stay pressed on the screen. The gesture matching result is determined * result is determined in a duration. * in a duration. */ */ final class TwoFingersDown extends GestureMatcher { final class TwoFingersDownOrSwipe extends GestureMatcher { private MotionEvent mLastDown; private final int mDoubleTapTimeout; private final int mDetectionDurationMillis; private final int mDetectionDurationMillis; private final int mSwipeMinDistance; private MotionEvent mFirstPointerDown; private MotionEvent mSecondPointerDown; TwoFingersDown(Context context) { TwoFingersDownOrSwipe(Context context) { super(MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN, super(MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, new Handler(context.getMainLooper()), null); new Handler(context.getMainLooper()), null); mDetectionDurationMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout( mDetectionDurationMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout( context); context); mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout(); mSwipeMinDistance = ViewConfiguration.get(context).getScaledTouchSlop(); } } @Override @Override protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { mLastDown = MotionEvent.obtain(event); mFirstPointerDown = MotionEvent.obtain(event); cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags); cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags); } } @Override @Override protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (mLastDown == null) { if (mFirstPointerDown == null) { cancelGesture(event, rawEvent, policyFlags); cancelGesture(event, rawEvent, policyFlags); } } if (event.getPointerCount() == 2) { mSecondPointerDown = MotionEvent.obtain(event); completeAfter(mDoubleTapTimeout, event, rawEvent, policyFlags); } else { cancelGesture(event, rawEvent, policyFlags); } } @Override protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (mFirstPointerDown == null || mSecondPointerDown == null) { return; } if (distance(mFirstPointerDown, /* move */ event) > mSwipeMinDistance) { completeGesture(event, rawEvent, policyFlags); return; } if (distance(mSecondPointerDown, /* move */ event) > mSwipeMinDistance) { // The second pointer is swiping. completeGesture(event, rawEvent, policyFlags); completeGesture(event, rawEvent, policyFlags); } } } @Override protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) { cancelGesture(event, rawEvent, policyFlags); } @Override @Override protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) { protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) { Loading @@ -60,9 +94,13 @@ final class TwoFingersDown extends GestureMatcher { @Override @Override public void clear() { public void clear() { if (mLastDown != null) { if (mFirstPointerDown != null) { mLastDown.recycle(); mFirstPointerDown.recycle(); mLastDown = null; mFirstPointerDown = null; } if (mSecondPointerDown != null) { mSecondPointerDown.recycle(); mSecondPointerDown = null; } } super.clear(); super.clear(); } } Loading @@ -71,4 +109,15 @@ final class TwoFingersDown extends GestureMatcher { protected String getGestureName() { protected String getGestureName() { return this.getClass().getSimpleName(); return this.getClass().getSimpleName(); } } private static double distance(@NonNull MotionEvent downEvent, @NonNull MotionEvent moveEvent) { final int downActionIndex = downEvent.getActionIndex(); final int downPointerId = downEvent.getPointerId(downActionIndex); final int moveActionIndex = moveEvent.findPointerIndex(downPointerId); if (moveActionIndex < 0) { return -1; } return MathUtils.dist(downEvent.getX(downActionIndex), downEvent.getY(downActionIndex), moveEvent.getX(moveActionIndex), moveEvent.getY(moveActionIndex)); } } } Loading
services/accessibility/java/com/android/server/accessibility/magnification/GesturesObserver.java +5 −3 Original line number Original line Diff line number Diff line Loading @@ -98,8 +98,7 @@ public final class GesturesObserver implements GestureMatcher.StateChangeListene } } mProcessMotionEvent = true; mProcessMotionEvent = true; for (int i = 0; i < mGestureMatchers.size(); i++) { for (int i = 0; i < mGestureMatchers.size(); i++) { final GestureMatcher matcher = final GestureMatcher matcher = mGestureMatchers.get(i); mGestureMatchers.get(i); matcher.onMotionEvent(event, rawEvent, policyFlags); matcher.onMotionEvent(event, rawEvent, policyFlags); if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) { if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) { clear(); clear(); Loading Loading @@ -128,7 +127,10 @@ public final class GesturesObserver implements GestureMatcher.StateChangeListene MotionEvent rawEvent, int policyFlags) { MotionEvent rawEvent, int policyFlags) { if (state == GestureMatcher.STATE_GESTURE_COMPLETED) { if (state == GestureMatcher.STATE_GESTURE_COMPLETED) { mListener.onGestureCompleted(gestureId, event, rawEvent, policyFlags); mListener.onGestureCompleted(gestureId, event, rawEvent, policyFlags); //Clear the states in onMotionEvent(). // Ideally we clear the states in onMotionEvent(), this case is for hold gestures. // If we clear before processing up event , then MultiTap matcher cancels the gesture // due to incorrect state. It ends up listener#onGestureCancelled is called even // the gesture is detected. if (!mProcessMotionEvent) { if (!mProcessMotionEvent) { clear(); clear(); } } Loading
services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureMatcher.java +10 −4 Original line number Original line Diff line number Diff line Loading @@ -33,7 +33,7 @@ import java.lang.annotation.RetentionPolicy; class MagnificationGestureMatcher { class MagnificationGestureMatcher { private static final int GESTURE_BASE = 100; private static final int GESTURE_BASE = 100; public static final int GESTURE_TWO_FINGER_DOWN = GESTURE_BASE + 1; public static final int GESTURE_TWO_FINGERS_DOWN_OR_SWIPE = GESTURE_BASE + 1; public static final int GESTURE_SWIPE = GESTURE_BASE + 2; public static final int GESTURE_SWIPE = GESTURE_BASE + 2; public static final int GESTURE_SINGLE_TAP = GESTURE_BASE + 3; public static final int GESTURE_SINGLE_TAP = GESTURE_BASE + 3; public static final int GESTURE_SINGLE_TAP_AND_HOLD = GESTURE_BASE + 4; public static final int GESTURE_SINGLE_TAP_AND_HOLD = GESTURE_BASE + 4; Loading @@ -41,7 +41,7 @@ class MagnificationGestureMatcher { public static final int GESTURE_TRIPLE_TAP_AND_HOLD = GESTURE_BASE + 6; public static final int GESTURE_TRIPLE_TAP_AND_HOLD = GESTURE_BASE + 6; @IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = { @IntDef(prefix = {"GESTURE_MAGNIFICATION_"}, value = { GESTURE_TWO_FINGER_DOWN, GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, GESTURE_SWIPE GESTURE_SWIPE }) }) @Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE) Loading @@ -57,8 +57,8 @@ class MagnificationGestureMatcher { switch (gestureId) { switch (gestureId) { case GESTURE_SWIPE: case GESTURE_SWIPE: return "GESTURE_SWIPE"; return "GESTURE_SWIPE"; case GESTURE_TWO_FINGER_DOWN: case GESTURE_TWO_FINGERS_DOWN_OR_SWIPE: return "GESTURE_TWO_FINGER_DOWN"; return "GESTURE_TWO_FINGERS_DOWN_OR_SWIPE"; case GESTURE_SINGLE_TAP: case GESTURE_SINGLE_TAP: return "GESTURE_SINGLE_TAP"; return "GESTURE_SINGLE_TAP"; case GESTURE_SINGLE_TAP_AND_HOLD: case GESTURE_SINGLE_TAP_AND_HOLD: Loading @@ -71,6 +71,12 @@ class MagnificationGestureMatcher { return "none"; return "none"; } } /** * @param context * @return the duration in milliseconds between the first tap's down event and * the second tap's down event to be considered that the user is going to performing * panning/scaling gesture. */ static int getMagnificationMultiTapTimeout(Context context) { static int getMagnificationMultiTapTimeout(Context context) { return ViewConfiguration.getDoubleTapTimeout() + context.getResources().getInteger( return ViewConfiguration.getDoubleTapTimeout() + context.getResources().getInteger( R.integer.config_screen_magnification_multi_tap_adjustment); R.integer.config_screen_magnification_multi_tap_adjustment); Loading
services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGesturesObserver.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -65,7 +65,7 @@ class MagnificationGesturesObserver implements GesturesObserver.Listener { * the last event before timeout. * the last event before timeout. * * * @see MagnificationGestureMatcher#GESTURE_SWIPE * @see MagnificationGestureMatcher#GESTURE_SWIPE * @see MagnificationGestureMatcher#GESTURE_TWO_FINGER_DOWN * @see MagnificationGestureMatcher#GESTURE_TWO_FINGERS_DOWN_OR_SWIPE */ */ void onGestureCompleted(@GestureId int gestureId, long lastDownEventTime, void onGestureCompleted(@GestureId int gestureId, long lastDownEventTime, List<MotionEventInfo> delayedEventQueue, MotionEvent event); List<MotionEventInfo> delayedEventQueue, MotionEvent event); Loading
services/accessibility/java/com/android/server/accessibility/magnification/SimpleSwipe.java +6 −1 Original line number Original line Diff line number Diff line Loading @@ -48,6 +48,11 @@ class SimpleSwipe extends GestureMatcher { cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags); cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags); } } @Override protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { cancelGesture(event, rawEvent, policyFlags); } @Override @Override protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) { protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (gestureMatched(event, rawEvent, policyFlags)) { if (gestureMatched(event, rawEvent, policyFlags)) { Loading @@ -65,7 +70,7 @@ class SimpleSwipe extends GestureMatcher { } } private boolean gestureMatched(MotionEvent event, MotionEvent rawEvent, int policyFlags) { private boolean gestureMatched(MotionEvent event, MotionEvent rawEvent, int policyFlags) { return mLastDown != null && (distance(mLastDown, event) >= mSwipeMinDistance); return mLastDown != null && (distance(mLastDown, event) > mSwipeMinDistance); } } @Override @Override Loading
services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDown.java→services/accessibility/java/com/android/server/accessibility/magnification/TwoFingersDownOrSwipe.java +123 −0 Original line number Original line Diff line number Diff line /* /* * Copyright (C) 2020 The Android Open Source Project * Copyright (C) 2021 The Android Open Source Project * * * Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License. Loading @@ -16,42 +16,76 @@ package com.android.server.accessibility.magnification; package com.android.server.accessibility.magnification; import android.annotation.NonNull; import android.content.Context; import android.content.Context; import android.os.Handler; import android.os.Handler; import android.util.MathUtils; import android.view.MotionEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; import com.android.server.accessibility.gestures.GestureMatcher; import com.android.server.accessibility.gestures.GestureMatcher; /** /** * * This class is responsible for detecting that the user is using two fingers to perform * This class is responsible for matching two fingers down gestures. The gesture matching * swiping gestures or just stay pressed on the screen. The gesture matching result is determined * result is determined in a duration. * in a duration. */ */ final class TwoFingersDown extends GestureMatcher { final class TwoFingersDownOrSwipe extends GestureMatcher { private MotionEvent mLastDown; private final int mDoubleTapTimeout; private final int mDetectionDurationMillis; private final int mDetectionDurationMillis; private final int mSwipeMinDistance; private MotionEvent mFirstPointerDown; private MotionEvent mSecondPointerDown; TwoFingersDown(Context context) { TwoFingersDownOrSwipe(Context context) { super(MagnificationGestureMatcher.GESTURE_TWO_FINGER_DOWN, super(MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, new Handler(context.getMainLooper()), null); new Handler(context.getMainLooper()), null); mDetectionDurationMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout( mDetectionDurationMillis = MagnificationGestureMatcher.getMagnificationMultiTapTimeout( context); context); mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout(); mSwipeMinDistance = ViewConfiguration.get(context).getScaledTouchSlop(); } } @Override @Override protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { mLastDown = MotionEvent.obtain(event); mFirstPointerDown = MotionEvent.obtain(event); cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags); cancelAfter(mDetectionDurationMillis, event, rawEvent, policyFlags); } } @Override @Override protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (mLastDown == null) { if (mFirstPointerDown == null) { cancelGesture(event, rawEvent, policyFlags); cancelGesture(event, rawEvent, policyFlags); } } if (event.getPointerCount() == 2) { mSecondPointerDown = MotionEvent.obtain(event); completeAfter(mDoubleTapTimeout, event, rawEvent, policyFlags); } else { cancelGesture(event, rawEvent, policyFlags); } } @Override protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (mFirstPointerDown == null || mSecondPointerDown == null) { return; } if (distance(mFirstPointerDown, /* move */ event) > mSwipeMinDistance) { completeGesture(event, rawEvent, policyFlags); return; } if (distance(mSecondPointerDown, /* move */ event) > mSwipeMinDistance) { // The second pointer is swiping. completeGesture(event, rawEvent, policyFlags); completeGesture(event, rawEvent, policyFlags); } } } @Override protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) { cancelGesture(event, rawEvent, policyFlags); } @Override @Override protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) { protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) { Loading @@ -60,9 +94,13 @@ final class TwoFingersDown extends GestureMatcher { @Override @Override public void clear() { public void clear() { if (mLastDown != null) { if (mFirstPointerDown != null) { mLastDown.recycle(); mFirstPointerDown.recycle(); mLastDown = null; mFirstPointerDown = null; } if (mSecondPointerDown != null) { mSecondPointerDown.recycle(); mSecondPointerDown = null; } } super.clear(); super.clear(); } } Loading @@ -71,4 +109,15 @@ final class TwoFingersDown extends GestureMatcher { protected String getGestureName() { protected String getGestureName() { return this.getClass().getSimpleName(); return this.getClass().getSimpleName(); } } private static double distance(@NonNull MotionEvent downEvent, @NonNull MotionEvent moveEvent) { final int downActionIndex = downEvent.getActionIndex(); final int downPointerId = downEvent.getPointerId(downActionIndex); final int moveActionIndex = moveEvent.findPointerIndex(downPointerId); if (moveActionIndex < 0) { return -1; } return MathUtils.dist(downEvent.getX(downActionIndex), downEvent.getY(downActionIndex), moveEvent.getX(moveActionIndex), moveEvent.getY(moveActionIndex)); } } }