SwipeHelper.java revision 96f4818ef41141649337d4dee09f837594e93d09
107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka/*
207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka * Copyright (C) 2011 The Android Open Source Project
307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka *
407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka * Licensed under the Apache License, Version 2.0 (the "License");
507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka * you may not use this file except in compliance with the License.
607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka * You may obtain a copy of the License at
707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka *
807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka *      http://www.apache.org/licenses/LICENSE-2.0
907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka *
1007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka * Unless required by applicable law or agreed to in writing, software
1107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka * distributed under the License is distributed on an "AS IS" BASIS,
1207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka * See the License for the specific language governing permissions and
1407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka * limitations under the License.
1507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka */
1607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
1707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkapackage com.android.systemui;
1807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
1907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.animation.Animator;
2007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.animation.ObjectAnimator;
2107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.animation.Animator.AnimatorListener;
2207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.animation.ValueAnimator;
2307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.animation.ValueAnimator.AnimatorUpdateListener;
2407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.graphics.RectF;
2507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.util.Log;
2607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.view.animation.LinearInterpolator;
2707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.view.MotionEvent;
2807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.view.VelocityTracker;
2907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkaimport android.view.View;
3007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
3107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurkapublic class SwipeHelper {
3207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    static final String TAG = "com.android.systemui.SwipeHelper";
3396f4818ef41141649337d4dee09f837594e93d09Daniel Sandler    private static final boolean DEBUG = false;
3407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private static final boolean DEBUG_INVALIDATE = false;
3507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
3607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
3707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public static final int X = 0;
3807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public static final int Y = 1;
3907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
4007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private boolean CONSTRAIN_SWIPE = true;
4107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private boolean FADE_OUT_DURING_SWIPE = true;
4207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
4307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
4407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
4507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private int MAX_ESCAPE_ANIMATION_DURATION = 500; // ms
460761e4cd95b6968dd4b1659a766ef504d13f6d50Daniel Sandler    private int MAX_DISMISS_VELOCITY = 1000; // dp/sec
4707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 250; // ms
4807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
4907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public static float ALPHA_FADE_START = 0.8f; // fraction of thumbnail width
5007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                                                 // where fade starts
5107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width
5207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                                              // beyond which alpha->0
5307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
5407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float mPagingTouchSlop;
5507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private Callback mCallback;
5607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private int mSwipeDirection;
5707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private VelocityTracker mVelocityTracker;
5807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
5907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float mInitialTouchPos;
6007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private boolean mDragging;
6107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private View mCurrView;
6207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float mDensityScale;
6307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
6407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public SwipeHelper(int swipeDirection, Callback callback, float densityScale,
6507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            float pagingTouchSlop) {
6607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mCallback = callback;
6707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mSwipeDirection = swipeDirection;
6807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mVelocityTracker = VelocityTracker.obtain();
6907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mDensityScale = densityScale;
7007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mPagingTouchSlop = pagingTouchSlop;
7107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
7207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
7307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public void setDensityScale(float densityScale) {
7407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mDensityScale = densityScale;
7507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
7607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
7707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public void setPagingTouchSlop(float pagingTouchSlop) {
7807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mPagingTouchSlop = pagingTouchSlop;
7907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
8007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
8107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float getPos(MotionEvent ev) {
8207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return mSwipeDirection == X ? ev.getX() : ev.getY();
8307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
8407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
8507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float getPos(View v) {
8607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return mSwipeDirection == X ? v.getX() : v.getY();
8707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
8807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
8907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float getVelocity(VelocityTracker vt) {
9007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return mSwipeDirection == X ? vt.getXVelocity() :
9107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                vt.getYVelocity();
9207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
9307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
9407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private ObjectAnimator createTranslationAnimation(View v, float newPos) {
9507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        ObjectAnimator anim = ObjectAnimator.ofFloat(v,
9607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                mSwipeDirection == X ? "translationX" : "translationY", newPos);
9707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return anim;
9807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
9907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
10007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float getPerpendicularVelocity(VelocityTracker vt) {
10107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return mSwipeDirection == X ? vt.getYVelocity() :
10207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                vt.getXVelocity();
10307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
10407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
10507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private void setTranslation(View v, float translate) {
10607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        if (mSwipeDirection == X) {
10707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            v.setTranslationX(translate);
10807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        } else {
10907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            v.setTranslationY(translate);
11007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        }
11107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
11207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
11307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float getSize(View v) {
11407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return mSwipeDirection == X ? v.getMeasuredWidth() :
11507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                v.getMeasuredHeight();
11607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
11707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
11807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float getContentSize(View v) {
11907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        View content = mCallback.getChildContentView(v);
12007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return getSize(content);
12107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
12207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
12307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    private float getAlphaForOffset(View view, float thumbSize) {
12407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        final float fadeSize = ALPHA_FADE_END * thumbSize;
12507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        float result = 1.0f;
12607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        float pos = getPos(view);
12707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        if (pos >= thumbSize * ALPHA_FADE_START) {
12807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            result = 1.0f - (pos - thumbSize * ALPHA_FADE_START) / fadeSize;
12907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        } else if (pos < thumbSize * (1.0f - ALPHA_FADE_START)) {
13007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            result = 1.0f + (thumbSize * ALPHA_FADE_START + pos) / fadeSize;
13107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        }
13207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return result;
13307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
13407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
135a375c94fca986d76d21f8cb9a3eb29f1ef88c4a8Daniel Sandler    // invalidate the view's own bounds all the way up the view hierarchy
136a375c94fca986d76d21f8cb9a3eb29f1ef88c4a8Daniel Sandler    public static void invalidateGlobalRegion(View view) {
137a375c94fca986d76d21f8cb9a3eb29f1ef88c4a8Daniel Sandler        invalidateGlobalRegion(
138a375c94fca986d76d21f8cb9a3eb29f1ef88c4a8Daniel Sandler            view,
139a375c94fca986d76d21f8cb9a3eb29f1ef88c4a8Daniel Sandler            new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
140a375c94fca986d76d21f8cb9a3eb29f1ef88c4a8Daniel Sandler    }
141a375c94fca986d76d21f8cb9a3eb29f1ef88c4a8Daniel Sandler
142a375c94fca986d76d21f8cb9a3eb29f1ef88c4a8Daniel Sandler    // invalidate a rectangle relative to the view's coordinate system all the way up the view
143a375c94fca986d76d21f8cb9a3eb29f1ef88c4a8Daniel Sandler    // hierarchy
144a375c94fca986d76d21f8cb9a3eb29f1ef88c4a8Daniel Sandler    public static void invalidateGlobalRegion(View view, RectF childBounds) {
14596f4818ef41141649337d4dee09f837594e93d09Daniel Sandler        //childBounds.offset(view.getTranslationX(), view.getTranslationY());
14607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        if (DEBUG_INVALIDATE)
14707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            Log.v(TAG, "-------------");
14807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        while (view.getParent() != null && view.getParent() instanceof View) {
14907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            view = (View) view.getParent();
15007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            view.getMatrix().mapRect(childBounds);
15107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            view.invalidate((int) Math.floor(childBounds.left),
15207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                            (int) Math.floor(childBounds.top),
15307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                            (int) Math.ceil(childBounds.right),
15407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                            (int) Math.ceil(childBounds.bottom));
15507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            if (DEBUG_INVALIDATE) {
15607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
15707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        + "," + (int) Math.floor(childBounds.top)
15807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        + "," + (int) Math.ceil(childBounds.right)
15907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        + "," + (int) Math.ceil(childBounds.bottom));
16007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            }
16107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        }
16207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
16307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
16407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public boolean onInterceptTouchEvent(MotionEvent ev) {
16507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        final int action = ev.getAction();
16607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
16707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        switch (action) {
16807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            case MotionEvent.ACTION_DOWN:
16907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                mDragging = false;
17007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                mCurrView = mCallback.getChildAtPosition(ev);
17107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                mVelocityTracker.clear();
17207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                mVelocityTracker.addMovement(ev);
17307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                mInitialTouchPos = getPos(ev);
17407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                break;
17507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            case MotionEvent.ACTION_MOVE:
17607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                if (mCurrView != null) {
17707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    mVelocityTracker.addMovement(ev);
17807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    float pos = getPos(ev);
17907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    float delta = pos - mInitialTouchPos;
18007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    if (Math.abs(delta) > mPagingTouchSlop) {
18107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        mCallback.onBeginDrag(mCurrView);
18207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        mDragging = true;
18307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        mInitialTouchPos = getPos(ev) - getPos(mCurrView);
18407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    }
18507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                }
18607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                break;
18707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            case MotionEvent.ACTION_UP:
18807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                mDragging = false;
18907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                mCurrView = null;
19007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                break;
19107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        }
19207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return mDragging;
19307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
19407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
19507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public void dismissChild(final View animView, float velocity) {
19607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        float newPos;
19707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        if (velocity < 0 || (velocity == 0 && getPos(animView) < 0)) {
19807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            newPos = -getSize(animView);
19907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        } else {
20007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            newPos = getSize(animView);
20107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        }
20207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        int duration = MAX_ESCAPE_ANIMATION_DURATION;
20307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        if (velocity != 0) {
20407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            duration = Math.min(duration,
20507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                                (int) (Math.abs(newPos - getPos(animView)) * 1000f / Math
20607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                                        .abs(velocity)));
20707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        }
20807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        ObjectAnimator anim = createTranslationAnimation(animView, newPos);
20907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        anim.setInterpolator(new LinearInterpolator());
21007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        anim.setDuration(duration);
21107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        anim.addListener(new AnimatorListener() {
21207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            public void onAnimationStart(Animator animation) {
21307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            }
21407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
21507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            public void onAnimationRepeat(Animator animation) {
21607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            }
21707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
21807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            public void onAnimationEnd(Animator animation) {
21907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                mCallback.onChildDismissed(animView);
22007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            }
22107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
22207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            public void onAnimationCancel(Animator animation) {
22307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                mCallback.onChildDismissed(animView);
22407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            }
22507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        });
22607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        anim.addUpdateListener(new AnimatorUpdateListener() {
22707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            public void onAnimationUpdate(ValueAnimator animation) {
22807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                if (FADE_OUT_DURING_SWIPE) {
22907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    animView.setAlpha(getAlphaForOffset(animView, getContentSize(animView)));
23007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                }
23107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                invalidateGlobalRegion(animView);
23207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            }
23307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        });
23407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        anim.start();
23507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
23607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
23707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public void snapChild(final View animView, float velocity) {
23807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        ObjectAnimator anim = createTranslationAnimation(animView, 0);
23907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        int duration = SNAP_ANIM_LEN;
24007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        anim.setDuration(duration);
24107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        anim.addUpdateListener(new AnimatorUpdateListener() {
24207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            public void onAnimationUpdate(ValueAnimator animation) {
24307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                if (FADE_OUT_DURING_SWIPE) {
24407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    animView.setAlpha(getAlphaForOffset(animView, getContentSize(animView)));
24507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                }
24607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                invalidateGlobalRegion(animView);
24707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            }
24807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        });
24907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        anim.start();
25007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
25107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
25207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public boolean onTouchEvent(MotionEvent ev) {
25307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        if (!mDragging) {
25407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            return false;
25507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        }
25607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
25707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        mVelocityTracker.addMovement(ev);
25807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        final int action = ev.getAction();
25907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        switch (action) {
26007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            case MotionEvent.ACTION_OUTSIDE:
26107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            case MotionEvent.ACTION_MOVE:
26207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                if (mCurrView != null) {
26307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    float delta = getPos(ev) - mInitialTouchPos;
26407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    // don't let items that can't be dismissed be dragged more than
26507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    // maxScrollDistance
26607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {
26707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        float size = getSize(mCurrView);
26807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        float maxScrollDistance = 0.15f * size;
26907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        if (Math.abs(delta) >= size) {
27007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                            delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
27107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        } else {
27207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                            delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2));
27307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        }
27407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    }
27507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    setTranslation(mCurrView, delta);
27607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    if (FADE_OUT_DURING_SWIPE) {
27707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        mCurrView.setAlpha(getAlphaForOffset(mCurrView, getContentSize(mCurrView)));
27807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    }
27907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    invalidateGlobalRegion(mCurrView);
28007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                }
28107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                break;
28207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            case MotionEvent.ACTION_UP:
28307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka            case MotionEvent.ACTION_CANCEL:
28407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                if (mCurrView != null) {
2850761e4cd95b6968dd4b1659a766ef504d13f6d50Daniel Sandler                    float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
28607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
28707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
28807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    float velocity = getVelocity(mVelocityTracker);
28907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
29007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
29107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    // Decide whether to dismiss the current view
29207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
29307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                            Math.abs(getPos(mCurrView)) > 0.4 * getSize(mCurrView);
29407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
29507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                            (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
29607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                            (velocity > 0) == (getPos(mCurrView) > 0);
29707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
29807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    boolean dismissChild = mCallback.canChildBeDismissed(mCurrView) &&
29907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                            (childSwipedFastEnough || childSwipedFarEnough);
30007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
30107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    if (dismissChild) {
30207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        // flingadingy
30307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
30407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    } else {
30507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        // snappity
30607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                        snapChild(mCurrView, velocity);
30707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                    }
30807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                }
30907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka                break;
31007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        }
31107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        return true;
31207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
31307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
31407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    public interface Callback {
31507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        View getChildAtPosition(MotionEvent ev);
31607d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
31707d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        View getChildContentView(View v);
31807d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
31907d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        boolean canChildBeDismissed(View v);
32007d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
32107d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        void onBeginDrag(View v);
32207d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka
32307d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka        void onChildDismissed(View v);
32407d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka    }
32507d4046ea914c999b7aaa587b7bfe81d548121f7Michael Jurka}
326