SwipeDismissLayout.java revision df4ee5c8ac0830b23451ea466d48487f8b2205a0
1ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown/*
2ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * Copyright (C) 2014 The Android Open Source Project
3ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown *
4ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * Licensed under the Apache License, Version 2.0 (the "License");
5ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * you may not use this file except in compliance with the License.
6ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * You may obtain a copy of the License at
7ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown *
8ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown *      http://www.apache.org/licenses/LICENSE-2.0
9ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown *
10ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * Unless required by applicable law or agreed to in writing, software
11ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * distributed under the License is distributed on an "AS IS" BASIS,
12ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * See the License for the specific language governing permissions and
14ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * limitations under the License.
15ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown */
16ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
17ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownpackage com.android.internal.widget;
18ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
19ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.animation.TimeInterpolator;
20ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.content.Context;
21ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.util.AttributeSet;
22ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.util.Log;
23ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.MotionEvent;
24ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.VelocityTracker;
25ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.View;
26ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.ViewConfiguration;
27ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.ViewGroup;
28ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.animation.AccelerateInterpolator;
29ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.animation.DecelerateInterpolator;
30ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.widget.FrameLayout;
31ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
32ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown/**
33ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * Special layout that finishes its activity when swiped away.
34ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown */
35ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownpublic class SwipeDismissLayout extends FrameLayout {
36ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private static final String TAG = "SwipeDismissLayout";
37ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
38ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private static final float TRANSLATION_MIN_ALPHA = 0.5f;
39ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
40ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public interface OnDismissedListener {
41ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onDismissed(SwipeDismissLayout layout);
42ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
43ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
44ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public interface OnSwipeProgressChangedListener {
45ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        /**
46ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * Called when the layout has been swiped and the position of the window should change.
47ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         *
48ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * @param progress A number in [-1, 1] representing how far to the left
49ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * or right the window has been swiped. Negative values are swipes
50ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * left, and positives are right.
51ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * @param translate A number in [-w, w], where w is the width of the
52ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * layout. This is equivalent to progress * layout.getWidth().
53ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         */
54ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate);
55ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
56ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onSwipeCancelled(SwipeDismissLayout layout);
57ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
58ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
59ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    // Cached ViewConfiguration and system-wide constant values
60ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mSlop;
61ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mMinFlingVelocity;
62ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mMaxFlingVelocity;
63ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private long mAnimationTime;
64ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private TimeInterpolator mCancelInterpolator;
65ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private TimeInterpolator mDismissInterpolator;
66ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
67ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    // Transient properties
68ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mActiveTouchId;
69ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mDownX;
70ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mDownY;
71ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mSwiping;
72ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mDismissed;
73ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mDiscardIntercept;
74ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private VelocityTracker mVelocityTracker;
75ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mTranslationX;
76ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
77ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private OnDismissedListener mDismissedListener;
78ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private OnSwipeProgressChangedListener mProgressListener;
79ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
80ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context) {
81ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context);
82ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
83ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
84ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
85ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context, AttributeSet attrs) {
86ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context, attrs);
87ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
88ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
89ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
90ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle) {
91ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context, attrs, defStyle);
92ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
93ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
94ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
95ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void init(Context context) {
96ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        ViewConfiguration vc = ViewConfiguration.get(getContext());
97ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mSlop = vc.getScaledTouchSlop();
98ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity() * 16;
99ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
100ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mAnimationTime = getContext().getResources().getInteger(
101ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                android.R.integer.config_shortAnimTime);
102ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mCancelInterpolator = new DecelerateInterpolator(1.5f);
103ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDismissInterpolator = new AccelerateInterpolator(1.5f);
104ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
105ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
106ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public void setOnDismissedListener(OnDismissedListener listener) {
107ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDismissedListener = listener;
108ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
109ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
110ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public void setOnSwipeProgressChangedListener(OnSwipeProgressChangedListener listener) {
111ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mProgressListener = listener;
112ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
113ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
114ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    @Override
115ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public boolean onInterceptTouchEvent(MotionEvent ev) {
116ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        // offset because the view is translated during swipe
117ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        ev.offsetLocation(mTranslationX, 0);
118ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
119ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        switch (ev.getActionMasked()) {
120ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_DOWN:
121ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
122ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDownX = ev.getRawX();
123ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDownY = ev.getRawY();
124ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mActiveTouchId = ev.getPointerId(0);
125ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker = VelocityTracker.obtain();
126ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker.addMovement(ev);
127ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
128ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
129df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            case MotionEvent.ACTION_POINTER_DOWN:
130df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                int actionIndex = ev.getActionIndex();
131df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                mActiveTouchId = ev.getPointerId(actionIndex);
132df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                break;
133df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            case MotionEvent.ACTION_POINTER_UP:
134df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                actionIndex = ev.getActionIndex();
135df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                int pointerId = ev.getPointerId(actionIndex);
136df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                if (pointerId == mActiveTouchId) {
137df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    // This was our active pointer going up. Choose a new active pointer.
138df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    int newActionIndex = actionIndex == 0 ? 1 : 0;
139df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    mActiveTouchId = ev.getPointerId(newActionIndex);
140df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                }
141df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                break;
142df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh
143ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_CANCEL:
144ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_UP:
145ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
146ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
147ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
148ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_MOVE:
149ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mVelocityTracker == null || mDiscardIntercept) {
150ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
151ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
152ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
153ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                int pointerIndex = ev.findPointerIndex(mActiveTouchId);
154df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                if (pointerIndex == -1) {
155df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    Log.e(TAG, "Invalid pointer index: ignoring.");
156df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    mDiscardIntercept = true;
157df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    break;
158df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                }
159ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float dx = ev.getRawX() - mDownX;
160ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float x = ev.getX(pointerIndex);
161ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float y = ev.getY(pointerIndex);
162ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (dx != 0 && canScroll(this, false, dx, x, y)) {
163ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    mDiscardIntercept = true;
164ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
165ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
166ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateSwiping(ev);
167ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
168ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
169ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
170ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return !mDiscardIntercept && mSwiping;
171ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
172ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
173ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    @Override
174ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public boolean onTouchEvent(MotionEvent ev) {
175ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mVelocityTracker == null) {
176ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            return super.onTouchEvent(ev);
177ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
178ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        switch (ev.getActionMasked()) {
179ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_UP:
180ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateDismiss(ev);
181ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mDismissed) {
182ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    dismiss();
183ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                } else if (mSwiping) {
184ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    cancel();
185ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
186ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
187ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
188ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
189ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_CANCEL:
190ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                cancel();
191ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
192ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
193ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
194ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_MOVE:
195ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker.addMovement(ev);
196ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateSwiping(ev);
197ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateDismiss(ev);
198ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mSwiping) {
199ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    setProgress(ev.getRawX() - mDownX);
200ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
201ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
202ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
203ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return true;
204ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
205ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
206ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void setProgress(float deltaX) {
207ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mTranslationX = deltaX;
208ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mProgressListener != null) {
209ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX);
210ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
211ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
212ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
213ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void dismiss() {
214ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mDismissedListener != null) {
215ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mDismissedListener.onDismissed(this);
216ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
217ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
218ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
219ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    protected void cancel() {
220ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mProgressListener != null) {
221ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mProgressListener.onSwipeCancelled(this);
222ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
223ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
224ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
225ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    /**
226ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * Resets internal members when canceling.
227ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     */
228ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void resetMembers() {
229ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mVelocityTracker != null) {
230ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mVelocityTracker.recycle();
231ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
232ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mVelocityTracker = null;
233ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mTranslationX = 0;
234ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDownX = 0;
235ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDownY = 0;
236ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mSwiping = false;
237ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDismissed = false;
238ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDiscardIntercept = false;
239ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
240ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
241ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void updateSwiping(MotionEvent ev) {
242ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (!mSwiping) {
243ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float deltaX = ev.getRawX() - mDownX;
244ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float deltaY = ev.getRawY() - mDownY;
245ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2;
246ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
247ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
248ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
249ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void updateDismiss(MotionEvent ev) {
250df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        float deltaX = ev.getRawX() - mDownX;
251ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (!mDismissed) {
252ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mVelocityTracker.addMovement(ev);
253ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mVelocityTracker.computeCurrentVelocity(1000);
254ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
255ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float velocityX = mVelocityTracker.getXVelocity();
256ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float absVelocityX = Math.abs(velocityX);
257ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
258ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
259ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            if (deltaX > getWidth() / 2) {
260ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDismissed = true;
261ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            } else if (absVelocityX >= mMinFlingVelocity
262ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    && absVelocityX <= mMaxFlingVelocity
263ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    && absVelocityY < absVelocityX / 2
264ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    && velocityX > 0
265ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    && deltaX > 0) {
266ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDismissed = true;
267ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            }
268ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
269df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        // Check if the user tried to undo this.
270df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        if (mDismissed && mSwiping) {
271df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            // Check if the user's finger is actually back
272df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            if (deltaX < getWidth() / 2) {
273df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                mDismissed = false;
274df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            }
275df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        }
276ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
277ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
278ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    /**
279ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * Tests scrollability within child views of v in the direction of dx.
280ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     *
281ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param v View to test for horizontal scrollability
282ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param checkV Whether the view v passed should itself be checked for scrollability (true),
283ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     *               or just its children (false).
284ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param dx Delta scrolled in pixels. Only the sign of this is used.
285ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param x X coordinate of the active touch point
286ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param y Y coordinate of the active touch point
287ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @return true if child views of v can be scrolled by delta of dx.
288ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     */
289ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    protected boolean canScroll(View v, boolean checkV, float dx, float x, float y) {
290ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (v instanceof ViewGroup) {
291ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final ViewGroup group = (ViewGroup) v;
292ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int scrollX = v.getScrollX();
293ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int scrollY = v.getScrollY();
294ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int count = group.getChildCount();
295ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            for (int i = count - 1; i >= 0; i--) {
296ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                final View child = group.getChildAt(i);
297ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
298ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                        y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
299ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                        canScroll(child, true, dx, x + scrollX - child.getLeft(),
300ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                                y + scrollY - child.getTop())) {
301ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    return true;
302ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
303ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            }
304ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
305ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
306ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return checkV && v.canScrollHorizontally((int) -dx);
307ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
308ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown}
309