Some cleanup for SwipeDetector.
It is now organized as follows: - private constants - public constants - private final fields - private variable fields - constructors - public methods - private methods - public interface/abstract class This is intended to be a functional no-op. Bug: 141939911 Change-Id: Iad5a9b3b73b35641f8a4f1d52ada6adef3825c47 Tested: Built and sanity checked manually.
This commit is contained in:
parent
4c420047cf
commit
769c795edf
|
@ -16,6 +16,8 @@
|
|||
|
||||
package com.android.launcher3.notification;
|
||||
|
||||
import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
|
@ -33,8 +35,6 @@ import com.android.launcher3.util.Themes;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
|
||||
|
||||
/**
|
||||
* Utility class to manage notification UI
|
||||
*/
|
||||
|
|
|
@ -24,12 +24,11 @@ import android.view.MotionEvent;
|
|||
import android.view.VelocityTracker;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
/**
|
||||
* One dimensional scroll/drag/swipe gesture detector.
|
||||
*
|
||||
|
@ -41,47 +40,14 @@ public class SwipeDetector {
|
|||
|
||||
private static final boolean DBG = false;
|
||||
private static final String TAG = "SwipeDetector";
|
||||
private static final float ANIMATION_DURATION = 1200;
|
||||
/** The minimum release velocity in pixels per millisecond that triggers fling.*/
|
||||
private static final float RELEASE_VELOCITY_PX_MS = 1.0f;
|
||||
|
||||
private int mScrollConditions;
|
||||
public static final int DIRECTION_POSITIVE = 1 << 0;
|
||||
public static final int DIRECTION_NEGATIVE = 1 << 1;
|
||||
public static final int DIRECTION_BOTH = DIRECTION_NEGATIVE | DIRECTION_POSITIVE;
|
||||
|
||||
private static final float ANIMATION_DURATION = 1200;
|
||||
|
||||
protected int mActivePointerId = INVALID_POINTER_ID;
|
||||
|
||||
/**
|
||||
* The minimum release velocity in pixels per millisecond that triggers fling..
|
||||
*/
|
||||
public static final float RELEASE_VELOCITY_PX_MS = 1.0f;
|
||||
|
||||
/* Scroll state, this is set to true during dragging and animation. */
|
||||
private ScrollState mState = ScrollState.IDLE;
|
||||
|
||||
enum ScrollState {
|
||||
IDLE,
|
||||
DRAGGING, // onDragStart, onDrag
|
||||
SETTLING // onDragEnd
|
||||
}
|
||||
|
||||
public static abstract class Direction {
|
||||
|
||||
abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint,
|
||||
boolean isRtl);
|
||||
|
||||
/**
|
||||
* Distance in pixels a touch can wander before we think the user is scrolling.
|
||||
*/
|
||||
abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
|
||||
|
||||
abstract float getVelocity(VelocityTracker tracker, boolean isRtl);
|
||||
|
||||
abstract boolean isPositive(float displacement);
|
||||
|
||||
abstract boolean isNegative(float displacement);
|
||||
}
|
||||
|
||||
public static final Direction VERTICAL = new Direction() {
|
||||
|
||||
@Override
|
||||
|
@ -150,35 +116,54 @@ public class SwipeDetector {
|
|||
}
|
||||
};
|
||||
|
||||
//------------------- ScrollState transition diagram -----------------------------------
|
||||
//
|
||||
// IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
|
||||
// DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
|
||||
// SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
|
||||
// SETTLING -> (View settled) -> IDLE
|
||||
private final PointF mDownPos = new PointF();
|
||||
private final PointF mLastPos = new PointF();
|
||||
private final Direction mDir;
|
||||
private final boolean mIsRtl;
|
||||
private final float mTouchSlop;
|
||||
private final float mMaxVelocity;
|
||||
/* Client of this gesture detector can register a callback. */
|
||||
private final Listener mListener;
|
||||
|
||||
private void setState(ScrollState newState) {
|
||||
if (DBG) {
|
||||
Log.d(TAG, "setState:" + mState + "->" + newState);
|
||||
}
|
||||
// onDragStart and onDragEnd is reported ONLY on state transition
|
||||
if (newState == ScrollState.DRAGGING) {
|
||||
initializeDragging();
|
||||
if (mState == ScrollState.IDLE) {
|
||||
reportDragStart(false /* recatch */);
|
||||
} else if (mState == ScrollState.SETTLING) {
|
||||
reportDragStart(true /* recatch */);
|
||||
}
|
||||
}
|
||||
if (newState == ScrollState.SETTLING) {
|
||||
reportDragEnd();
|
||||
}
|
||||
private int mActivePointerId = INVALID_POINTER_ID;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private float mLastDisplacement;
|
||||
private float mDisplacement;
|
||||
private float mSubtractDisplacement;
|
||||
private boolean mIgnoreSlopWhenSettling;
|
||||
private int mScrollDirections;
|
||||
private ScrollState mState = ScrollState.IDLE;
|
||||
|
||||
mState = newState;
|
||||
private enum ScrollState {
|
||||
IDLE,
|
||||
DRAGGING, // onDragStart, onDrag
|
||||
SETTLING // onDragEnd
|
||||
}
|
||||
|
||||
public boolean isDraggingOrSettling() {
|
||||
return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
|
||||
public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
|
||||
this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected SwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
|
||||
@NonNull Direction dir, boolean isRtl) {
|
||||
mListener = l;
|
||||
mDir = dir;
|
||||
mIsRtl = isRtl;
|
||||
mTouchSlop = config.getScaledTouchSlop();
|
||||
mMaxVelocity = config.getScaledMaximumFlingVelocity();
|
||||
}
|
||||
|
||||
public static long calculateDuration(float velocity, float progressNeeded) {
|
||||
// TODO: make these values constants after tuning.
|
||||
float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
|
||||
float travelDistance = Math.max(0.2f, progressNeeded);
|
||||
long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
|
||||
if (DBG) {
|
||||
Log.d(TAG, String.format(
|
||||
"calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
public int getDownX() {
|
||||
|
@ -203,73 +188,31 @@ public class SwipeDetector {
|
|||
return mState == ScrollState.DRAGGING;
|
||||
}
|
||||
|
||||
private final PointF mDownPos = new PointF();
|
||||
private final PointF mLastPos = new PointF();
|
||||
private final Direction mDir;
|
||||
private final boolean mIsRtl;
|
||||
|
||||
private final float mTouchSlop;
|
||||
private final float mMaxVelocity;
|
||||
|
||||
/* Client of this gesture detector can register a callback. */
|
||||
private final Listener mListener;
|
||||
|
||||
private VelocityTracker mVelocityTracker;
|
||||
|
||||
private float mLastDisplacement;
|
||||
private float mDisplacement;
|
||||
|
||||
private float mSubtractDisplacement;
|
||||
private boolean mIgnoreSlopWhenSettling;
|
||||
|
||||
public interface Listener {
|
||||
void onDragStart(boolean start);
|
||||
|
||||
boolean onDrag(float displacement);
|
||||
|
||||
default boolean onDrag(float displacement, MotionEvent event) {
|
||||
return onDrag(displacement);
|
||||
}
|
||||
|
||||
void onDragEnd(float velocity, boolean fling);
|
||||
}
|
||||
|
||||
public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
|
||||
this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected SwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
|
||||
@NonNull Direction dir, boolean isRtl) {
|
||||
mListener = l;
|
||||
mDir = dir;
|
||||
mIsRtl = isRtl;
|
||||
mTouchSlop = config.getScaledTouchSlop();
|
||||
mMaxVelocity = config.getScaledMaximumFlingVelocity();
|
||||
public boolean isDraggingOrSettling() {
|
||||
return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
|
||||
}
|
||||
|
||||
public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
|
||||
mScrollConditions = scrollDirectionFlags;
|
||||
mScrollDirections = scrollDirectionFlags;
|
||||
mIgnoreSlopWhenSettling = ignoreSlop;
|
||||
}
|
||||
|
||||
public int getScrollDirections() {
|
||||
return mScrollConditions;
|
||||
return mScrollDirections;
|
||||
}
|
||||
|
||||
private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) {
|
||||
// reject cases where the angle or slop condition is not met.
|
||||
if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop)
|
||||
> Math.abs(mDisplacement)) {
|
||||
return false;
|
||||
}
|
||||
public void finishedScrolling() {
|
||||
setState(ScrollState.IDLE);
|
||||
}
|
||||
|
||||
// Check if the client is interested in scroll in current direction.
|
||||
if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(mDisplacement)) ||
|
||||
((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDir.isPositive(mDisplacement))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
/**
|
||||
* Returns if the start drag was towards the positive direction or negative.
|
||||
*
|
||||
* @see #setDetectableScrollConditions(int, boolean)
|
||||
* @see #DIRECTION_BOTH
|
||||
*/
|
||||
public boolean wasInitialTouchPositive() {
|
||||
return mDir.isPositive(mSubtractDisplacement);
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
|
@ -338,16 +281,50 @@ public class SwipeDetector {
|
|||
return true;
|
||||
}
|
||||
|
||||
public void finishedScrolling() {
|
||||
setState(ScrollState.IDLE);
|
||||
//------------------- ScrollState transition diagram -----------------------------------
|
||||
//
|
||||
// IDLE -> (mDisplacement > mTouchSlop) -> DRAGGING
|
||||
// DRAGGING -> (MotionEvent#ACTION_UP, MotionEvent#ACTION_CANCEL) -> SETTLING
|
||||
// SETTLING -> (MotionEvent#ACTION_DOWN) -> DRAGGING
|
||||
// SETTLING -> (View settled) -> IDLE
|
||||
|
||||
private void setState(ScrollState newState) {
|
||||
if (DBG) {
|
||||
Log.d(TAG, "setState:" + mState + "->" + newState);
|
||||
}
|
||||
// onDragStart and onDragEnd is reported ONLY on state transition
|
||||
if (newState == ScrollState.DRAGGING) {
|
||||
initializeDragging();
|
||||
if (mState == ScrollState.IDLE) {
|
||||
reportDragStart(false /* recatch */);
|
||||
} else if (mState == ScrollState.SETTLING) {
|
||||
reportDragStart(true /* recatch */);
|
||||
}
|
||||
}
|
||||
if (newState == ScrollState.SETTLING) {
|
||||
reportDragEnd();
|
||||
}
|
||||
|
||||
mState = newState;
|
||||
}
|
||||
|
||||
private boolean reportDragStart(boolean recatch) {
|
||||
private boolean shouldScrollStart(MotionEvent ev, int pointerIndex) {
|
||||
// reject cases where the angle or slop condition is not met.
|
||||
if (Math.max(mDir.getActiveTouchSlop(ev, pointerIndex, mDownPos), mTouchSlop)
|
||||
> Math.abs(mDisplacement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the client is interested in scroll in current direction.
|
||||
return ((mScrollDirections & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(mDisplacement))
|
||||
|| ((mScrollDirections & DIRECTION_POSITIVE) > 0 && mDir.isPositive(mDisplacement));
|
||||
}
|
||||
|
||||
private void reportDragStart(boolean recatch) {
|
||||
mListener.onDragStart(!recatch);
|
||||
if (DBG) {
|
||||
Log.d(TAG, "onDragStart recatch:" + recatch);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void initializeDragging() {
|
||||
|
@ -361,26 +338,15 @@ public class SwipeDetector {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the start drag was towards the positive direction or negative.
|
||||
*
|
||||
* @see #setDetectableScrollConditions(int, boolean)
|
||||
* @see #DIRECTION_BOTH
|
||||
*/
|
||||
public boolean wasInitialTouchPositive() {
|
||||
return mDir.isPositive(mSubtractDisplacement);
|
||||
}
|
||||
|
||||
private boolean reportDragging(MotionEvent event) {
|
||||
private void reportDragging(MotionEvent event) {
|
||||
if (mDisplacement != mLastDisplacement) {
|
||||
if (DBG) {
|
||||
Log.d(TAG, String.format("onDrag disp=%.1f", mDisplacement));
|
||||
}
|
||||
|
||||
mLastDisplacement = mDisplacement;
|
||||
return mListener.onDrag(mDisplacement - mSubtractDisplacement, event);
|
||||
mListener.onDrag(mDisplacement - mSubtractDisplacement, event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void reportDragEnd() {
|
||||
|
@ -394,14 +360,33 @@ public class SwipeDetector {
|
|||
mListener.onDragEnd(velocity, Math.abs(velocity) > RELEASE_VELOCITY_PX_MS);
|
||||
}
|
||||
|
||||
public static long calculateDuration(float velocity, float progressNeeded) {
|
||||
// TODO: make these values constants after tuning.
|
||||
float velocityDivisor = Math.max(2f, Math.abs(0.5f * velocity));
|
||||
float travelDistance = Math.max(0.2f, progressNeeded);
|
||||
long duration = (long) Math.max(100, ANIMATION_DURATION / velocityDivisor * travelDistance);
|
||||
if (DBG) {
|
||||
Log.d(TAG, String.format("calculateDuration=%d, v=%f, d=%f", duration, velocity, progressNeeded));
|
||||
/** Listener to receive updates on the swipe. */
|
||||
public interface Listener {
|
||||
void onDragStart(boolean start);
|
||||
|
||||
boolean onDrag(float displacement);
|
||||
|
||||
default boolean onDrag(float displacement, MotionEvent event) {
|
||||
return onDrag(displacement);
|
||||
}
|
||||
return duration;
|
||||
|
||||
void onDragEnd(float velocity, boolean fling);
|
||||
}
|
||||
|
||||
public abstract static class Direction {
|
||||
|
||||
abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint,
|
||||
boolean isRtl);
|
||||
|
||||
/**
|
||||
* Distance in pixels a touch can wander before we think the user is scrolling.
|
||||
*/
|
||||
abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
|
||||
|
||||
abstract float getVelocity(VelocityTracker tracker, boolean isRtl);
|
||||
|
||||
abstract boolean isPositive(float displacement);
|
||||
|
||||
abstract boolean isNegative(float displacement);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ import static org.mockito.Mockito.verify;
|
|||
import android.util.Log;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.launcher3.testcomponent.TouchEventGenerator;
|
||||
|
||||
import org.junit.Before;
|
||||
|
@ -33,10 +37,6 @@ import org.junit.runner.RunWith;
|
|||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SwipeDetectorTest {
|
||||
|
|
Loading…
Reference in New Issue