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
193f8dd14451521d728fba548c7655d8fe531ed2efFilip Gruszczynskiimport android.app.Activity;
2006756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynskiimport android.content.BroadcastReceiver;
21ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.content.Context;
2206756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynskiimport android.content.Intent;
2306756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynskiimport android.content.IntentFilter;
2423958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynskiimport android.content.res.TypedArray;
25ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.util.AttributeSet;
26ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.util.Log;
27ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.MotionEvent;
28ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.VelocityTracker;
29ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.View;
30ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.ViewConfiguration;
31ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.ViewGroup;
326eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynskiimport android.view.ViewTreeObserver;
33ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.widget.FrameLayout;
34ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
35ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown/**
36ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * Special layout that finishes its activity when swiped away.
37ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown */
38ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownpublic class SwipeDismissLayout extends FrameLayout {
39ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private static final String TAG = "SwipeDismissLayout";
40ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
4156e533a5f69e930608f6caf478d4fa5afc7c5eecMindy Pereira    private static final float DISMISS_MIN_DRAG_WIDTH_RATIO = .33f;
4223958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski    private boolean mUseDynamicTranslucency = true;
43ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
44ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public interface OnDismissedListener {
45ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onDismissed(SwipeDismissLayout layout);
46ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
47ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
48ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public interface OnSwipeProgressChangedListener {
49ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        /**
50ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * Called when the layout has been swiped and the position of the window should change.
51ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         *
526c5c48a8b05e7668de551c3bf37aa3f33c7f0920Mark Renouf         * @param progress A number in [0, 1] representing how far to the
536c5c48a8b05e7668de551c3bf37aa3f33c7f0920Mark Renouf         * right the window has been swiped
546c5c48a8b05e7668de551c3bf37aa3f33c7f0920Mark Renouf         * @param translate A number in [0, w], where w is the width of the
55ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * layout. This is equivalent to progress * layout.getWidth().
56ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         */
57ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate);
58ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
59ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onSwipeCancelled(SwipeDismissLayout layout);
60ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
61ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
62ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    // Cached ViewConfiguration and system-wide constant values
63ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mSlop;
64ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mMinFlingVelocity;
65ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
66ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    // Transient properties
67ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mActiveTouchId;
68ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mDownX;
69ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mDownY;
70ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mSwiping;
71ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mDismissed;
72ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mDiscardIntercept;
73ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private VelocityTracker mVelocityTracker;
74ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mTranslationX;
75ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
76ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private OnDismissedListener mDismissedListener;
77ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private OnSwipeProgressChangedListener mProgressListener;
786eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    private ViewTreeObserver.OnEnterAnimationCompleteListener mOnEnterAnimationCompleteListener =
796eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski            new ViewTreeObserver.OnEnterAnimationCompleteListener() {
806eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                @Override
816eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                public void onEnterAnimationComplete() {
826eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    // SwipeDismissLayout assumes that the host Activity is translucent
836eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    // and temporarily disables translucency when it is fully visible.
846eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    // As soon as the user starts swiping, we will re-enable
856eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    // translucency.
8623958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski                    if (mUseDynamicTranslucency && getContext() instanceof Activity) {
876eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                        ((Activity) getContext()).convertFromTranslucent();
886eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    }
896eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                }
906eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski            };
9106756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski    private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
9206756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        @Override
9306756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        public void onReceive(Context context, Intent intent) {
9406756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski            if (mDismissed) {
9506756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski                dismiss();
9606756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski            } else {
9706756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski                cancel();
9806756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski            }
9906756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski            resetMembers();
10006756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        }
10106756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski    };
10206756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski    private IntentFilter mScreenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
103ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
10411b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf    private float mLastX;
10511b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf
106ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context) {
107ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context);
108ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
109ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
110ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
111ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context, AttributeSet attrs) {
112ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context, attrs);
113ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
114ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
115ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
116ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle) {
117ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context, attrs, defStyle);
118ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
119ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
120ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
121ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void init(Context context) {
12255555a3dab1f3b99a3299b90851624e9dc10f3ccMark Renouf        ViewConfiguration vc = ViewConfiguration.get(context);
123ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mSlop = vc.getScaledTouchSlop();
12411b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
12523958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski        TypedArray a = context.getTheme().obtainStyledAttributes(
12623958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski                com.android.internal.R.styleable.Theme);
12723958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski        mUseDynamicTranslucency = !a.hasValue(
12823958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski                com.android.internal.R.styleable.Window_windowIsTranslucent);
12923958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski        a.recycle();
130ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
131ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
132ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public void setOnDismissedListener(OnDismissedListener listener) {
133ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDismissedListener = listener;
134ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
135ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
136ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public void setOnSwipeProgressChangedListener(OnSwipeProgressChangedListener listener) {
137ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mProgressListener = listener;
138ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
139ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
140ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    @Override
1416eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    protected void onAttachedToWindow() {
1426eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        super.onAttachedToWindow();
1436eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        if (getContext() instanceof Activity) {
1446eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski            getViewTreeObserver().addOnEnterAnimationCompleteListener(
1456eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    mOnEnterAnimationCompleteListener);
1466eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        }
14706756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        getContext().registerReceiver(mScreenOffReceiver, mScreenOffFilter);
1486eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    }
1496eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski
1506eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    @Override
1516eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    protected void onDetachedFromWindow() {
15206756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        getContext().unregisterReceiver(mScreenOffReceiver);
1536eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        if (getContext() instanceof Activity) {
1546eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski            getViewTreeObserver().removeOnEnterAnimationCompleteListener(
1556eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski                    mOnEnterAnimationCompleteListener);
1566eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        }
15706756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        super.onDetachedFromWindow();
1586eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    }
1596eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski
1606eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    @Override
161ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public boolean onInterceptTouchEvent(MotionEvent ev) {
162ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        // offset because the view is translated during swipe
163ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        ev.offsetLocation(mTranslationX, 0);
164ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
165ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        switch (ev.getActionMasked()) {
166ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_DOWN:
167ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
168ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDownX = ev.getRawX();
169ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDownY = ev.getRawY();
170ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mActiveTouchId = ev.getPointerId(0);
171ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker = VelocityTracker.obtain();
172ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker.addMovement(ev);
173ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
174ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
175df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            case MotionEvent.ACTION_POINTER_DOWN:
176df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                int actionIndex = ev.getActionIndex();
177df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                mActiveTouchId = ev.getPointerId(actionIndex);
178df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                break;
179df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            case MotionEvent.ACTION_POINTER_UP:
180df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                actionIndex = ev.getActionIndex();
181df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                int pointerId = ev.getPointerId(actionIndex);
182df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                if (pointerId == mActiveTouchId) {
183df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    // This was our active pointer going up. Choose a new active pointer.
184df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    int newActionIndex = actionIndex == 0 ? 1 : 0;
185df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    mActiveTouchId = ev.getPointerId(newActionIndex);
186df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                }
187df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                break;
188df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh
189ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_CANCEL:
190ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_UP:
191ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
192ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
193ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
194ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_MOVE:
195ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mVelocityTracker == null || mDiscardIntercept) {
196ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
197ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
198ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
199ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                int pointerIndex = ev.findPointerIndex(mActiveTouchId);
200df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                if (pointerIndex == -1) {
201df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    Log.e(TAG, "Invalid pointer index: ignoring.");
202df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    mDiscardIntercept = true;
203df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    break;
204df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                }
205ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float dx = ev.getRawX() - mDownX;
206ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float x = ev.getX(pointerIndex);
207ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float y = ev.getY(pointerIndex);
208ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (dx != 0 && canScroll(this, false, dx, x, y)) {
209ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    mDiscardIntercept = true;
210ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
211ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
212ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateSwiping(ev);
213ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
214ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
215ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
216ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return !mDiscardIntercept && mSwiping;
217ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
218ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
219ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    @Override
220ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public boolean onTouchEvent(MotionEvent ev) {
221ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mVelocityTracker == null) {
222ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            return super.onTouchEvent(ev);
223ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
224026e204ee461e101fe251742f6ffb09b1747c804Gus Prevas        // offset because the view is translated during swipe
225026e204ee461e101fe251742f6ffb09b1747c804Gus Prevas        ev.offsetLocation(mTranslationX, 0);
226ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        switch (ev.getActionMasked()) {
227ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_UP:
228ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateDismiss(ev);
229ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mDismissed) {
230ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    dismiss();
231ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                } else if (mSwiping) {
232ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    cancel();
233ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
234ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
235ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
236ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
237ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_CANCEL:
238ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                cancel();
239ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
240ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
241ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
242ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_MOVE:
243ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker.addMovement(ev);
24411b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf                mLastX = ev.getRawX();
245ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateSwiping(ev);
246ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mSwiping) {
24723958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski                    if (mUseDynamicTranslucency && getContext() instanceof Activity) {
248625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski                        ((Activity) getContext()).convertToTranslucent(null, null);
249625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski                    }
250ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    setProgress(ev.getRawX() - mDownX);
251ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
252ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
253ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
254ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return true;
255ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
256ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
257ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void setProgress(float deltaX) {
258ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mTranslationX = deltaX;
2596c5c48a8b05e7668de551c3bf37aa3f33c7f0920Mark Renouf        if (mProgressListener != null && deltaX >= 0)  {
260ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX);
261ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
262ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
263ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
264ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void dismiss() {
265ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mDismissedListener != null) {
266ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mDismissedListener.onDismissed(this);
267ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
268ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
269ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
270ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    protected void cancel() {
27123958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski        if (mUseDynamicTranslucency && getContext() instanceof Activity) {
272625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski            ((Activity) getContext()).convertFromTranslucent();
273625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski        }
274ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mProgressListener != null) {
275ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mProgressListener.onSwipeCancelled(this);
276ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
277ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
278ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
279ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    /**
280ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * Resets internal members when canceling.
281ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     */
282ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void resetMembers() {
283ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mVelocityTracker != null) {
284ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mVelocityTracker.recycle();
285ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
286ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mVelocityTracker = null;
287ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mTranslationX = 0;
288ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDownX = 0;
289ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDownY = 0;
290ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mSwiping = false;
291ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDismissed = false;
292ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDiscardIntercept = false;
293ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
294ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
295ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void updateSwiping(MotionEvent ev) {
296ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (!mSwiping) {
297ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float deltaX = ev.getRawX() - mDownX;
298ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float deltaY = ev.getRawY() - mDownY;
299072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            if ((deltaX * deltaX) + (deltaY * deltaY) > mSlop * mSlop) {
30055555a3dab1f3b99a3299b90851624e9dc10f3ccMark Renouf                mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < Math.abs(deltaX);
301072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            } else {
302072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira                mSwiping = false;
303072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            }
304ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
305ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
306ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
307ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void updateDismiss(MotionEvent ev) {
308df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        float deltaX = ev.getRawX() - mDownX;
30955555a3dab1f3b99a3299b90851624e9dc10f3ccMark Renouf        mVelocityTracker.addMovement(ev);
31055555a3dab1f3b99a3299b90851624e9dc10f3ccMark Renouf        mVelocityTracker.computeCurrentVelocity(1000);
311ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (!mDismissed) {
312ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
313072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            if (deltaX > (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) &&
31411b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf                    ev.getRawX() >= mLastX) {
315ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDismissed = true;
316ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            }
317ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
318df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        // Check if the user tried to undo this.
319df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        if (mDismissed && mSwiping) {
320df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            // Check if the user's finger is actually back
32155555a3dab1f3b99a3299b90851624e9dc10f3ccMark Renouf            if (deltaX < (getWidth() * DISMISS_MIN_DRAG_WIDTH_RATIO) ||
32255555a3dab1f3b99a3299b90851624e9dc10f3ccMark Renouf                    // or user is flinging back left
32355555a3dab1f3b99a3299b90851624e9dc10f3ccMark Renouf                    mVelocityTracker.getXVelocity() < -mMinFlingVelocity) {
324df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                mDismissed = false;
325df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            }
326df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        }
327ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
328ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
329ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    /**
330ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * Tests scrollability within child views of v in the direction of dx.
331ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     *
332ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param v View to test for horizontal scrollability
333ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param checkV Whether the view v passed should itself be checked for scrollability (true),
334ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     *               or just its children (false).
335ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param dx Delta scrolled in pixels. Only the sign of this is used.
336ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param x X coordinate of the active touch point
337ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param y Y coordinate of the active touch point
338ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @return true if child views of v can be scrolled by delta of dx.
339ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     */
340ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    protected boolean canScroll(View v, boolean checkV, float dx, float x, float y) {
341ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (v instanceof ViewGroup) {
342ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final ViewGroup group = (ViewGroup) v;
343ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int scrollX = v.getScrollX();
344ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int scrollY = v.getScrollY();
345ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int count = group.getChildCount();
346ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            for (int i = count - 1; i >= 0; i--) {
347ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                final View child = group.getChildAt(i);
348ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
349ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                        y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
350ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                        canScroll(child, true, dx, x + scrollX - child.getLeft(),
351ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                                y + scrollY - child.getTop())) {
352ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    return true;
353ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
354ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            }
355ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
356ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
357ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return checkV && v.canScrollHorizontally((int) -dx);
358ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
359ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown}
360