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;
203f8dd14451521d728fba548c7655d8fe531ed2efFilip Gruszczynskiimport android.app.Activity;
21ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.content.Context;
22ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.util.AttributeSet;
23ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.util.Log;
24ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.MotionEvent;
25ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.VelocityTracker;
26ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.View;
27ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.ViewConfiguration;
28ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.ViewGroup;
296eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynskiimport android.view.ViewTreeObserver;
30ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.animation.AccelerateInterpolator;
31ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.animation.DecelerateInterpolator;
32ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.widget.FrameLayout;
33ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
34ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown/**
35ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * Special layout that finishes its activity when swiped away.
36ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown */
37ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownpublic class SwipeDismissLayout extends FrameLayout {
38ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private static final String TAG = "SwipeDismissLayout";
39ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
4056e533a5f69e930608f6caf478d4fa5afc7c5eecMindy Pereira    private static final float DISMISS_MIN_DRAG_WIDTH_RATIO = .33f;
41ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
42ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public interface OnDismissedListener {
43ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onDismissed(SwipeDismissLayout layout);
44ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
45ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
46ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public interface OnSwipeProgressChangedListener {
47ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        /**
48ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * Called when the layout has been swiped and the position of the window should change.
49ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         *
506c5c48a8b05e7668de551c3bf37aa3f33c7f0920Mark Renouf         * @param progress A number in [0, 1] representing how far to the
516c5c48a8b05e7668de551c3bf37aa3f33c7f0920Mark Renouf         * right the window has been swiped
526c5c48a8b05e7668de551c3bf37aa3f33c7f0920Mark Renouf         * @param translate A number in [0, w], where w is the width of the
53ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * layout. This is equivalent to progress * layout.getWidth().
54ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         */
55ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate);
56ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
57ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onSwipeCancelled(SwipeDismissLayout layout);
58ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
59ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
60ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    // Cached ViewConfiguration and system-wide constant values
61ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mSlop;
62ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mMinFlingVelocity;
63ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mMaxFlingVelocity;
64ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private long mAnimationTime;
65ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private TimeInterpolator mCancelInterpolator;
66ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private TimeInterpolator mDismissInterpolator;
67ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
68ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    // Transient properties
69ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mActiveTouchId;
70ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mDownX;
71ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mDownY;
72ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mSwiping;
73ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mDismissed;
74ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mDiscardIntercept;
75ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private VelocityTracker mVelocityTracker;
76ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mTranslationX;
77ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
78ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private OnDismissedListener mDismissedListener;
79ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private OnSwipeProgressChangedListener mProgressListener;
806eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    private ViewTreeObserver.OnEnterAnimationCompleteListener mOnEnterAnimationCompleteListener =
816eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski            new ViewTreeObserver.OnEnterAnimationCompleteListener() {
826eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                @Override
836eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                public void onEnterAnimationComplete() {
846eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    // SwipeDismissLayout assumes that the host Activity is translucent
856eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    // and temporarily disables translucency when it is fully visible.
866eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    // As soon as the user starts swiping, we will re-enable
876eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    // translucency.
886eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    if (getContext() instanceof Activity) {
896eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                        ((Activity) getContext()).convertFromTranslucent();
906eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    }
916eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                }
926eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski            };
93ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
9411b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf    private float mLastX;
9511b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf
96ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context) {
97ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context);
98ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
99ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
100ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
101ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context, AttributeSet attrs) {
102ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context, attrs);
103ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
104ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
105ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
106ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle) {
107ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context, attrs, defStyle);
108ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
109ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
110ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
111ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void init(Context context) {
112ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        ViewConfiguration vc = ViewConfiguration.get(getContext());
113ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mSlop = vc.getScaledTouchSlop();
11411b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
115ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
116ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mAnimationTime = getContext().getResources().getInteger(
117ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                android.R.integer.config_shortAnimTime);
118ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mCancelInterpolator = new DecelerateInterpolator(1.5f);
119ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDismissInterpolator = new AccelerateInterpolator(1.5f);
120ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
121ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
122ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public void setOnDismissedListener(OnDismissedListener listener) {
123ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDismissedListener = listener;
124ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
125ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
126ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public void setOnSwipeProgressChangedListener(OnSwipeProgressChangedListener listener) {
127ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mProgressListener = listener;
128ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
129ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
130ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    @Override
1316eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    protected void onAttachedToWindow() {
1326eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        super.onAttachedToWindow();
1336eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        if (getContext() instanceof Activity) {
1346eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski            getViewTreeObserver().addOnEnterAnimationCompleteListener(
1356eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    mOnEnterAnimationCompleteListener);
1366eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        }
1376eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    }
1386eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski
1396eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    @Override
1406eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    protected void onDetachedFromWindow() {
1416eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        super.onDetachedFromWindow();
1426eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        if (getContext() instanceof Activity) {
1436eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski            getViewTreeObserver().removeOnEnterAnimationCompleteListener(
1446eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    mOnEnterAnimationCompleteListener);
1456eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        }
1466eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    }
1476eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski
1486eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    @Override
149ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public boolean onInterceptTouchEvent(MotionEvent ev) {
150ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        // offset because the view is translated during swipe
151ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        ev.offsetLocation(mTranslationX, 0);
152ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
153ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        switch (ev.getActionMasked()) {
154ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_DOWN:
155ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
156ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDownX = ev.getRawX();
157ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDownY = ev.getRawY();
158ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mActiveTouchId = ev.getPointerId(0);
159ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker = VelocityTracker.obtain();
160ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker.addMovement(ev);
161ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
162ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
163df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            case MotionEvent.ACTION_POINTER_DOWN:
164df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                int actionIndex = ev.getActionIndex();
165df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                mActiveTouchId = ev.getPointerId(actionIndex);
166df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                break;
167df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            case MotionEvent.ACTION_POINTER_UP:
168df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                actionIndex = ev.getActionIndex();
169df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                int pointerId = ev.getPointerId(actionIndex);
170df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                if (pointerId == mActiveTouchId) {
171df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    // This was our active pointer going up. Choose a new active pointer.
172df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    int newActionIndex = actionIndex == 0 ? 1 : 0;
173df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    mActiveTouchId = ev.getPointerId(newActionIndex);
174df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                }
175df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                break;
176df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh
177ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_CANCEL:
178ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_UP:
179ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
180ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
181ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
182ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_MOVE:
183ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mVelocityTracker == null || mDiscardIntercept) {
184ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
185ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
186ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
187ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                int pointerIndex = ev.findPointerIndex(mActiveTouchId);
188df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                if (pointerIndex == -1) {
189df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    Log.e(TAG, "Invalid pointer index: ignoring.");
190df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    mDiscardIntercept = true;
191df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    break;
192df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                }
193ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float dx = ev.getRawX() - mDownX;
194ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float x = ev.getX(pointerIndex);
195ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float y = ev.getY(pointerIndex);
196ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (dx != 0 && canScroll(this, false, dx, x, y)) {
197ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    mDiscardIntercept = true;
198ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
199ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
200ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateSwiping(ev);
201ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
202ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
203ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
204ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return !mDiscardIntercept && mSwiping;
205ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
206ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
207ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    @Override
208ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public boolean onTouchEvent(MotionEvent ev) {
209ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mVelocityTracker == null) {
210ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            return super.onTouchEvent(ev);
211ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
212ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        switch (ev.getActionMasked()) {
213ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_UP:
214ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateDismiss(ev);
215ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mDismissed) {
216ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    dismiss();
217ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                } else if (mSwiping) {
218ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    cancel();
219ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
220ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
221ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
222ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
223ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_CANCEL:
224ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                cancel();
225ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
226ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
227ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
228ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_MOVE:
229ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker.addMovement(ev);
23011b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf                mLastX = ev.getRawX();
231ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateSwiping(ev);
232ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mSwiping) {
233625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski                    if (getContext() instanceof Activity) {
234625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski                        ((Activity) getContext()).convertToTranslucent(null, null);
235625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski                    }
236ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    setProgress(ev.getRawX() - mDownX);
237ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
238ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
239ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
240ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return true;
241ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
242ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
243ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void setProgress(float deltaX) {
244ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mTranslationX = deltaX;
2456c5c48a8b05e7668de551c3bf37aa3f33c7f0920Mark Renouf        if (mProgressListener != null && deltaX >= 0)  {
246ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX);
247ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
248ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
249ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
250ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void dismiss() {
251ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mDismissedListener != null) {
252ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mDismissedListener.onDismissed(this);
253ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
254ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
255ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
256ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    protected void cancel() {
257625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski        if (getContext() instanceof Activity) {
258625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski            ((Activity) getContext()).convertFromTranslucent();
259625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski        }
260ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mProgressListener != null) {
261ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mProgressListener.onSwipeCancelled(this);
262ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
263ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
264ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
265ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    /**
266ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * Resets internal members when canceling.
267ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     */
268ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void resetMembers() {
269ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mVelocityTracker != null) {
270ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mVelocityTracker.recycle();
271ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
272ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mVelocityTracker = null;
273ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mTranslationX = 0;
274ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDownX = 0;
275ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDownY = 0;
276ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mSwiping = false;
277ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDismissed = false;
278ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDiscardIntercept = false;
279ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
280ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
281ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void updateSwiping(MotionEvent ev) {
282ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (!mSwiping) {
283ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float deltaX = ev.getRawX() - mDownX;
284ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float deltaY = ev.getRawY() - mDownY;
285072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            if ((deltaX * deltaX) + (deltaY * deltaY) > mSlop * mSlop) {
286072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira                mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < mSlop * 2;
287072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            } else {
288072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira                mSwiping = false;
289072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            }
290ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
291ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
292ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
293ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void updateDismiss(MotionEvent ev) {
294df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        float deltaX = ev.getRawX() - mDownX;
295ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (!mDismissed) {
296ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mVelocityTracker.addMovement(ev);
297ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mVelocityTracker.computeCurrentVelocity(1000);
298ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
299072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            if (deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) &&
30011b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf                    ev.getRawX() >= mLastX) {
301ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDismissed = true;
302ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            }
303ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
304df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        // Check if the user tried to undo this.
305df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        if (mDismissed && mSwiping) {
306df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            // Check if the user's finger is actually back
307072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO)) {
308df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                mDismissed = false;
309df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            }
310df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        }
311ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
312ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
313ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    /**
314ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * Tests scrollability within child views of v in the direction of dx.
315ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     *
316ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param v View to test for horizontal scrollability
317ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param checkV Whether the view v passed should itself be checked for scrollability (true),
318ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     *               or just its children (false).
319ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param dx Delta scrolled in pixels. Only the sign of this is used.
320ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param x X coordinate of the active touch point
321ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param y Y coordinate of the active touch point
322ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @return true if child views of v can be scrolled by delta of dx.
323ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     */
324ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    protected boolean canScroll(View v, boolean checkV, float dx, float x, float y) {
325ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (v instanceof ViewGroup) {
326ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final ViewGroup group = (ViewGroup) v;
327ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int scrollX = v.getScrollX();
328ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int scrollY = v.getScrollY();
329ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int count = group.getChildCount();
330ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            for (int i = count - 1; i >= 0; i--) {
331ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                final View child = group.getChildAt(i);
332ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
333ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                        y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
334ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                        canScroll(child, true, dx, x + scrollX - child.getLeft(),
335ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                                y + scrollY - child.getTop())) {
336ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    return true;
337ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
338ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            }
339ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
340ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
341ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return checkV && v.canScrollHorizontally((int) -dx);
342ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
343ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown}
344