SwipeHelper.java revision 5a1db1cd6a8addc0401529454bde6e1d15f215a0
111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee/*
211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * Copyright (C) 2012 Google Inc.
311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * Licensed to The Android Open Source Project.
411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee *
511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * Licensed under the Apache License, Version 2.0 (the "License");
611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * you may not use this file except in compliance with the License.
711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * You may obtain a copy of the License at
811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee *
911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee *      http://www.apache.org/licenses/LICENSE-2.0
1011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee *
1111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * Unless required by applicable law or agreed to in writing, software
1211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * distributed under the License is distributed on an "AS IS" BASIS,
1311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * See the License for the specific language governing permissions and
1511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * limitations under the License.
1611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee */
1711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
1811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leepackage com.android.dialer.list;
1911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
2011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.animation.Animator;
2111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.animation.AnimatorListenerAdapter;
2211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.animation.ObjectAnimator;
2311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.animation.ValueAnimator;
2411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.animation.ValueAnimator.AnimatorUpdateListener;
2511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.content.Context;
2611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.content.res.Resources;
2711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.graphics.RectF;
2811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.util.Log;
2911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.view.MotionEvent;
3011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.view.VelocityTracker;
3111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.view.View;
3211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport android.view.animation.LinearInterpolator;
3311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
3411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leeimport com.android.dialer.R;
3511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
3611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee/**
3711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee * Copy of packages/apps/UnifiedEmail - com.android.mail.ui.SwipeHelper with changes.
3811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee */
3911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Leepublic class SwipeHelper {
4011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    static final String TAG = SwipeHelper.class.getSimpleName();
4111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static final boolean DEBUG_INVALIDATE = false;
4211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static final boolean CONSTRAIN_SWIPE = true;
4311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static final boolean FADE_OUT_DURING_SWIPE = true;
4411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
4511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static final boolean LOG_SWIPE_DISMISS_VELOCITY = false; // STOPSHIP - DEBUG ONLY
4611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
4711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static final int IS_SWIPEABLE_TAG = R.id.is_swipeable_tag;
4811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static final Object IS_SWIPEABLE = new Object();
4911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
5011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static final int X = 0;
5111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static final int Y = 1;
5211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
5311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static LinearInterpolator sLinearInterpolator = new LinearInterpolator();
5411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
5511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static int SWIPE_ESCAPE_VELOCITY = -1;
5611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static int DEFAULT_ESCAPE_ANIMATION_DURATION;
5711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static int MAX_ESCAPE_ANIMATION_DURATION;
5811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static int MAX_DISMISS_VELOCITY;
5911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static int SNAP_ANIM_LEN;
6011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static int SWIPE_SCROLL_SLOP;
6111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static float MIN_SWIPE;
6211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static float MIN_VERT;
6311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static float MIN_LOCK;
6411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
6511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static float ALPHA_FADE_START = 0f; // fraction of thumbnail width
6611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                                               // where fade starts
6711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    static final float ALPHA_FADE_END = 0.7f; // fraction of thumbnail width
6811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                                              // beyond which alpha->0
6911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static final float FACTOR = 1.2f;
7011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
7111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private static final int PROTECTION_PADDING = 50;
7211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
7311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float mMinAlpha = 0.3f;
7411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
7511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float mPagingTouchSlop;
7611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private final SwipeHelperCallback mCallback;
7711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private final int mSwipeDirection;
7811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private final VelocityTracker mVelocityTracker;
7911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
8011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float mInitialTouchPosX;
8111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private boolean mDragging;
8211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private View mCurrView;
8311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private View mCurrAnimView;
8411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private boolean mCanCurrViewBeDimissed;
8511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float mDensityScale;
8611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float mLastY;
8711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float mInitialTouchPosY;
8811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
8911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float mStartAlpha;
9011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private boolean mProtected = false;
9111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
9211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public SwipeHelper(Context context, int swipeDirection, SwipeHelperCallback callback, float densityScale,
9311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            float pagingTouchSlop) {
9411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mCallback = callback;
9511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mSwipeDirection = swipeDirection;
9611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mVelocityTracker = VelocityTracker.obtain();
9711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mDensityScale = densityScale;
9811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mPagingTouchSlop = pagingTouchSlop;
9911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (SWIPE_ESCAPE_VELOCITY == -1) {
10011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            Resources res = context.getResources();
10111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            SWIPE_ESCAPE_VELOCITY = res.getInteger(R.integer.swipe_escape_velocity);
10211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            DEFAULT_ESCAPE_ANIMATION_DURATION = res.getInteger(R.integer.escape_animation_duration);
10311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            MAX_ESCAPE_ANIMATION_DURATION = res.getInteger(R.integer.max_escape_animation_duration);
10411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            MAX_DISMISS_VELOCITY = res.getInteger(R.integer.max_dismiss_velocity);
10511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            SNAP_ANIM_LEN = res.getInteger(R.integer.snap_animation_duration);
10611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            SWIPE_SCROLL_SLOP = res.getInteger(R.integer.swipe_scroll_slop);
10711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            MIN_SWIPE = res.getDimension(R.dimen.min_swipe);
10811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            MIN_VERT = res.getDimension(R.dimen.min_vert);
10911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            MIN_LOCK = res.getDimension(R.dimen.min_lock);
11011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
11111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
11211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
11311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void setDensityScale(float densityScale) {
11411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mDensityScale = densityScale;
11511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
11611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
11711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void setPagingTouchSlop(float pagingTouchSlop) {
11811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mPagingTouchSlop = pagingTouchSlop;
11911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
12011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
12111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float getVelocity(VelocityTracker vt) {
12211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return mSwipeDirection == X ? vt.getXVelocity() :
12311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                vt.getYVelocity();
12411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
12511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
12611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private ObjectAnimator createTranslationAnimation(View v, float newPos) {
12711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        ObjectAnimator anim = ObjectAnimator.ofFloat(v,
12811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mSwipeDirection == X ? "translationX" : "translationY", newPos);
12911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return anim;
13011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
13111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
13211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private ObjectAnimator createDismissAnimation(View v, float newPos, int duration) {
13311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        ObjectAnimator anim = createTranslationAnimation(v, newPos);
13411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.setInterpolator(sLinearInterpolator);
13511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.setDuration(duration);
13611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return anim;
13711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
13811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
13911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float getPerpendicularVelocity(VelocityTracker vt) {
14011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return mSwipeDirection == X ? vt.getYVelocity() :
14111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                vt.getXVelocity();
14211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
14311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
14411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private void setTranslation(View v, float translate) {
14511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (mSwipeDirection == X) {
14611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            v.setTranslationX(translate);
14711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        } else {
14811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            v.setTranslationY(translate);
14911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
15011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
15111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
15211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float getSize(View v) {
15311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return mSwipeDirection == X ? v.getMeasuredWidth() :
15411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                v.getMeasuredHeight();
15511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
15611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
15711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void setMinAlpha(float minAlpha) {
15811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mMinAlpha = minAlpha;
15911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
16011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
16111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float getAlphaForOffset(View view) {
16211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        float viewSize = getSize(view);
16311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final float fadeSize = ALPHA_FADE_END * viewSize;
16411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        float result = mStartAlpha;
16511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        float pos = view.getTranslationX();
16611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (pos >= viewSize * ALPHA_FADE_START) {
16711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            result = mStartAlpha - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
16811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        } else if (pos < viewSize * (mStartAlpha - ALPHA_FADE_START)) {
16911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            result = mStartAlpha + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
17011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
17111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return Math.max(mMinAlpha, result);
17211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
17311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
17411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    // invalidate the view's own bounds all the way up the view hierarchy
17511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static void invalidateGlobalRegion(View view) {
17611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        invalidateGlobalRegion(
17711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                view,
17811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
17911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
18011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
18111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    // invalidate a rectangle relative to the view's coordinate system all the way up the view
18211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    // hierarchy
18311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static void invalidateGlobalRegion(View view, RectF childBounds) {
18411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        // childBounds.offset(view.getTranslationX(), view.getTranslationY());
18511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (DEBUG_INVALIDATE)
18611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            Log.v(TAG, "-------------");
18711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        while (view.getParent() != null && view.getParent() instanceof View) {
18811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            view = (View) view.getParent();
18911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            view.getMatrix().mapRect(childBounds);
19011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            view.invalidate((int) Math.floor(childBounds.left),
19111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    (int) Math.floor(childBounds.top),
19211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    (int) Math.ceil(childBounds.right),
19311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    (int) Math.ceil(childBounds.bottom));
19411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            if (DEBUG_INVALIDATE) {
19511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
19611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        + "," + (int) Math.floor(childBounds.top)
19711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        + "," + (int) Math.ceil(childBounds.right)
19811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        + "," + (int) Math.ceil(childBounds.bottom));
19911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
20011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
20111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
20211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
20311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public boolean onInterceptTouchEvent(MotionEvent ev) {
20411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final int action = ev.getAction();
20511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        switch (action) {
20611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_DOWN:
20711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mLastY = ev.getY();
20811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mDragging = false;
20911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mCurrView = mCallback.getChildAtPosition(ev);
21011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mVelocityTracker.clear();
21111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (mCurrView != null) {
21211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mCurrAnimView = mCallback.getChildContentView(mCurrView);
21311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mStartAlpha = mCurrAnimView.getAlpha();
21411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
21511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mVelocityTracker.addMovement(ev);
21611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mInitialTouchPosX = ev.getX();
21711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mInitialTouchPosY = ev.getY();
21811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
21911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                break;
22011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_MOVE:
22111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (mCurrView != null) {
22211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // Check the movement direction.
22311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (mLastY >= 0 && !mDragging) {
22411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float currY = ev.getY();
22511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float currX = ev.getX();
22611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float deltaY = Math.abs(currY - mInitialTouchPosY);
22711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float deltaX = Math.abs(currX - mInitialTouchPosX);
22811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        if (deltaY > SWIPE_SCROLL_SLOP && deltaY > (FACTOR * deltaX)) {
22911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            mLastY = ev.getY();
23011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            mCallback.onScroll();
23111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            return false;
23211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        }
23311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
23411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mVelocityTracker.addMovement(ev);
23511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float pos = ev.getX();
23611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float delta = pos - mInitialTouchPosX;
23711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (Math.abs(delta) > mPagingTouchSlop) {
23811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mCallback.onBeginDrag(mCallback.getChildContentView(mCurrView));
23911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mDragging = true;
24011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mInitialTouchPosX = ev.getX() - mCurrAnimView.getTranslationX();
24111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mInitialTouchPosY = ev.getY();
24211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
24311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
24411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mLastY = ev.getY();
24511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                break;
24611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_UP:
24711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_CANCEL:
24811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mDragging = false;
24911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mCurrView = null;
25011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mCurrAnimView = null;
25111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mLastY = -1;
25211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                break;
25311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
25411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return mDragging;
25511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
25611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
25711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    /**
25811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     * @param view The view to be dismissed
25911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     * @param velocity The desired pixels/second speed at which the view should
26011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     *            move
26111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     */
26211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private void dismissChild(final View view, float velocity) {
26311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final View animView = mCallback.getChildContentView(view);
26411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
26511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        float newPos = determinePos(animView, velocity);
26611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        int duration = determineDuration(animView, newPos, velocity);
26711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
26811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
26911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        ObjectAnimator anim = createDismissAnimation(animView, newPos, duration);
27011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.addListener(new AnimatorListenerAdapter() {
27111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
27211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationEnd(Animator animation) {
27311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mCallback.onChildDismissed(mCurrView);
27411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                animView.setLayerType(View.LAYER_TYPE_NONE, null);
27511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
27611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        });
27711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.addUpdateListener(new AnimatorUpdateListener() {
27811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
27911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationUpdate(ValueAnimator animation) {
28011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
28111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    animView.setAlpha(getAlphaForOffset(animView));
28211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
28311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                invalidateGlobalRegion(animView);
28411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
28511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        });
28611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.start();
28711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
28811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
28911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private int determineDuration(View animView, float newPos, float velocity) {
29011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        int duration = MAX_ESCAPE_ANIMATION_DURATION;
29111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (velocity != 0) {
29211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            duration = Math
29311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    .min(duration,
29411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            (int) (Math.abs(newPos - animView.getTranslationX()) * 1000f / Math
29511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                                    .abs(velocity)));
29611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        } else {
29711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
29811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
29911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return duration;
30011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
30111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
30211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float determinePos(View animView, float velocity) {
30311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        float newPos = 0;
30411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (velocity < 0 || (velocity == 0 && animView.getTranslationX() < 0)
30511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                // if we use the Menu to dismiss an item in landscape, animate up
30611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                || (velocity == 0 && animView.getTranslationX() == 0 && mSwipeDirection == Y)) {
30711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            newPos = -getSize(animView);
30811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        } else {
30911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            newPos = getSize(animView);
31011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
31111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return newPos;
31211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
31311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
31411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void snapChild(final View view, float velocity) {
31511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final View animView = mCallback.getChildContentView(view);
31611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
31711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        ObjectAnimator anim = createTranslationAnimation(animView, 0);
31811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        int duration = SNAP_ANIM_LEN;
31911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.setDuration(duration);
32011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.addUpdateListener(new AnimatorUpdateListener() {
32111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
32211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationUpdate(ValueAnimator animation) {
32311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
32411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    animView.setAlpha(getAlphaForOffset(animView));
32511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
32611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                invalidateGlobalRegion(animView);
32711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
32811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        });
32911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.addListener(new Animator.AnimatorListener() {
33011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
33111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationStart(Animator animation) {
33211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
33311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
33411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
33511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationEnd(Animator animation) {
33611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                animView.setAlpha(mStartAlpha);
33711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mCallback.onDragCancelled(mCurrView);
33811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
33911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
34011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
34111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationCancel(Animator animation) {
34211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
34311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
34411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
34511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationRepeat(Animator animation) {
34611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
34711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        });
34811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.start();
34911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
35011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
35111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public boolean onTouchEvent(MotionEvent ev) {
35211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (!mDragging || mProtected) {
35311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            return false;
35411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
35511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mVelocityTracker.addMovement(ev);
35611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final int action = ev.getAction();
35711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        switch (action) {
35811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_OUTSIDE:
35911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_MOVE:
36011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (mCurrView != null) {
36111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float deltaX = ev.getX() - mInitialTouchPosX;
36211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float deltaY = Math.abs(ev.getY() - mInitialTouchPosY);
36311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // If the user has gone vertical and not gone horizontalish AT
36411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // LEAST minBeforeLock, switch to scroll. Otherwise, cancel
36511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // the swipe.
36611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (!mDragging && deltaY > MIN_VERT && (Math.abs(deltaX)) < MIN_LOCK
36711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            && deltaY > (FACTOR * Math.abs(deltaX))) {
36811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mCallback.onScroll();
36911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        return false;
37011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
37111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float minDistance = MIN_SWIPE;
37211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (Math.abs(deltaX) < minDistance) {
37311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        // Don't start the drag until at least X distance has
37411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        // occurred.
37511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        return true;
37611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
37711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // don't let items that can't be dismissed be dragged more
37811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // than maxScrollDistance
37911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {
38011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float size = getSize(mCurrAnimView);
38111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float maxScrollDistance = 0.15f * size;
38211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        if (Math.abs(deltaX) >= size) {
38311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            deltaX = deltaX > 0 ? maxScrollDistance : -maxScrollDistance;
38411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        } else {
38511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            deltaX = maxScrollDistance
38611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                                    * (float) Math.sin((deltaX / size) * (Math.PI / 2));
38711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        }
38811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
38911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    setTranslation(mCurrAnimView, deltaX);
39011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
39111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mCurrAnimView.setAlpha(getAlphaForOffset(mCurrAnimView));
39211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
39311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    invalidateGlobalRegion(mCallback.getChildContentView(mCurrView));
39411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
39511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                break;
39611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_UP:
39711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_CANCEL:
39811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (mCurrView != null) {
39911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
40011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
40111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
40211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float velocity = getVelocity(mVelocityTracker);
40311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
40411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
40511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // Decide whether to dismiss the current view
40611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // Tweak constants below as required to prevent erroneous
40711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // swipe/dismiss
40811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float translation = Math.abs(mCurrAnimView.getTranslationX());
40911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float currAnimViewSize = getSize(mCurrAnimView);
41011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // Long swipe = translation of .4 * width
41111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH
41211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            && translation > 0.4 * currAnimViewSize;
41311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // Fast swipe = > escapeVelocity and translation of .1 *
41411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // width
41511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity)
41611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            && (Math.abs(velocity) > Math.abs(perpendicularVelocity))
41711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            && (velocity > 0) == (mCurrAnimView.getTranslationX() > 0)
41811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            && translation > 0.05 * currAnimViewSize;
41911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (LOG_SWIPE_DISMISS_VELOCITY) {
42011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        Log.v(TAG, "Swipe/Dismiss: " + velocity + "/" + escapeVelocity + "/"
42111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                                + perpendicularVelocity + ", x: " + translation + "/"
42211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                                + currAnimViewSize);
42311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
42411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
42511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
42611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            && (childSwipedFastEnough || childSwipedFarEnough);
42711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
42811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (dismissChild) {
42911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
43011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    } else {
43111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        snapChild(mCurrView, velocity);
43211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
43311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
43411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                break;
43511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
43611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return true;
43711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
43811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
43911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static void setSwipeable(View view, boolean swipeable) {
44011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        view.setTag(IS_SWIPEABLE_TAG, swipeable ? IS_SWIPEABLE : null);
44111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
44211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
44311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static boolean isSwipeable(View view) {
44411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return IS_SWIPEABLE == view.getTag(IS_SWIPEABLE_TAG);
44511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
44611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
44711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public interface SwipeHelperCallback {
44811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        View getChildAtPosition(MotionEvent ev);
44911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
45011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        View getChildContentView(View v);
45111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
45211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        void onScroll();
45311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
45411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        boolean canChildBeDismissed(View v);
45511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
45611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        void onBeginDrag(View v);
45711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
45811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        void onChildDismissed(View v);
45911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
46011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        void onDragCancelled(View v);
46111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
46211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
46311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
46411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public interface OnItemGestureListener {
46511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public void onSwipe(View view);
46611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
46711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public void onTouch();
4685a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee
4695a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee        public boolean isSwipeEnabled();
47011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
47111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee}