Refactor SwipeDetector to track both axes
Existing clients now use the SingleAxisSwipeDetector subclass. A followup CL will add BothAxesSwipeDetector, whose first client will be the quick switch from home controller. Bug: 126596417 Change-Id: I54c71088cfe99ff28cdc719a1eb7a7d06ac95d2d
This commit is contained in:
parent
f6d504d4cb
commit
5aeb3b84b8
|
@ -173,7 +173,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd(float velocity, boolean fling) {
|
||||
public void onDragEnd(float velocity) {
|
||||
if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
|
||||
if (mPeekAnim != null) {
|
||||
mPeekAnim.cancel();
|
||||
|
@ -196,7 +196,7 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
|
|||
});
|
||||
overviewAnim.start();
|
||||
} else {
|
||||
super.onDragEnd(velocity, fling);
|
||||
super.onDragEnd(velocity);
|
||||
}
|
||||
|
||||
View searchView = mLauncher.getAppsView().getSearchView();
|
||||
|
|
|
@ -43,7 +43,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
|
|||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
|
@ -52,12 +52,13 @@ import com.android.quickstep.views.RecentsView;
|
|||
/**
|
||||
* Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps.
|
||||
*/
|
||||
public class NavBarToHomeTouchController implements TouchController, SwipeDetector.Listener {
|
||||
public class NavBarToHomeTouchController implements TouchController,
|
||||
SingleAxisSwipeDetector.Listener {
|
||||
|
||||
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;
|
||||
|
||||
private final Launcher mLauncher;
|
||||
private final SwipeDetector mSwipeDetector;
|
||||
private final SingleAxisSwipeDetector mSwipeDetector;
|
||||
private final float mPullbackDistance;
|
||||
|
||||
private boolean mNoIntercept;
|
||||
|
@ -67,7 +68,8 @@ public class NavBarToHomeTouchController implements TouchController, SwipeDetect
|
|||
|
||||
public NavBarToHomeTouchController(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
mSwipeDetector = new SwipeDetector(mLauncher, this, SwipeDetector.VERTICAL);
|
||||
mSwipeDetector = new SingleAxisSwipeDetector(mLauncher, this,
|
||||
SingleAxisSwipeDetector.VERTICAL);
|
||||
mPullbackDistance = mLauncher.getResources().getDimension(R.dimen.home_pullback_distance);
|
||||
}
|
||||
|
||||
|
@ -79,7 +81,8 @@ public class NavBarToHomeTouchController implements TouchController, SwipeDetect
|
|||
if (mNoIntercept) {
|
||||
return false;
|
||||
}
|
||||
mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
|
||||
mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_POSITIVE,
|
||||
false /* ignoreSlop */);
|
||||
}
|
||||
|
||||
if (mNoIntercept) {
|
||||
|
@ -173,7 +176,8 @@ public class NavBarToHomeTouchController implements TouchController, SwipeDetect
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd(float velocity, boolean fling) {
|
||||
public void onDragEnd(float velocity) {
|
||||
boolean fling = mSwipeDetector.isFling(velocity);
|
||||
final int logAction = fling ? Touch.FLING : Touch.SWIPE;
|
||||
float progress = mCurrentAnimation.getProgressFraction();
|
||||
float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(progress);
|
||||
|
|
|
@ -42,7 +42,7 @@ import com.android.launcher3.LauncherStateManager;
|
|||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.touch.AbstractStateChangeTouchController;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
|
@ -59,10 +59,10 @@ public class QuickSwitchTouchController extends AbstractStateChangeTouchControll
|
|||
private @Nullable TaskView mTaskToLaunch;
|
||||
|
||||
public QuickSwitchTouchController(Launcher launcher) {
|
||||
this(launcher, SwipeDetector.HORIZONTAL);
|
||||
this(launcher, SingleAxisSwipeDetector.HORIZONTAL);
|
||||
}
|
||||
|
||||
protected QuickSwitchTouchController(Launcher l, SwipeDetector.Direction dir) {
|
||||
protected QuickSwitchTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
|
||||
super(l, dir);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,9 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
|
|||
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
|
||||
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
|
||||
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
|
||||
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
|
||||
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
|
||||
|
||||
import android.animation.Animator;
|
||||
|
@ -32,7 +35,8 @@ import com.android.launcher3.LauncherAnimUtils;
|
|||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.launcher3.touch.BaseSwipeDetector;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
import com.android.launcher3.util.FlingBlockCheck;
|
||||
import com.android.launcher3.util.PendingAnimation;
|
||||
|
@ -46,15 +50,14 @@ import com.android.quickstep.views.TaskView;
|
|||
* Touch controller for handling task view card swipes
|
||||
*/
|
||||
public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
||||
extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener {
|
||||
|
||||
private static final String TAG = "OverviewSwipeController";
|
||||
extends AnimatorListenerAdapter implements TouchController,
|
||||
SingleAxisSwipeDetector.Listener {
|
||||
|
||||
// Progress after which the transition is assumed to be a success in case user does not fling
|
||||
public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
|
||||
|
||||
protected final T mActivity;
|
||||
private final SwipeDetector mDetector;
|
||||
private final SingleAxisSwipeDetector mDetector;
|
||||
private final RecentsView mRecentsView;
|
||||
private final int[] mTempCords = new int[2];
|
||||
|
||||
|
@ -74,7 +77,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
|||
public TaskViewTouchController(T activity) {
|
||||
mActivity = activity;
|
||||
mRecentsView = activity.getOverviewPanel();
|
||||
mDetector = new SwipeDetector(activity, this, SwipeDetector.VERTICAL);
|
||||
mDetector = new SingleAxisSwipeDetector(activity, this, SingleAxisSwipeDetector.VERTICAL);
|
||||
}
|
||||
|
||||
private boolean canInterceptTouch() {
|
||||
|
@ -113,7 +116,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
|||
int directionsToDetectScroll = 0;
|
||||
boolean ignoreSlopWhenSettling = false;
|
||||
if (mCurrentAnimation != null) {
|
||||
directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
|
||||
directionsToDetectScroll = DIRECTION_BOTH;
|
||||
ignoreSlopWhenSettling = true;
|
||||
} else {
|
||||
mTaskBeingDragged = null;
|
||||
|
@ -126,12 +129,12 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
|||
if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
|
||||
// Don't allow swipe down to open if we don't support swipe up
|
||||
// to enter overview.
|
||||
directionsToDetectScroll = SwipeDetector.DIRECTION_POSITIVE;
|
||||
directionsToDetectScroll = DIRECTION_POSITIVE;
|
||||
} else {
|
||||
// The task can be dragged up to dismiss it,
|
||||
// and down to open if it's the current page.
|
||||
directionsToDetectScroll = i == mRecentsView.getCurrentPage()
|
||||
? SwipeDetector.DIRECTION_BOTH : SwipeDetector.DIRECTION_POSITIVE;
|
||||
? DIRECTION_BOTH : DIRECTION_POSITIVE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -165,8 +168,8 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
|||
return;
|
||||
}
|
||||
int scrollDirections = mDetector.getScrollDirections();
|
||||
if (goingUp && ((scrollDirections & SwipeDetector.DIRECTION_POSITIVE) == 0)
|
||||
|| !goingUp && ((scrollDirections & SwipeDetector.DIRECTION_NEGATIVE) == 0)) {
|
||||
if (goingUp && ((scrollDirections & DIRECTION_POSITIVE) == 0)
|
||||
|| !goingUp && ((scrollDirections & DIRECTION_NEGATIVE) == 0)) {
|
||||
// Trying to re-init in an unsupported direction.
|
||||
return;
|
||||
}
|
||||
|
@ -243,7 +246,8 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd(float velocity, boolean fling) {
|
||||
public void onDragEnd(float velocity) {
|
||||
boolean fling = mDetector.isFling(velocity);
|
||||
final boolean goingToEnd;
|
||||
final int logAction;
|
||||
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
|
||||
|
@ -260,7 +264,7 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
|
|||
logAction = Touch.SWIPE;
|
||||
goingToEnd = interpolatedProgress > SUCCESS_TRANSITION_PROGRESS;
|
||||
}
|
||||
long animationDuration = SwipeDetector.calculateDuration(
|
||||
long animationDuration = BaseSwipeDetector.calculateDuration(
|
||||
velocity, goingToEnd ? (1 - progress) : progress);
|
||||
if (blockedFling && !goingToEnd) {
|
||||
animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
|
||||
|
|
|
@ -17,12 +17,12 @@ package com.android.launcher3.uioverrides.touchcontrollers;
|
|||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
|
||||
public class TransposedQuickSwitchTouchController extends QuickSwitchTouchController {
|
||||
|
||||
public TransposedQuickSwitchTouchController(Launcher launcher) {
|
||||
super(launcher, SwipeDetector.VERTICAL);
|
||||
super(launcher, SingleAxisSwipeDetector.VERTICAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -11,7 +11,7 @@ import com.android.launcher3.Launcher;
|
|||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherStateManager.AnimationComponents;
|
||||
import com.android.launcher3.touch.AbstractStateChangeTouchController;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
import com.android.quickstep.RecentsModel;
|
||||
|
@ -24,7 +24,7 @@ public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchContro
|
|||
private static final String TAG = "LandscapeEdgeSwipeCtrl";
|
||||
|
||||
public LandscapeEdgeSwipeController(Launcher l) {
|
||||
super(l, SwipeDetector.HORIZONTAL);
|
||||
super(l, SingleAxisSwipeDetector.HORIZONTAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -43,7 +43,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
|
|||
import com.android.launcher3.anim.AnimatorSetBuilder;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.touch.AbstractStateChangeTouchController;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
import com.android.launcher3.uioverrides.states.OverviewState;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
|
@ -79,7 +79,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
|
|||
private boolean mFinishFastOnSecondTouch;
|
||||
|
||||
public PortraitStatesTouchController(Launcher l, boolean allowDragToOverview) {
|
||||
super(l, SwipeDetector.VERTICAL);
|
||||
super(l, SingleAxisSwipeDetector.VERTICAL);
|
||||
mOverviewPortraitStateTouchHelper = new PortraitOverviewStateTouchHelper(l);
|
||||
mAllowDragToOverview = allowDragToOverview;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package com.android.launcher3.notification;
|
||||
|
||||
import static com.android.launcher3.touch.SwipeDetector.HORIZONTAL;
|
||||
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.content.Context;
|
||||
|
@ -30,7 +30,7 @@ import android.widget.TextView;
|
|||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.graphics.IconPalette;
|
||||
import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -49,7 +49,7 @@ public class NotificationItemView {
|
|||
private final TextView mHeaderCount;
|
||||
private final NotificationMainView mMainView;
|
||||
private final NotificationFooterLayout mFooter;
|
||||
private final SwipeDetector mSwipeDetector;
|
||||
private final SingleAxisSwipeDetector mSwipeDetector;
|
||||
private final View mIconView;
|
||||
|
||||
private final View mHeader;
|
||||
|
@ -74,8 +74,8 @@ public class NotificationItemView {
|
|||
mHeader = container.findViewById(R.id.header);
|
||||
mDivider = container.findViewById(R.id.divider);
|
||||
|
||||
mSwipeDetector = new SwipeDetector(mContext, mMainView, HORIZONTAL);
|
||||
mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
|
||||
mSwipeDetector = new SingleAxisSwipeDetector(mContext, mMainView, HORIZONTAL);
|
||||
mSwipeDetector.setDetectableScrollConditions(SingleAxisSwipeDetector.DIRECTION_BOTH, false);
|
||||
mMainView.setSwipeDetector(mSwipeDetector);
|
||||
mFooter.setContainer(this);
|
||||
}
|
||||
|
|
|
@ -38,8 +38,9 @@ import com.android.launcher3.ItemInfo;
|
|||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.touch.BaseSwipeDetector;
|
||||
import com.android.launcher3.touch.OverScroll;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
||||
|
@ -48,7 +49,7 @@ import com.android.launcher3.util.Themes;
|
|||
* e.g. icon + title + text.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.N)
|
||||
public class NotificationMainView extends FrameLayout implements SwipeDetector.Listener {
|
||||
public class NotificationMainView extends FrameLayout implements SingleAxisSwipeDetector.Listener {
|
||||
|
||||
private static FloatProperty<NotificationMainView> CONTENT_TRANSLATION =
|
||||
new FloatProperty<NotificationMainView>("contentTranslation") {
|
||||
|
@ -75,7 +76,7 @@ public class NotificationMainView extends FrameLayout implements SwipeDetector.L
|
|||
private TextView mTextView;
|
||||
private View mIconView;
|
||||
|
||||
private SwipeDetector mSwipeDetector;
|
||||
private SingleAxisSwipeDetector mSwipeDetector;
|
||||
|
||||
public NotificationMainView(Context context) {
|
||||
this(context, null, 0);
|
||||
|
@ -107,7 +108,7 @@ public class NotificationMainView extends FrameLayout implements SwipeDetector.L
|
|||
mIconView = findViewById(R.id.popup_item_icon);
|
||||
}
|
||||
|
||||
public void setSwipeDetector(SwipeDetector swipeDetector) {
|
||||
public void setSwipeDetector(SingleAxisSwipeDetector swipeDetector) {
|
||||
mSwipeDetector = swipeDetector;
|
||||
}
|
||||
|
||||
|
@ -173,7 +174,7 @@ public class NotificationMainView extends FrameLayout implements SwipeDetector.L
|
|||
LauncherLogProto.ItemType.NOTIFICATION);
|
||||
}
|
||||
|
||||
// SwipeDetector.Listener's
|
||||
// SingleAxisSwipeDetector.Listener's
|
||||
@Override
|
||||
public void onDragStart(boolean start) { }
|
||||
|
||||
|
@ -187,7 +188,7 @@ public class NotificationMainView extends FrameLayout implements SwipeDetector.L
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd(float velocity, boolean fling) {
|
||||
public void onDragEnd(float velocity) {
|
||||
final boolean willExit;
|
||||
final float endTranslation;
|
||||
final float startTranslation = mTextAndBackground.getTranslationX();
|
||||
|
@ -195,7 +196,7 @@ public class NotificationMainView extends FrameLayout implements SwipeDetector.L
|
|||
if (!canChildBeDismissed()) {
|
||||
willExit = false;
|
||||
endTranslation = 0;
|
||||
} else if (fling) {
|
||||
} else if (mSwipeDetector.isFling(velocity)) {
|
||||
willExit = true;
|
||||
endTranslation = velocity < 0 ? - getWidth() : getWidth();
|
||||
} else if (Math.abs(startTranslation) > getWidth() / 2) {
|
||||
|
@ -206,7 +207,7 @@ public class NotificationMainView extends FrameLayout implements SwipeDetector.L
|
|||
endTranslation = 0;
|
||||
}
|
||||
|
||||
long duration = SwipeDetector.calculateDuration(velocity,
|
||||
long duration = BaseSwipeDetector.calculateDuration(velocity,
|
||||
(endTranslation - startTranslation) / getWidth());
|
||||
|
||||
mContentTranslateAnimator.removeAllListeners();
|
||||
|
|
|
@ -53,7 +53,7 @@ import com.android.launcher3.util.TouchController;
|
|||
* TouchController for handling state changes
|
||||
*/
|
||||
public abstract class AbstractStateChangeTouchController
|
||||
implements TouchController, SwipeDetector.Listener {
|
||||
implements TouchController, SingleAxisSwipeDetector.Listener {
|
||||
|
||||
// Progress after which the transition is assumed to be a success in case user does not fling
|
||||
public static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
|
||||
|
@ -65,8 +65,8 @@ public abstract class AbstractStateChangeTouchController
|
|||
protected final long ATOMIC_DURATION = getAtomicDuration();
|
||||
|
||||
protected final Launcher mLauncher;
|
||||
protected final SwipeDetector mDetector;
|
||||
protected final SwipeDetector.Direction mSwipeDirection;
|
||||
protected final SingleAxisSwipeDetector mDetector;
|
||||
protected final SingleAxisSwipeDetector.Direction mSwipeDirection;
|
||||
|
||||
private boolean mNoIntercept;
|
||||
private boolean mIsLogContainerSet;
|
||||
|
@ -101,9 +101,9 @@ public abstract class AbstractStateChangeTouchController
|
|||
|
||||
private float mAtomicComponentsStartProgress;
|
||||
|
||||
public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
|
||||
public AbstractStateChangeTouchController(Launcher l, SingleAxisSwipeDetector.Direction dir) {
|
||||
mLauncher = l;
|
||||
mDetector = new SwipeDetector(l, this, dir);
|
||||
mDetector = new SingleAxisSwipeDetector(l, this, dir);
|
||||
mSwipeDirection = dir;
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ public abstract class AbstractStateChangeTouchController
|
|||
boolean ignoreSlopWhenSettling = false;
|
||||
|
||||
if (mCurrentAnimation != null) {
|
||||
directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
|
||||
directionsToDetectScroll = SingleAxisSwipeDetector.DIRECTION_BOTH;
|
||||
ignoreSlopWhenSettling = true;
|
||||
} else {
|
||||
directionsToDetectScroll = getSwipeDirection();
|
||||
|
@ -152,10 +152,10 @@ public abstract class AbstractStateChangeTouchController
|
|||
LauncherState fromState = mLauncher.getStateManager().getState();
|
||||
int swipeDirection = 0;
|
||||
if (getTargetState(fromState, true /* isDragTowardPositive */) != fromState) {
|
||||
swipeDirection |= SwipeDetector.DIRECTION_POSITIVE;
|
||||
swipeDirection |= SingleAxisSwipeDetector.DIRECTION_POSITIVE;
|
||||
}
|
||||
if (getTargetState(fromState, false /* isDragTowardPositive */) != fromState) {
|
||||
swipeDirection |= SwipeDetector.DIRECTION_NEGATIVE;
|
||||
swipeDirection |= SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
|
||||
}
|
||||
return swipeDirection;
|
||||
}
|
||||
|
@ -369,7 +369,8 @@ public abstract class AbstractStateChangeTouchController
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd(float velocity, boolean fling) {
|
||||
public void onDragEnd(float velocity) {
|
||||
boolean fling = mDetector.isFling(velocity);
|
||||
final int logAction = fling ? Touch.FLING : Touch.SWIPE;
|
||||
|
||||
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
|
||||
|
@ -406,7 +407,7 @@ public abstract class AbstractStateChangeTouchController
|
|||
} else {
|
||||
startProgress = Utilities.boundToRange(progress
|
||||
+ velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
|
||||
duration = SwipeDetector.calculateDuration(velocity,
|
||||
duration = BaseSwipeDetector.calculateDuration(velocity,
|
||||
endProgress - Math.max(progress, 0)) * durationMultiplier;
|
||||
}
|
||||
} else {
|
||||
|
@ -424,7 +425,7 @@ public abstract class AbstractStateChangeTouchController
|
|||
} else {
|
||||
startProgress = Utilities.boundToRange(progress
|
||||
+ velocity * getSingleFrameMs(mLauncher) * mProgressMultiplier, 0f, 1f);
|
||||
duration = SwipeDetector.calculateDuration(velocity,
|
||||
duration = BaseSwipeDetector.calculateDuration(velocity,
|
||||
Math.min(progress, 1) - endProgress) * durationMultiplier;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ public class AllAppsSwipeController extends AbstractStateChangeTouchController {
|
|||
private MotionEvent mTouchDownEvent;
|
||||
|
||||
public AllAppsSwipeController(Launcher l) {
|
||||
super(l, SwipeDetector.VERTICAL);
|
||||
super(l, SingleAxisSwipeDetector.VERTICAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,7 +17,6 @@ package com.android.launcher3.touch;
|
|||
|
||||
import static android.view.MotionEvent.INVALID_POINTER_ID;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
@ -25,133 +24,50 @@ import android.view.VelocityTracker;
|
|||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
/**
|
||||
* One dimensional scroll/drag/swipe gesture detector.
|
||||
* Scroll/drag/swipe gesture detector.
|
||||
*
|
||||
* Definition of swipe is different from android system in that this detector handles
|
||||
* 'swipe to dismiss', 'swiping up/down a container' but also keeps scrolling state before
|
||||
* swipe action happens
|
||||
* swipe action happens.
|
||||
*
|
||||
* @see SingleAxisSwipeDetector
|
||||
*/
|
||||
public class SwipeDetector {
|
||||
public abstract class BaseSwipeDetector {
|
||||
|
||||
private static final boolean DBG = false;
|
||||
private static final String TAG = "SwipeDetector";
|
||||
private static final String TAG = "BaseSwipeDetector";
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
public static final Direction VERTICAL = new Direction() {
|
||||
|
||||
@Override
|
||||
float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint, boolean isRtl) {
|
||||
return ev.getY(pointerIndex) - refPoint.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
|
||||
return Math.abs(ev.getX(pointerIndex) - downPos.x);
|
||||
}
|
||||
|
||||
@Override
|
||||
float getVelocity(VelocityTracker tracker, boolean isRtl) {
|
||||
return tracker.getYVelocity();
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isPositive(float displacement) {
|
||||
// Up
|
||||
return displacement < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isNegative(float displacement) {
|
||||
// Down
|
||||
return displacement > 0;
|
||||
}
|
||||
};
|
||||
|
||||
public static final Direction HORIZONTAL = new Direction() {
|
||||
|
||||
@Override
|
||||
float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint, boolean isRtl) {
|
||||
float displacement = ev.getX(pointerIndex) - refPoint.x;
|
||||
if (isRtl) {
|
||||
displacement = -displacement;
|
||||
}
|
||||
return displacement;
|
||||
}
|
||||
|
||||
@Override
|
||||
float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos) {
|
||||
return Math.abs(ev.getY(pointerIndex) - downPos.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
float getVelocity(VelocityTracker tracker, boolean isRtl) {
|
||||
float velocity = tracker.getXVelocity();
|
||||
if (isRtl) {
|
||||
velocity = -velocity;
|
||||
}
|
||||
return velocity;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isPositive(float displacement) {
|
||||
// Right
|
||||
return displacement > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isNegative(float displacement) {
|
||||
// Left
|
||||
return displacement < 0;
|
||||
}
|
||||
};
|
||||
private static final PointF sTempPoint = new PointF();
|
||||
|
||||
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;
|
||||
protected final boolean mIsRtl;
|
||||
protected final float mTouchSlop;
|
||||
protected final float mMaxVelocity;
|
||||
|
||||
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 PointF mLastDisplacement = new PointF();
|
||||
private PointF mDisplacement = new PointF();
|
||||
protected PointF mSubtractDisplacement = new PointF();
|
||||
private ScrollState mState = ScrollState.IDLE;
|
||||
|
||||
protected boolean mIgnoreSlopWhenSettling;
|
||||
|
||||
private enum ScrollState {
|
||||
IDLE,
|
||||
DRAGGING, // onDragStart, onDrag
|
||||
SETTLING // onDragEnd
|
||||
}
|
||||
|
||||
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;
|
||||
protected BaseSwipeDetector(@NonNull ViewConfiguration config, boolean isRtl) {
|
||||
mTouchSlop = config.getScaledTouchSlop();
|
||||
mMaxVelocity = config.getScaledMaximumFlingVelocity();
|
||||
mIsRtl = isRtl;
|
||||
}
|
||||
|
||||
public static long calculateDuration(float velocity, float progressNeeded) {
|
||||
|
@ -192,27 +108,12 @@ public class SwipeDetector {
|
|||
return mState == ScrollState.DRAGGING || mState == ScrollState.SETTLING;
|
||||
}
|
||||
|
||||
public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
|
||||
mScrollDirections = scrollDirectionFlags;
|
||||
mIgnoreSlopWhenSettling = ignoreSlop;
|
||||
}
|
||||
|
||||
public int getScrollDirections() {
|
||||
return mScrollDirections;
|
||||
}
|
||||
|
||||
public void finishedScrolling() {
|
||||
setState(ScrollState.IDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 isFling(float velocity) {
|
||||
return Math.abs(velocity) > RELEASE_VELOCITY_PX_MS;
|
||||
}
|
||||
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
|
@ -230,8 +131,8 @@ public class SwipeDetector {
|
|||
mActivePointerId = ev.getPointerId(0);
|
||||
mDownPos.set(ev.getX(), ev.getY());
|
||||
mLastPos.set(mDownPos);
|
||||
mLastDisplacement = 0;
|
||||
mDisplacement = 0;
|
||||
mLastDisplacement.set(0, 0);
|
||||
mDisplacement.set(0, 0);
|
||||
|
||||
if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
|
||||
setState(ScrollState.DRAGGING);
|
||||
|
@ -255,10 +156,14 @@ public class SwipeDetector {
|
|||
if (pointerIndex == INVALID_POINTER_ID) {
|
||||
break;
|
||||
}
|
||||
mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos, mIsRtl);
|
||||
mDisplacement.set(ev.getX(pointerIndex) - mDownPos.x,
|
||||
ev.getY(pointerIndex) - mDownPos.y);
|
||||
if (mIsRtl) {
|
||||
mDisplacement.x = -mDisplacement.x;
|
||||
}
|
||||
|
||||
// handle state and listener calls.
|
||||
if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
|
||||
if (mState != ScrollState.DRAGGING && shouldScrollStart(mDisplacement)) {
|
||||
setState(ScrollState.DRAGGING);
|
||||
}
|
||||
if (mState == ScrollState.DRAGGING) {
|
||||
|
@ -308,84 +213,55 @@ public class SwipeDetector {
|
|||
mState = newState;
|
||||
}
|
||||
|
||||
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;
|
||||
private void initializeDragging() {
|
||||
if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
|
||||
mSubtractDisplacement.set(0, 0);
|
||||
} else {
|
||||
mSubtractDisplacement.x = mDisplacement.x > 0 ? mTouchSlop : -mTouchSlop;
|
||||
mSubtractDisplacement.y = mDisplacement.y > 0 ? mTouchSlop : -mTouchSlop;
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
protected abstract boolean shouldScrollStart(PointF displacement);
|
||||
|
||||
private void reportDragStart(boolean recatch) {
|
||||
mListener.onDragStart(!recatch);
|
||||
reportDragStartInternal(recatch);
|
||||
if (DBG) {
|
||||
Log.d(TAG, "onDragStart recatch:" + recatch);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeDragging() {
|
||||
if (mState == ScrollState.SETTLING && mIgnoreSlopWhenSettling) {
|
||||
mSubtractDisplacement = 0;
|
||||
} else if (mDisplacement > 0) {
|
||||
mSubtractDisplacement = mTouchSlop;
|
||||
} else {
|
||||
mSubtractDisplacement = -mTouchSlop;
|
||||
}
|
||||
}
|
||||
protected abstract void reportDragStartInternal(boolean recatch);
|
||||
|
||||
private void reportDragging(MotionEvent event) {
|
||||
if (mDisplacement != mLastDisplacement) {
|
||||
if (DBG) {
|
||||
Log.d(TAG, String.format("onDrag disp=%.1f", mDisplacement));
|
||||
Log.d(TAG, String.format("onDrag disp=%s", mDisplacement));
|
||||
}
|
||||
|
||||
mLastDisplacement = mDisplacement;
|
||||
mListener.onDrag(mDisplacement - mSubtractDisplacement, event);
|
||||
mLastDisplacement.set(mDisplacement);
|
||||
sTempPoint.set(mDisplacement.x - mSubtractDisplacement.x,
|
||||
mDisplacement.y - mSubtractDisplacement.y);
|
||||
reportDraggingInternal(sTempPoint, event);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void reportDraggingInternal(PointF displacement, MotionEvent event);
|
||||
|
||||
private void reportDragEnd() {
|
||||
mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
|
||||
float velocity = mDir.getVelocity(mVelocityTracker, mIsRtl) / 1000;
|
||||
PointF velocity = new PointF(mVelocityTracker.getXVelocity() / 1000,
|
||||
mVelocityTracker.getYVelocity() / 1000);
|
||||
if (mIsRtl) {
|
||||
velocity.x = -velocity.x;
|
||||
}
|
||||
if (DBG) {
|
||||
Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
|
||||
Log.d(TAG, String.format("onScrollEnd disp=%.1s, velocity=%.1s",
|
||||
mDisplacement, velocity));
|
||||
}
|
||||
|
||||
mListener.onDragEnd(velocity, Math.abs(velocity) > RELEASE_VELOCITY_PX_MS);
|
||||
reportDragEndInternal(velocity);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
protected abstract void reportDragEndInternal(PointF velocity);
|
||||
}
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright (C) 2019 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 com.android.launcher3.touch;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
/**
|
||||
* One dimensional scroll/drag/swipe gesture detector (either HORIZONTAL or VERTICAL).
|
||||
*/
|
||||
public class SingleAxisSwipeDetector extends BaseSwipeDetector {
|
||||
|
||||
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;
|
||||
|
||||
public static final Direction VERTICAL = new Direction() {
|
||||
|
||||
@Override
|
||||
boolean isPositive(float displacement) {
|
||||
// Up
|
||||
return displacement < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isNegative(float displacement) {
|
||||
// Down
|
||||
return displacement > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
float extractDirection(PointF direction) {
|
||||
return direction.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean canScrollStart(PointF displacement, float touchSlop) {
|
||||
return Math.abs(displacement.y) >= Math.max(Math.abs(displacement.x), touchSlop);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public static final Direction HORIZONTAL = new Direction() {
|
||||
|
||||
@Override
|
||||
boolean isPositive(float displacement) {
|
||||
// Right
|
||||
return displacement > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isNegative(float displacement) {
|
||||
// Left
|
||||
return displacement < 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
float extractDirection(PointF direction) {
|
||||
return direction.x;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean canScrollStart(PointF displacement, float touchSlop) {
|
||||
return Math.abs(displacement.x) >= Math.max(Math.abs(displacement.y), touchSlop);
|
||||
}
|
||||
};
|
||||
|
||||
private final Direction mDir;
|
||||
/* Client of this gesture detector can register a callback. */
|
||||
private final Listener mListener;
|
||||
|
||||
private int mScrollDirections;
|
||||
|
||||
public SingleAxisSwipeDetector(@NonNull Context context, @NonNull Listener l,
|
||||
@NonNull Direction dir) {
|
||||
this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected SingleAxisSwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
|
||||
@NonNull Direction dir, boolean isRtl) {
|
||||
super(config, isRtl);
|
||||
mListener = l;
|
||||
mDir = dir;
|
||||
}
|
||||
|
||||
public void setDetectableScrollConditions(int scrollDirectionFlags, boolean ignoreSlop) {
|
||||
mScrollDirections = scrollDirectionFlags;
|
||||
mIgnoreSlopWhenSettling = ignoreSlop;
|
||||
}
|
||||
|
||||
public int getScrollDirections() {
|
||||
return mScrollDirections;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(mDir.extractDirection(mSubtractDisplacement));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean shouldScrollStart(PointF displacement) {
|
||||
// Reject cases where the angle or slop condition is not met.
|
||||
if (!mDir.canScrollStart(displacement, mTouchSlop)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the client is interested in scroll in current direction.
|
||||
float displacementComponent = mDir.extractDirection(displacement);
|
||||
return canScrollNegative(displacementComponent) || canScrollPositive(displacementComponent);
|
||||
}
|
||||
|
||||
private boolean canScrollNegative(float displacement) {
|
||||
return (mScrollDirections & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(displacement);
|
||||
}
|
||||
|
||||
private boolean canScrollPositive(float displacement) {
|
||||
return (mScrollDirections & DIRECTION_POSITIVE) > 0 && mDir.isPositive(displacement);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reportDragStartInternal(boolean recatch) {
|
||||
mListener.onDragStart(!recatch);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reportDraggingInternal(PointF displacement, MotionEvent event) {
|
||||
mListener.onDrag(mDir.extractDirection(displacement), event);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void reportDragEndInternal(PointF velocity) {
|
||||
float velocityComponent = mDir.extractDirection(velocity);
|
||||
mListener.onDragEnd(velocityComponent);
|
||||
}
|
||||
|
||||
/** Listener to receive updates on the swipe. */
|
||||
public interface Listener {
|
||||
void onDragStart(boolean start);
|
||||
|
||||
// TODO remove
|
||||
boolean onDrag(float displacement);
|
||||
|
||||
default boolean onDrag(float displacement, MotionEvent event) {
|
||||
return onDrag(displacement);
|
||||
}
|
||||
|
||||
void onDragEnd(float velocity);
|
||||
}
|
||||
|
||||
public abstract static class Direction {
|
||||
|
||||
abstract boolean isPositive(float displacement);
|
||||
|
||||
abstract boolean isNegative(float displacement);
|
||||
|
||||
/** Returns the part of the given {@link PointF} that is relevant to this direction. */
|
||||
abstract float extractDirection(PointF point);
|
||||
|
||||
/** Reject cases where the angle or slop condition is not met. */
|
||||
abstract boolean canScrollStart(PointF displacement, float touchSlop);
|
||||
|
||||
}
|
||||
}
|
|
@ -32,13 +32,14 @@ import com.android.launcher3.AbstractFloatingView;
|
|||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.touch.SwipeDetector;
|
||||
import com.android.launcher3.touch.BaseSwipeDetector;
|
||||
import com.android.launcher3.touch.SingleAxisSwipeDetector;
|
||||
|
||||
/**
|
||||
* Extension of AbstractFloatingView with common methods for sliding in from bottom
|
||||
*/
|
||||
public abstract class AbstractSlideInView extends AbstractFloatingView
|
||||
implements SwipeDetector.Listener {
|
||||
implements SingleAxisSwipeDetector.Listener {
|
||||
|
||||
protected static Property<AbstractSlideInView, Float> TRANSLATION_SHIFT =
|
||||
new Property<AbstractSlideInView, Float>(Float.class, "translationShift") {
|
||||
|
@ -57,7 +58,7 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
protected static final float TRANSLATION_SHIFT_OPENED = 0f;
|
||||
|
||||
protected final Launcher mLauncher;
|
||||
protected final SwipeDetector mSwipeDetector;
|
||||
protected final SingleAxisSwipeDetector mSwipeDetector;
|
||||
protected final ObjectAnimator mOpenCloseAnimator;
|
||||
|
||||
protected View mContent;
|
||||
|
@ -73,7 +74,8 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
mLauncher = Launcher.getLauncher(context);
|
||||
|
||||
mScrollInterpolator = Interpolators.SCROLL_CUBIC;
|
||||
mSwipeDetector = new SwipeDetector(context, this, SwipeDetector.VERTICAL);
|
||||
mSwipeDetector = new SingleAxisSwipeDetector(context, this,
|
||||
SingleAxisSwipeDetector.VERTICAL);
|
||||
|
||||
mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
|
||||
mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
|
@ -97,7 +99,7 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
}
|
||||
|
||||
int directionsToDetectScroll = mSwipeDetector.isIdleState() ?
|
||||
SwipeDetector.DIRECTION_NEGATIVE : 0;
|
||||
SingleAxisSwipeDetector.DIRECTION_NEGATIVE : 0;
|
||||
mSwipeDetector.setDetectableScrollConditions(
|
||||
directionsToDetectScroll, false);
|
||||
mSwipeDetector.onTouchEvent(ev);
|
||||
|
@ -122,7 +124,7 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
return mIsOpen && mOpenCloseAnimator.isRunning();
|
||||
}
|
||||
|
||||
/* SwipeDetector.Listener */
|
||||
/* SingleAxisSwipeDetector.Listener */
|
||||
|
||||
@Override
|
||||
public void onDragStart(boolean start) { }
|
||||
|
@ -136,17 +138,17 @@ public abstract class AbstractSlideInView extends AbstractFloatingView
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd(float velocity, boolean fling) {
|
||||
if ((fling && velocity > 0) || mTranslationShift > 0.5f) {
|
||||
public void onDragEnd(float velocity) {
|
||||
if ((mSwipeDetector.isFling(velocity) && velocity > 0) || mTranslationShift > 0.5f) {
|
||||
mScrollInterpolator = scrollInterpolatorForVelocity(velocity);
|
||||
mOpenCloseAnimator.setDuration(SwipeDetector.calculateDuration(
|
||||
mOpenCloseAnimator.setDuration(BaseSwipeDetector.calculateDuration(
|
||||
velocity, TRANSLATION_SHIFT_CLOSED - mTranslationShift));
|
||||
close(true);
|
||||
} else {
|
||||
mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(
|
||||
TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
|
||||
mOpenCloseAnimator.setDuration(
|
||||
SwipeDetector.calculateDuration(velocity, mTranslationShift))
|
||||
BaseSwipeDetector.calculateDuration(velocity, mTranslationShift))
|
||||
.setInterpolator(Interpolators.DEACCEL);
|
||||
mOpenCloseAnimator.start();
|
||||
}
|
||||
|
|
|
@ -15,6 +15,12 @@
|
|||
*/
|
||||
package com.android.launcher3.touch;
|
||||
|
||||
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_BOTH;
|
||||
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE;
|
||||
import static com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_POSITIVE;
|
||||
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
|
||||
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
|
||||
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyFloat;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
|
@ -39,19 +45,19 @@ import org.mockito.MockitoAnnotations;
|
|||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class SwipeDetectorTest {
|
||||
public class SingleAxisSwipeDetectorTest {
|
||||
|
||||
private static final String TAG = SwipeDetectorTest.class.getSimpleName();
|
||||
private static final String TAG = SingleAxisSwipeDetectorTest.class.getSimpleName();
|
||||
public static void L(String s, Object... parts) {
|
||||
Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts));
|
||||
}
|
||||
|
||||
private TouchEventGenerator mGenerator;
|
||||
private SwipeDetector mDetector;
|
||||
private SingleAxisSwipeDetector mDetector;
|
||||
private int mTouchSlop;
|
||||
|
||||
@Mock
|
||||
private SwipeDetector.Listener mMockListener;
|
||||
private SingleAxisSwipeDetector.Listener mMockListener;
|
||||
|
||||
@Mock
|
||||
private ViewConfiguration mMockConfig;
|
||||
|
@ -65,8 +71,8 @@ public class SwipeDetectorTest {
|
|||
doReturn(orgConfig.getScaledMaximumFlingVelocity()).when(mMockConfig)
|
||||
.getScaledMaximumFlingVelocity();
|
||||
|
||||
mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
|
||||
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
|
||||
mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
|
||||
mDetector.setDetectableScrollConditions(DIRECTION_BOTH, false);
|
||||
mTouchSlop = orgConfig.getScaledTouchSlop();
|
||||
doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
|
||||
|
||||
|
@ -75,8 +81,8 @@ public class SwipeDetectorTest {
|
|||
|
||||
@Test
|
||||
public void testDragStart_verticalPositive() {
|
||||
mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
|
||||
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
|
||||
mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
|
||||
mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
|
||||
mGenerator.put(0, 100, 100);
|
||||
mGenerator.move(0, 100, 100 - mTouchSlop);
|
||||
// TODO: actually calculate the following parameters and do exact value checks.
|
||||
|
@ -85,8 +91,8 @@ public class SwipeDetectorTest {
|
|||
|
||||
@Test
|
||||
public void testDragStart_verticalNegative() {
|
||||
mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
|
||||
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
|
||||
mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, VERTICAL, false);
|
||||
mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
|
||||
mGenerator.put(0, 100, 100);
|
||||
mGenerator.move(0, 100, 100 + mTouchSlop);
|
||||
// TODO: actually calculate the following parameters and do exact value checks.
|
||||
|
@ -103,8 +109,8 @@ public class SwipeDetectorTest {
|
|||
|
||||
@Test
|
||||
public void testDragStart_horizontalPositive() {
|
||||
mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, false);
|
||||
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
|
||||
mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, false);
|
||||
mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
|
||||
|
||||
mGenerator.put(0, 100, 100);
|
||||
mGenerator.move(0, 100 + mTouchSlop, 100);
|
||||
|
@ -114,8 +120,8 @@ public class SwipeDetectorTest {
|
|||
|
||||
@Test
|
||||
public void testDragStart_horizontalNegative() {
|
||||
mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, false);
|
||||
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
|
||||
mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, false);
|
||||
mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
|
||||
|
||||
mGenerator.put(0, 100, 100);
|
||||
mGenerator.move(0, 100 - mTouchSlop, 100);
|
||||
|
@ -125,8 +131,8 @@ public class SwipeDetectorTest {
|
|||
|
||||
@Test
|
||||
public void testDragStart_horizontalRtlPositive() {
|
||||
mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, true);
|
||||
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
|
||||
mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, true);
|
||||
mDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
|
||||
|
||||
mGenerator.put(0, 100, 100);
|
||||
mGenerator.move(0, 100 - mTouchSlop, 100);
|
||||
|
@ -136,8 +142,8 @@ public class SwipeDetectorTest {
|
|||
|
||||
@Test
|
||||
public void testDragStart_horizontalRtlNegative() {
|
||||
mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, true);
|
||||
mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
|
||||
mDetector = new SingleAxisSwipeDetector(mMockConfig, mMockListener, HORIZONTAL, true);
|
||||
mDetector.setDetectableScrollConditions(DIRECTION_NEGATIVE, false);
|
||||
|
||||
mGenerator.put(0, 100, 100);
|
||||
mGenerator.move(0, 100 + mTouchSlop, 100);
|
||||
|
@ -160,6 +166,6 @@ public class SwipeDetectorTest {
|
|||
mGenerator.move(0, 100, 100 + mTouchSlop * 2);
|
||||
mGenerator.lift(0);
|
||||
// TODO: actually calculate the following parameters and do exact value checks.
|
||||
verify(mMockListener).onDragEnd(anyFloat(), anyBoolean());
|
||||
verify(mMockListener).onDragEnd(anyFloat());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue