10bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska/* 20bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Copyright (C) 2017 The Android Open Source Project 30bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 40bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Licensed under the Apache License, Version 2.0 (the "License"); 50bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * you may not use this file except in compliance with the License. 60bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * You may obtain a copy of the License at 70bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 80bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * http://www.apache.org/licenses/LICENSE-2.0 90bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 100bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Unless required by applicable law or agreed to in writing, software 110bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * distributed under the License is distributed on an "AS IS" BASIS, 120bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * See the License for the specific language governing permissions and 140bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * limitations under the License. 150bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 160bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 179b028c55b13889bf75b5dd43b5b0f4051834ae1dAga Madurskapackage android.support.wear.widget; 180bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 190bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 200bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 210bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.content.Context; 220bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.content.res.Resources; 230bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.support.annotation.Nullable; 240bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.support.annotation.RestrictTo; 250bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.support.annotation.UiThread; 260bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.util.AttributeSet; 270bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.util.Log; 280bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.view.MotionEvent; 290bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.view.VelocityTracker; 300bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.view.View; 310bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.view.ViewConfiguration; 320bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.view.ViewGroup; 330bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaimport android.widget.FrameLayout; 340bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 350bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska/** 360bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Special layout that finishes its activity when swiped away. 370bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 380bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * <p>This is a modified copy of the internal framework class 390bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * com.android.internal.widget.SwipeDismissLayout. 400bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 410bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @hide 420bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 430bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska@RestrictTo(LIBRARY_GROUP) 440bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska@UiThread 450bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurskaclass SwipeDismissLayout extends FrameLayout { 460bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private static final String TAG = "SwipeDismissLayout"; 470bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 480bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska public static final float DEFAULT_DISMISS_DRAG_WIDTH_RATIO = .33f; 490bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // A value between 0.0 and 1.0 determining the percentage of the screen on the left-hand-side 500bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // where edge swipe gestures are permitted to begin. 510bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private static final float EDGE_SWIPE_THRESHOLD = 0.1f; 520bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 530bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** Called when the layout is about to consider a swipe. */ 540bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska @UiThread 550bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska interface OnPreSwipeListener { 560bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** 570bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Notifies listeners that the view is now considering to start a dismiss gesture from a 580bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * particular point on the screen. The default implementation returns true for all 590bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * coordinates so that is is possible to start a swipe-to-dismiss gesture from any location. 600bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * If any one instance of this Callback returns false for a given set of coordinates, 610bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * swipe-to-dismiss will not be allowed to start in that point. 620bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 630bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param xDown the x coordinate of the initial {@link android.view.MotionEvent#ACTION_DOWN} 640bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * event for this motion 650bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param yDown the y coordinate of the initial {@link android.view.MotionEvent#ACTION_DOWN} 660bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * event for this motion 670bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @return {@code true} if these coordinates should be considered as a start of a swipe 680bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * gesture, {@code false} otherwise 690bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 700bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska boolean onPreSwipe(SwipeDismissLayout swipeDismissLayout, float xDown, float yDown); 710bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 720bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 730bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** 740bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Interface enabling listeners to react to when the swipe gesture is done and the view should 750bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * probably be dismissed from the UI. 760bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 770bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska @UiThread 780bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska interface OnDismissedListener { 790bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska void onDismissed(SwipeDismissLayout layout); 800bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 810bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 820bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** 830bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Interface enabling listeners to react to changes in the progress of the swipe-to-dismiss 840bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * gesture. 850bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 860bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska @UiThread 870bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska interface OnSwipeProgressChangedListener { 880bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** 890bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Called when the layout has been swiped and the position of the window should change. 900bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 910bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param layout the layout associated with this listener. 920bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param progress a number in [0, 1] representing how far to the right the window has 930bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * been swiped 940bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param translate a number in [0, w], where w is the width of the layout. This is 950bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * equivalent to progress * layout.getWidth() 960bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 970bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate); 980bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 990bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** 1000bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Called when the layout started to be swiped away but then the gesture was canceled. 1010bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 1020bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param layout the layout associated with this listener 1030bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 1049138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska void onSwipeCanceled(SwipeDismissLayout layout); 1050bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 1060bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1070bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // Cached ViewConfiguration and system-wide constant values 1080bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private int mSlop; 1090bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private int mMinFlingVelocity; 1100bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private float mGestureThresholdPx; 1110bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1120bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // Transient properties 1130bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private int mActiveTouchId; 1140bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private float mDownX; 1150bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private float mDownY; 1160bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private boolean mSwipeable; 1170bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private boolean mSwiping; 1180bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // This variable holds information about whether the initial move of a longer swipe 1190bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // (consisting of multiple move events) has conformed to the definition of a horizontal 1200bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // swipe-to-dismiss. A swipe gesture is only ever allowed to be recognized if this variable is 1210bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // set to true. Otherwise, the motion events will be allowed to propagate to the children. 1220bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private boolean mCanStartSwipe = true; 1230bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private boolean mDismissed; 1240bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private boolean mDiscardIntercept; 1250bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private VelocityTracker mVelocityTracker; 1260bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private float mTranslationX; 1279138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska private boolean mDisallowIntercept; 1280bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1290bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska @Nullable 1300bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private OnPreSwipeListener mOnPreSwipeListener; 1310bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private OnDismissedListener mDismissedListener; 1320bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private OnSwipeProgressChangedListener mProgressListener; 1330bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1340bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private float mLastX; 1350bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private float mDismissMinDragWidthRatio = DEFAULT_DISMISS_DRAG_WIDTH_RATIO; 1360bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1370bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska SwipeDismissLayout(Context context) { 1380bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska this(context, null); 1390bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 1400bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1410bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska SwipeDismissLayout(Context context, AttributeSet attrs) { 1420bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska this(context, attrs, 0); 1430bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 1440bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1450bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle) { 1460bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska this(context, attrs, defStyle, 0); 1470bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 1480bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1490bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle, int defStyleRes) { 1500bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska super(context, attrs, defStyle, defStyleRes); 1510bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska ViewConfiguration vc = ViewConfiguration.get(context); 1520bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mSlop = vc.getScaledTouchSlop(); 1530bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mMinFlingVelocity = vc.getScaledMinimumFlingVelocity(); 1540bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mGestureThresholdPx = 1550bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska Resources.getSystem().getDisplayMetrics().widthPixels * EDGE_SWIPE_THRESHOLD; 1560bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1570bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // By default, the view is swipeable. 1580bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska setSwipeable(true); 1590bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 1600bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1610bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** 1620bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Sets the minimum ratio of the screen after which the swipe gesture is treated as swipe-to- 1630bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * dismiss. 1640bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 1650bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param ratio the ratio of the screen at which the swipe gesture is treated as 1660bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * swipe-to-dismiss. should be provided as a fraction of the screen 1670bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 1680bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska public void setDismissMinDragWidthRatio(float ratio) { 1690bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDismissMinDragWidthRatio = ratio; 1700bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 1710bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1720bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** 1730bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Returns the current ratio of te screen at which the swipe gesture is treated as 1740bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * swipe-to-dismiss. 1750bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 1760bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @return the current ratio of te screen at which the swipe gesture is treated as 1770bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * swipe-to-dismiss 1780bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 1790bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska public float getDismissMinDragWidthRatio() { 1800bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return mDismissMinDragWidthRatio; 1810bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 1820bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1830bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** 1840bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Sets the layout to swipeable or not. This effectively turns the functionality of this layout 1850bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * on or off. 1860bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 1870bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param swipeable whether the layout should react to the swipe gesture 1880bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 1890bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska public void setSwipeable(boolean swipeable) { 1900bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mSwipeable = swipeable; 1910bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 1920bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1930bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** Returns true if the layout reacts to swipe gestures. */ 1940bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska public boolean isSwipeable() { 1950bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return mSwipeable; 1960bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 1970bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 1980bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska void setOnPreSwipeListener(@Nullable OnPreSwipeListener listener) { 1990bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mOnPreSwipeListener = listener; 2000bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2010bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2020bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska void setOnDismissedListener(@Nullable OnDismissedListener listener) { 2030bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDismissedListener = listener; 2040bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2050bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2060bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska void setOnSwipeProgressChangedListener(@Nullable OnSwipeProgressChangedListener listener) { 2070bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mProgressListener = listener; 2080bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2090bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2100bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska @Override 2119138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) { 2129138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska mDisallowIntercept = disallowIntercept; 2139138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska if (getParent() != null) { 2149138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska getParent().requestDisallowInterceptTouchEvent(disallowIntercept); 2159138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska } 2169138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska } 2179138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska 2189138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska @Override 2190bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska public boolean onInterceptTouchEvent(MotionEvent ev) { 2200bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (!mSwipeable) { 2210bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return super.onInterceptTouchEvent(ev); 2220bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2230bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2240bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // offset because the view is translated during swipe 2250bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska ev.offsetLocation(mTranslationX, 0); 2260bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2270bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska switch (ev.getActionMasked()) { 2280bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska case MotionEvent.ACTION_DOWN: 2290bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska resetMembers(); 2300bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDownX = ev.getRawX(); 2310bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDownY = ev.getRawY(); 2320bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mActiveTouchId = ev.getPointerId(0); 2330bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mVelocityTracker = VelocityTracker.obtain(); 2340bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mVelocityTracker.addMovement(ev); 2350bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 2360bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2370bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska case MotionEvent.ACTION_POINTER_DOWN: 2380bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska int actionIndex = ev.getActionIndex(); 2390bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mActiveTouchId = ev.getPointerId(actionIndex); 2400bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 2410bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska case MotionEvent.ACTION_POINTER_UP: 2420bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska actionIndex = ev.getActionIndex(); 2430bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska int pointerId = ev.getPointerId(actionIndex); 2440bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (pointerId == mActiveTouchId) { 2450bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // This was our active pointer going up. Choose a new active pointer. 2460bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska int newActionIndex = actionIndex == 0 ? 1 : 0; 2470bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mActiveTouchId = ev.getPointerId(newActionIndex); 2480bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2490bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 2500bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2510bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska case MotionEvent.ACTION_CANCEL: 2520bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska case MotionEvent.ACTION_UP: 2530bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska resetMembers(); 2540bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 2550bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2560bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska case MotionEvent.ACTION_MOVE: 2570bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (mVelocityTracker == null || mDiscardIntercept) { 2580bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 2590bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2600bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2610bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska int pointerIndex = ev.findPointerIndex(mActiveTouchId); 2620bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (pointerIndex == -1) { 2630bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska Log.e(TAG, "Invalid pointer index: ignoring."); 2640bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDiscardIntercept = true; 2650bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 2660bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2670bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska float dx = ev.getRawX() - mDownX; 2680bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska float x = ev.getX(pointerIndex); 2690bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska float y = ev.getY(pointerIndex); 2700bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2710bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (dx != 0 && mDownX >= mGestureThresholdPx && canScroll(this, false, dx, x, y)) { 2720bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDiscardIntercept = true; 2730bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 2740bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2750bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska updateSwiping(ev); 2760bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 2770bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2780bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2799138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska if ((mOnPreSwipeListener == null && !mDisallowIntercept) 2809138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska || mOnPreSwipeListener.onPreSwipe(this, mDownX, mDownY)) { 2810bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return (!mDiscardIntercept && mSwiping); 2820bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2830bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return false; 2840bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2850bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2860bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska @Override 2870bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska public boolean canScrollHorizontally(int direction) { 2880bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // This view can only be swiped horizontally from left to right - this means a negative 2890bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // SCROLLING direction. We return false if the view is not visible to avoid capturing swipe 2900bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // gestures when the view is hidden. 2910bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return direction < 0 && isSwipeable() && getVisibility() == View.VISIBLE; 2920bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 2930bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 2940bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** 2950bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Helper function determining if a particular move gesture was verbose enough to qualify as a 2960bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * beginning of a swipe. 2970bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 2980bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param dx distance traveled in the x direction, from the initial touch down 2990bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param dy distance traveled in the y direction, from the initial touch down 3000bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @return {@code true} if the gesture was long enough to be considered a potential swipe 3010bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 3020bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private boolean isPotentialSwipe(float dx, float dy) { 3030bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return (dx * dx) + (dy * dy) > mSlop * mSlop; 3040bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3050bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 3060bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska @Override 3070bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska public boolean onTouchEvent(MotionEvent ev) { 3080bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (!mSwipeable) { 3090bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return super.onTouchEvent(ev); 3100bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3110bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 3120bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (mVelocityTracker == null) { 3130bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return super.onTouchEvent(ev); 3140bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 31537bc8b03d33de5c375c26d915fcb056a42c5abc2Aga Madurska 31637bc8b03d33de5c375c26d915fcb056a42c5abc2Aga Madurska if (mOnPreSwipeListener != null && !mOnPreSwipeListener.onPreSwipe(this, mDownX, mDownY)) { 31737bc8b03d33de5c375c26d915fcb056a42c5abc2Aga Madurska return super.onTouchEvent(ev); 31837bc8b03d33de5c375c26d915fcb056a42c5abc2Aga Madurska } 31937bc8b03d33de5c375c26d915fcb056a42c5abc2Aga Madurska 3200bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // offset because the view is translated during swipe 3210bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska ev.offsetLocation(mTranslationX, 0); 3220bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska switch (ev.getActionMasked()) { 3230bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska case MotionEvent.ACTION_UP: 3240bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska updateDismiss(ev); 3250bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (mDismissed) { 3260bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska dismiss(); 3270bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } else if (mSwiping) { 3280bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska cancel(); 3290bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3300bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska resetMembers(); 3310bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 3320bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 3330bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska case MotionEvent.ACTION_CANCEL: 3340bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska cancel(); 3350bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska resetMembers(); 3360bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 3370bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 3380bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska case MotionEvent.ACTION_MOVE: 3390bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mVelocityTracker.addMovement(ev); 3400bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mLastX = ev.getRawX(); 3410bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska updateSwiping(ev); 3420bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (mSwiping) { 3430bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska setProgress(ev.getRawX() - mDownX); 3440bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska break; 3450bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3460bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3470bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return true; 3480bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3490bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 3500bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private void setProgress(float deltaX) { 3510bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mTranslationX = deltaX; 3520bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (mProgressListener != null && deltaX >= 0) { 3530bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX); 3540bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3550bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3560bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 3570bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private void dismiss() { 3580bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (mDismissedListener != null) { 3590bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDismissedListener.onDismissed(this); 3600bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3610bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3620bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 3639138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska private void cancel() { 3640bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (mProgressListener != null) { 3659138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska mProgressListener.onSwipeCanceled(this); 3660bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3670bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3680bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 3690bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** Resets internal members when canceling or finishing a given gesture. */ 3700bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private void resetMembers() { 3710bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (mVelocityTracker != null) { 3720bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mVelocityTracker.recycle(); 3730bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3740bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mVelocityTracker = null; 3750bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mTranslationX = 0; 3760bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDownX = 0; 3770bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDownY = 0; 3780bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mSwiping = false; 3790bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDismissed = false; 3800bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDiscardIntercept = false; 3810bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mCanStartSwipe = true; 3829138a30e06ea881190a9150f59c2f3310b27dd04Aga Madurska mDisallowIntercept = false; 3830bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3840bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 3850bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private void updateSwiping(MotionEvent ev) { 3860bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (!mSwiping) { 3870bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska float deltaX = ev.getRawX() - mDownX; 3880bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska float deltaY = ev.getRawY() - mDownY; 3890bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (isPotentialSwipe(deltaX, deltaY)) { 390d5fc393a5aa24693d35b5ebd6d588613103aafdcAga Madurska // There are three conditions on which we want want to start swiping: 391d5fc393a5aa24693d35b5ebd6d588613103aafdcAga Madurska // 1. The swipe is from left to right AND 392d5fc393a5aa24693d35b5ebd6d588613103aafdcAga Madurska // 2. It is horizontal AND 393d5fc393a5aa24693d35b5ebd6d588613103aafdcAga Madurska // 3. We actually can start swiping 394d5fc393a5aa24693d35b5ebd6d588613103aafdcAga Madurska mSwiping = mCanStartSwipe && Math.abs(deltaY) < Math.abs(deltaX) && deltaX > 0; 3950bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mCanStartSwipe = mSwiping; 3960bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3970bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3980bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 3990bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 4000bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska private void updateDismiss(MotionEvent ev) { 4010bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska float deltaX = ev.getRawX() - mDownX; 4020bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mVelocityTracker.addMovement(ev); 4030bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mVelocityTracker.computeCurrentVelocity(1000); 4040bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (!mDismissed) { 4050bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if ((deltaX > (getWidth() * mDismissMinDragWidthRatio) && ev.getRawX() >= mLastX) 4060bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska || mVelocityTracker.getXVelocity() >= mMinFlingVelocity) { 4070bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDismissed = true; 4080bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 4090bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 4100bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // Check if the user tried to undo this. 4110bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (mDismissed && mSwiping) { 4120bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska // Check if the user's finger is actually flinging back to left 4130bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (mVelocityTracker.getXVelocity() < -mMinFlingVelocity) { 4140bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska mDismissed = false; 4150bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 4160bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 4170bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 4180bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 4190bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska /** 4200bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * Tests scrollability within child views of v in the direction of dx. 4210bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * 4220bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param v view to test for horizontal scrollability 4230bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param checkV whether the view v passed should itself be checked for scrollability 4240bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * ({@code true}), or just its children ({@code false}) 4250bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param dx delta scrolled in pixels. Only the sign of this is used 4260bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param x x coordinate of the active touch point 4270bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @param y y coordinate of the active touch point 4280bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska * @return {@code true} if child views of v can be scrolled by delta of dx 4290bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska */ 4300bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska protected boolean canScroll(View v, boolean checkV, float dx, float x, float y) { 4310bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (v instanceof ViewGroup) { 4320bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska final ViewGroup group = (ViewGroup) v; 4330bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska final int scrollX = v.getScrollX(); 4340bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska final int scrollY = v.getScrollY(); 4350bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska final int count = group.getChildCount(); 4360bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska for (int i = count - 1; i >= 0; i--) { 4370bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska final View child = group.getChildAt(i); 4380bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska if (x + scrollX >= child.getLeft() 4390bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska && x + scrollX < child.getRight() 4400bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska && y + scrollY >= child.getTop() 4410bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska && y + scrollY < child.getBottom() 4420bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska && canScroll( 4430bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska child, true, dx, x + scrollX - child.getLeft(), 4440bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska y + scrollY - child.getTop())) { 4450bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return true; 4460bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 4470bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 4480bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 4490bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska 4500bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska return checkV && v.canScrollHorizontally((int) -dx); 4510bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska } 4520bb1bb33c39fa5eef545f8513753a8e3b8dfede7Aga Madurska} 453