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
197d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burnsimport android.animation.Animator;
207d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burnsimport android.animation.TimeInterpolator;
217d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burnsimport android.animation.ValueAnimator;
227d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burnsimport android.animation.ValueAnimator.AnimatorUpdateListener;
233f8dd14451521d728fba548c7655d8fe531ed2efFilip Gruszczynskiimport android.app.Activity;
2406756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynskiimport android.content.BroadcastReceiver;
25ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.content.Context;
26d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burnsimport android.content.ContextWrapper;
2706756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynskiimport android.content.Intent;
2806756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynskiimport android.content.IntentFilter;
2923958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynskiimport android.content.res.TypedArray;
30ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.util.AttributeSet;
31ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.util.Log;
32ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.MotionEvent;
33ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.VelocityTracker;
34ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.View;
35ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.ViewConfiguration;
36ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.view.ViewGroup;
377d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burnsimport android.view.animation.DecelerateInterpolator;
38ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownimport android.widget.FrameLayout;
39ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
40ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown/**
41ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown * Special layout that finishes its activity when swiped away.
42ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown */
43ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brownpublic class SwipeDismissLayout extends FrameLayout {
44ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private static final String TAG = "SwipeDismissLayout";
45ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
465758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan    private static final float MAX_DIST_THRESHOLD = .33f;
475758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan    private static final float MIN_DIST_THRESHOLD = .1f;
48ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
49ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public interface OnDismissedListener {
50ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onDismissed(SwipeDismissLayout layout);
51ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
52ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
53ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public interface OnSwipeProgressChangedListener {
54ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        /**
55ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * Called when the layout has been swiped and the position of the window should change.
56ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         *
577d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns         * @param alpha A number in [0, 1] representing what the alpha transparency of the window
587d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns         * should be.
596c5c48a8b05e7668de551c3bf37aa3f33c7f0920Mark Renouf         * @param translate A number in [0, w], where w is the width of the
60ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         * layout. This is equivalent to progress * layout.getWidth().
61ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown         */
627d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        void onSwipeProgressChanged(SwipeDismissLayout layout, float alpha, float translate);
63ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
64ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        void onSwipeCancelled(SwipeDismissLayout layout);
65ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
66ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
67d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns    private boolean mIsWindowNativelyTranslucent;
68d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns
69ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    // Cached ViewConfiguration and system-wide constant values
70ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mSlop;
71ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mMinFlingVelocity;
72ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
73ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    // Transient properties
74ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private int mActiveTouchId;
75ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mDownX;
76ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private float mDownY;
775758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan    private float mLastX;
78ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mSwiping;
79ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mDismissed;
80ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private boolean mDiscardIntercept;
81ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private VelocityTracker mVelocityTracker;
827d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns    private boolean mBlockGesture = false;
8339dd370d8e0d04e1a860afc4136ad6bd6d521018Michael Kwan    private boolean mActivityTranslucencyConverted = false;
847d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
857d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns    private final DismissAnimator mDismissAnimator = new DismissAnimator();
86ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
87ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private OnDismissedListener mDismissedListener;
88ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private OnSwipeProgressChangedListener mProgressListener;
8906756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski    private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
908a148a3b1de69a018867506a42774a47870f6f6cBryce Lee        private Runnable mRunnable = new Runnable() {
918a148a3b1de69a018867506a42774a47870f6f6cBryce Lee            @Override
928a148a3b1de69a018867506a42774a47870f6f6cBryce Lee            public void run() {
938a148a3b1de69a018867506a42774a47870f6f6cBryce Lee                if (mDismissed) {
948a148a3b1de69a018867506a42774a47870f6f6cBryce Lee                    dismiss();
958a148a3b1de69a018867506a42774a47870f6f6cBryce Lee                } else {
968a148a3b1de69a018867506a42774a47870f6f6cBryce Lee                    cancel();
978a148a3b1de69a018867506a42774a47870f6f6cBryce Lee                }
988a148a3b1de69a018867506a42774a47870f6f6cBryce Lee                resetMembers();
998a148a3b1de69a018867506a42774a47870f6f6cBryce Lee            }
1008a148a3b1de69a018867506a42774a47870f6f6cBryce Lee        };
1018a148a3b1de69a018867506a42774a47870f6f6cBryce Lee
10206756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        @Override
10306756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        public void onReceive(Context context, Intent intent) {
1048a148a3b1de69a018867506a42774a47870f6f6cBryce Lee            post(mRunnable);
10506756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        }
10606756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski    };
10706756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski    private IntentFilter mScreenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
108ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
10911b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf
110f7964be938338380654aaa41317b28335ed19084Michael Kwan    private boolean mDismissable = true;
111f7964be938338380654aaa41317b28335ed19084Michael Kwan
112ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context) {
113ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context);
114ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
115ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
116ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
117ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context, AttributeSet attrs) {
118ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context, attrs);
119ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
120ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
121ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
122ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public SwipeDismissLayout(Context context, AttributeSet attrs, int defStyle) {
123ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        super(context, attrs, defStyle);
124ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        init(context);
125ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
126ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
127ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void init(Context context) {
12855555a3dab1f3b99a3299b90851624e9dc10f3ccMark Renouf        ViewConfiguration vc = ViewConfiguration.get(context);
129ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mSlop = vc.getScaledTouchSlop();
13011b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf        mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
13123958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski        TypedArray a = context.getTheme().obtainStyledAttributes(
13223958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski                com.android.internal.R.styleable.Theme);
133d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns        mIsWindowNativelyTranslucent = a.getBoolean(
134d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns                com.android.internal.R.styleable.Window_windowIsTranslucent, false);
13523958c6e2ffd4e76a88bc553fe2874d2f5f3f5e2Filip Gruszczynski        a.recycle();
136ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
137ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
138ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public void setOnDismissedListener(OnDismissedListener listener) {
139ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDismissedListener = listener;
140ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
141ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
142ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public void setOnSwipeProgressChangedListener(OnSwipeProgressChangedListener listener) {
143ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mProgressListener = listener;
144ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
145ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
146ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    @Override
1476eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    protected void onAttachedToWindow() {
1486eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski        super.onAttachedToWindow();
14906756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        getContext().registerReceiver(mScreenOffReceiver, mScreenOffFilter);
1506eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    }
1516eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski
1526eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    @Override
1536eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    protected void onDetachedFromWindow() {
15406756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        getContext().unregisterReceiver(mScreenOffReceiver);
15506756f352d2046246bc147fa1f55d1dfbfcb37f5Filip Gruszczynski        super.onDetachedFromWindow();
1566eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    }
1576eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski
1586eafa902cbc15fa35f8f0dfb5e559673fa67f637Filip Gruszczynski    @Override
159ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public boolean onInterceptTouchEvent(MotionEvent ev) {
1607d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        checkGesture((ev));
1617d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        if (mBlockGesture) {
1627d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            return true;
1637d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
164f7964be938338380654aaa41317b28335ed19084Michael Kwan        if (!mDismissable) {
165f7964be938338380654aaa41317b28335ed19084Michael Kwan            return super.onInterceptTouchEvent(ev);
166f7964be938338380654aaa41317b28335ed19084Michael Kwan        }
167f7964be938338380654aaa41317b28335ed19084Michael Kwan
168390120b925398c754b4f785fc12a8def0d09c09bMichael Kwan        // Offset because the view is translated during swipe, match X with raw X. Active touch
169390120b925398c754b4f785fc12a8def0d09c09bMichael Kwan        // coordinates are mostly used by the velocity tracker, so offset it to match the raw
170390120b925398c754b4f785fc12a8def0d09c09bMichael Kwan        // coordinates which is what is primarily used elsewhere.
171390120b925398c754b4f785fc12a8def0d09c09bMichael Kwan        ev.offsetLocation(ev.getRawX() - ev.getX(), 0);
172ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
173ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        switch (ev.getActionMasked()) {
174ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_DOWN:
175ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
176ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDownX = ev.getRawX();
177ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDownY = ev.getRawY();
178ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mActiveTouchId = ev.getPointerId(0);
1795758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                mVelocityTracker = VelocityTracker.obtain("int1");
180ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker.addMovement(ev);
181ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
182ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
183df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            case MotionEvent.ACTION_POINTER_DOWN:
184df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                int actionIndex = ev.getActionIndex();
185df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                mActiveTouchId = ev.getPointerId(actionIndex);
186df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                break;
187df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            case MotionEvent.ACTION_POINTER_UP:
188df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                actionIndex = ev.getActionIndex();
189df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                int pointerId = ev.getPointerId(actionIndex);
190df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                if (pointerId == mActiveTouchId) {
191df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    // This was our active pointer going up. Choose a new active pointer.
192df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    int newActionIndex = actionIndex == 0 ? 1 : 0;
193df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    mActiveTouchId = ev.getPointerId(newActionIndex);
194df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                }
195df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                break;
196df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh
197ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_CANCEL:
198ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_UP:
199ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
200ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
201ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
202ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_MOVE:
203ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mVelocityTracker == null || mDiscardIntercept) {
204ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
205ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
206ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
207ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                int pointerIndex = ev.findPointerIndex(mActiveTouchId);
208df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                if (pointerIndex == -1) {
209df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    Log.e(TAG, "Invalid pointer index: ignoring.");
210df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    mDiscardIntercept = true;
211df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                    break;
212df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                }
213ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float dx = ev.getRawX() - mDownX;
214ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float x = ev.getX(pointerIndex);
215ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                float y = ev.getY(pointerIndex);
216ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (dx != 0 && canScroll(this, false, dx, x, y)) {
217ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    mDiscardIntercept = true;
218ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
219ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
220ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateSwiping(ev);
221ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
222ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
223ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
224ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return !mDiscardIntercept && mSwiping;
225ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
226ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
227ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    @Override
228ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    public boolean onTouchEvent(MotionEvent ev) {
2297d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        checkGesture((ev));
2307d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        if (mBlockGesture) {
2317d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            return true;
2327d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
233f7964be938338380654aaa41317b28335ed19084Michael Kwan        if (mVelocityTracker == null || !mDismissable) {
234ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            return super.onTouchEvent(ev);
235ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
236390120b925398c754b4f785fc12a8def0d09c09bMichael Kwan
237390120b925398c754b4f785fc12a8def0d09c09bMichael Kwan        // Offset because the view is translated during swipe, match X with raw X. Active touch
238390120b925398c754b4f785fc12a8def0d09c09bMichael Kwan        // coordinates are mostly used by the velocity tracker, so offset it to match the raw
239390120b925398c754b4f785fc12a8def0d09c09bMichael Kwan        // coordinates which is what is primarily used elsewhere.
240390120b925398c754b4f785fc12a8def0d09c09bMichael Kwan        ev.offsetLocation(ev.getRawX() - ev.getX(), 0);
241390120b925398c754b4f785fc12a8def0d09c09bMichael Kwan
242ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        switch (ev.getActionMasked()) {
243ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_UP:
244ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateDismiss(ev);
245ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mDismissed) {
2467d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    mDismissAnimator.animateDismissal(ev.getRawX() - mDownX);
2475758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                } else if (mSwiping
2485758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                        // Only trigger animation if we had a MOVE event that would shift the
2495758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                        // underlying view, otherwise the animation would be janky.
2505758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                        && mLastX != Integer.MIN_VALUE) {
2517d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    mDismissAnimator.animateRecovery(ev.getRawX() - mDownX);
252ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
253ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
254ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
255ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
256ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_CANCEL:
257ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                cancel();
258ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                resetMembers();
259ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                break;
260ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
261ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            case MotionEvent.ACTION_MOVE:
262ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mVelocityTracker.addMovement(ev);
26311b1469144d73ad0280ef9d0c2e478d180f17074Mark Renouf                mLastX = ev.getRawX();
264ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                updateSwiping(ev);
265ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (mSwiping) {
266ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    setProgress(ev.getRawX() - mDownX);
267ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    break;
268ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
269ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
270ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return true;
271ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
272ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
273ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void setProgress(float deltaX) {
2746c5c48a8b05e7668de551c3bf37aa3f33c7f0920Mark Renouf        if (mProgressListener != null && deltaX >= 0)  {
2757d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mProgressListener.onSwipeProgressChanged(
2767d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    this, progressToAlpha(deltaX / getWidth()), deltaX);
277ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
278ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
279ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
280ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void dismiss() {
281ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mDismissedListener != null) {
282ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mDismissedListener.onDismissed(this);
283ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
284ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
285ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
286ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    protected void cancel() {
287d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns        if (!mIsWindowNativelyTranslucent) {
288d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns            Activity activity = findActivity();
28939dd370d8e0d04e1a860afc4136ad6bd6d521018Michael Kwan            if (activity != null && mActivityTranslucencyConverted) {
290d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns                activity.convertFromTranslucent();
29139dd370d8e0d04e1a860afc4136ad6bd6d521018Michael Kwan                mActivityTranslucencyConverted = false;
292d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns            }
293625ec4849118f061a99558ad558b16020435a88dFilip Gruszczynski        }
294ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mProgressListener != null) {
295ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mProgressListener.onSwipeCancelled(this);
296ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
297ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
298ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
299ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    /**
300ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * Resets internal members when canceling.
301ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     */
302ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void resetMembers() {
303ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (mVelocityTracker != null) {
304ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            mVelocityTracker.recycle();
305ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
306ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mVelocityTracker = null;
307ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDownX = 0;
3085758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan        mLastX = Integer.MIN_VALUE;
309ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDownY = 0;
310ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mSwiping = false;
311ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDismissed = false;
312ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        mDiscardIntercept = false;
313ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
314ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
315ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void updateSwiping(MotionEvent ev) {
316d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns        boolean oldSwiping = mSwiping;
317ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (!mSwiping) {
318ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float deltaX = ev.getRawX() - mDownX;
319ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            float deltaY = ev.getRawY() - mDownY;
320072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            if ((deltaX * deltaX) + (deltaY * deltaY) > mSlop * mSlop) {
32155555a3dab1f3b99a3299b90851624e9dc10f3ccMark Renouf                mSwiping = deltaX > mSlop * 2 && Math.abs(deltaY) < Math.abs(deltaX);
322072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            } else {
323072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira                mSwiping = false;
324072c6033fd48900f9f522aad1c984055e38d8380Mindy Pereira            }
325ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
326d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns
327d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns        if (mSwiping && !oldSwiping) {
328d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns            // Swiping has started
329d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns            if (!mIsWindowNativelyTranslucent) {
330d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns                Activity activity = findActivity();
331d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns                if (activity != null) {
33239dd370d8e0d04e1a860afc4136ad6bd6d521018Michael Kwan                    mActivityTranslucencyConverted = activity.convertToTranslucent(null, null);
333d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns                }
334d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns            }
335d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns        }
336ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
337ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
338ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    private void updateDismiss(MotionEvent ev) {
339df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        float deltaX = ev.getRawX() - mDownX;
3405758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan        // Don't add the motion event as an UP event would clear the velocity tracker
34155555a3dab1f3b99a3299b90851624e9dc10f3ccMark Renouf        mVelocityTracker.computeCurrentVelocity(1000);
3425758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan        float xVelocity = mVelocityTracker.getXVelocity();
3435758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan        if (mLastX == Integer.MIN_VALUE) {
3445758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan            // If there's no changes to mLastX, we have only one point of data, and therefore no
3455758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan            // velocity. Estimate velocity from just the up and down event in that case.
3465758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan            xVelocity = deltaX / ((ev.getEventTime() - ev.getDownTime()) / 1000);
3475758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan        }
348ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (!mDismissed) {
3495758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan            // Adjust the distance threshold linearly between the min and max threshold based on the
3505758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan            // x-velocity scaled with the the fling threshold speed
3515758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan            float distanceThreshold = getWidth() * Math.max(
3525758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                    Math.min((MIN_DIST_THRESHOLD - MAX_DIST_THRESHOLD)
3535758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                            * xVelocity / mMinFlingVelocity // scale x-velocity with fling velocity
3545758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                            + MAX_DIST_THRESHOLD, // offset to start at max threshold
3555758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                            MAX_DIST_THRESHOLD), // cap at max threshold
3565758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                    MIN_DIST_THRESHOLD); // bottom out at min threshold
3575758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan            if ((deltaX > distanceThreshold && ev.getRawX() >= mLastX)
3585758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan                    || xVelocity >= mMinFlingVelocity) {
359ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                mDismissed = true;
360ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            }
361ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
362df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        // Check if the user tried to undo this.
363df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        if (mDismissed && mSwiping) {
3648a9b27773b2bd6f84a861bb0d38dcfebb3b8cfa7Michael Kwan            // Check if the user's finger is actually flinging back to left
3655758a9a94c7ca1a196b3d1c51b05ec2d937f0b59Michael Kwan            if (xVelocity < -mMinFlingVelocity) {
366df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh                mDismissed = false;
367df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh            }
368df4ee5c8ac0830b23451ea466d48487f8b2205a0Justin Koh        }
369ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
370ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
371ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    /**
372ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * Tests scrollability within child views of v in the direction of dx.
373ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     *
374ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param v View to test for horizontal scrollability
375ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param checkV Whether the view v passed should itself be checked for scrollability (true),
376ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     *               or just its children (false).
377ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param dx Delta scrolled in pixels. Only the sign of this is used.
378ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param x X coordinate of the active touch point
379ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @param y Y coordinate of the active touch point
380ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     * @return true if child views of v can be scrolled by delta of dx.
381ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown     */
382ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    protected boolean canScroll(View v, boolean checkV, float dx, float x, float y) {
383ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        if (v instanceof ViewGroup) {
384ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final ViewGroup group = (ViewGroup) v;
385ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int scrollX = v.getScrollX();
386ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int scrollY = v.getScrollY();
387ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            final int count = group.getChildCount();
388ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            for (int i = count - 1; i >= 0; i--) {
389ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                final View child = group.getChildAt(i);
390ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
391ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                        y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
392ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                        canScroll(child, true, dx, x + scrollX - child.getLeft(),
393ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                                y + scrollY - child.getTop())) {
394ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                    return true;
395ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown                }
396ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown            }
397ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        }
398ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown
399ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown        return checkV && v.canScrollHorizontally((int) -dx);
400ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown    }
401f7964be938338380654aaa41317b28335ed19084Michael Kwan
402f7964be938338380654aaa41317b28335ed19084Michael Kwan    public void setDismissable(boolean dismissable) {
403f7964be938338380654aaa41317b28335ed19084Michael Kwan        if (!dismissable && mDismissable) {
404f7964be938338380654aaa41317b28335ed19084Michael Kwan            cancel();
405f7964be938338380654aaa41317b28335ed19084Michael Kwan            resetMembers();
406f7964be938338380654aaa41317b28335ed19084Michael Kwan        }
407f7964be938338380654aaa41317b28335ed19084Michael Kwan
408f7964be938338380654aaa41317b28335ed19084Michael Kwan        mDismissable = dismissable;
409f7964be938338380654aaa41317b28335ed19084Michael Kwan    }
4107d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4117d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns    private void checkGesture(MotionEvent ev) {
4127d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
4137d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mBlockGesture = mDismissAnimator.isAnimating();
4147d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
4157d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns    }
4167d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4177d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns    private float progressToAlpha(float progress) {
4187d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        return 1 - progress * progress * progress;
4197d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns    }
4207d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
421d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns    private Activity findActivity() {
422d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns        Context context = getContext();
423d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns        while (context instanceof ContextWrapper) {
424d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns            if (context instanceof Activity) {
425d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns                return (Activity) context;
426d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns            }
427d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns            context = ((ContextWrapper) context).getBaseContext();
428d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns        }
429d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns        return null;
430d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns    }
431d7a313fb118fb6f5e5eaae6acff253038a85da4bNed Burns
4327d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns    private class DismissAnimator implements AnimatorUpdateListener, Animator.AnimatorListener {
4337d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        private final TimeInterpolator DISMISS_INTERPOLATOR = new DecelerateInterpolator(1.5f);
4347d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        private final long DISMISS_DURATION = 250;
4357d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4367d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        private final ValueAnimator mDismissAnimator = new ValueAnimator();
4377d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        private boolean mWasCanceled = false;
4387d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        private boolean mDismissOnComplete = false;
4397d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4407d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        /* package */ DismissAnimator() {
4417d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mDismissAnimator.addUpdateListener(this);
4427d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mDismissAnimator.addListener(this);
4437d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
4447d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4457d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        /* package */ void animateDismissal(float currentTranslation) {
4467d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            animate(
4477d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    currentTranslation / getWidth(),
4487d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    1,
4497d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    DISMISS_DURATION,
4507d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    DISMISS_INTERPOLATOR,
4517d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    true /* dismiss */);
4527d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
4537d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4547d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        /* package */ void animateRecovery(float currentTranslation) {
4557d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            animate(
4567d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    currentTranslation / getWidth(),
4577d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    0,
4587d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    DISMISS_DURATION,
4597d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    DISMISS_INTERPOLATOR,
4607d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    false /* don't dismiss */);
4617d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
4627d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4637d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        /* package */ boolean isAnimating() {
4647d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            return mDismissAnimator.isStarted();
4657d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
4667d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4677d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        private void animate(float from, float to, long duration, TimeInterpolator interpolator,
4687d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                boolean dismissOnComplete) {
4697d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mDismissAnimator.cancel();
4707d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mDismissOnComplete = dismissOnComplete;
4717d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mDismissAnimator.setFloatValues(from, to);
4727d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mDismissAnimator.setDuration(duration);
4737d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mDismissAnimator.setInterpolator(interpolator);
4747d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mDismissAnimator.start();
4757d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
4767d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4777d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        @Override
4787d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        public void onAnimationUpdate(ValueAnimator animation) {
4797d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            float value = (Float) animation.getAnimatedValue();
4807d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            setProgress(value * getWidth());
4817d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
4827d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4837d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        @Override
4847d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        public void onAnimationStart(Animator animation) {
4857d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mWasCanceled = false;
4867d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
4877d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4887d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        @Override
4897d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        public void onAnimationCancel(Animator animation) {
4907d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            mWasCanceled = true;
4917d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
4927d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
4937d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        @Override
4947d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        public void onAnimationEnd(Animator animation) {
4957d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            if (!mWasCanceled) {
4967d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                if (mDismissOnComplete) {
4977d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    dismiss();
4987d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                } else {
4997d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                    cancel();
5007d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns                }
5017d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns            }
5027d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
5037d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns
5047d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        @Override
5057d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        public void onAnimationRepeat(Animator animation) {
5067d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns        }
5077d6cb913de9b51dba0bae79e527b7d4fe79eb35dNed Burns    }
508ca6234e084a71e0c968cff404620298bcd971fccWill Haldean Brown}
509