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
92362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang    private float mChildSwipedFarEnoughFactor = 0.4f;
93362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang    private float mChildSwipedFastEnoughFactor = 0.05f;
94362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang
9511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public SwipeHelper(Context context, int swipeDirection, SwipeHelperCallback callback, float densityScale,
9611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            float pagingTouchSlop) {
9711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mCallback = callback;
9811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mSwipeDirection = swipeDirection;
9911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mVelocityTracker = VelocityTracker.obtain();
10011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mDensityScale = densityScale;
10111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mPagingTouchSlop = pagingTouchSlop;
10211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (SWIPE_ESCAPE_VELOCITY == -1) {
10311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            Resources res = context.getResources();
10411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            SWIPE_ESCAPE_VELOCITY = res.getInteger(R.integer.swipe_escape_velocity);
10511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            DEFAULT_ESCAPE_ANIMATION_DURATION = res.getInteger(R.integer.escape_animation_duration);
10611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            MAX_ESCAPE_ANIMATION_DURATION = res.getInteger(R.integer.max_escape_animation_duration);
10711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            MAX_DISMISS_VELOCITY = res.getInteger(R.integer.max_dismiss_velocity);
10811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            SNAP_ANIM_LEN = res.getInteger(R.integer.snap_animation_duration);
10911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            SWIPE_SCROLL_SLOP = res.getInteger(R.integer.swipe_scroll_slop);
11011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            MIN_SWIPE = res.getDimension(R.dimen.min_swipe);
11111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            MIN_VERT = res.getDimension(R.dimen.min_vert);
11211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            MIN_LOCK = res.getDimension(R.dimen.min_lock);
11311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
11411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
11511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
11611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void setDensityScale(float densityScale) {
11711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mDensityScale = densityScale;
11811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
11911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
12011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void setPagingTouchSlop(float pagingTouchSlop) {
12111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mPagingTouchSlop = pagingTouchSlop;
12211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
12311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
124362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang    public void setChildSwipedFarEnoughFactor(float factor) {
125362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang        mChildSwipedFarEnoughFactor = factor;
126362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang    }
127362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang
128362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang    public void setChildSwipedFastEnoughFactor(float factor) {
129362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang        mChildSwipedFastEnoughFactor = factor;
130362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang    }
131362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang
13211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float getVelocity(VelocityTracker vt) {
13311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return mSwipeDirection == X ? vt.getXVelocity() :
13411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                vt.getYVelocity();
13511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
13611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
13711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private ObjectAnimator createTranslationAnimation(View v, float newPos) {
13811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        ObjectAnimator anim = ObjectAnimator.ofFloat(v,
13911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mSwipeDirection == X ? "translationX" : "translationY", newPos);
14011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return anim;
14111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
14211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
14311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private ObjectAnimator createDismissAnimation(View v, float newPos, int duration) {
14411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        ObjectAnimator anim = createTranslationAnimation(v, newPos);
14511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.setInterpolator(sLinearInterpolator);
14611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.setDuration(duration);
14711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return anim;
14811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
14911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
15011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float getPerpendicularVelocity(VelocityTracker vt) {
15111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return mSwipeDirection == X ? vt.getYVelocity() :
15211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                vt.getXVelocity();
15311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
15411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
15511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private void setTranslation(View v, float translate) {
15611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (mSwipeDirection == X) {
15711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            v.setTranslationX(translate);
15811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        } else {
15911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            v.setTranslationY(translate);
16011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
16111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
16211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
16311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float getSize(View v) {
16411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return mSwipeDirection == X ? v.getMeasuredWidth() :
16511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                v.getMeasuredHeight();
16611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
16711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
16811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void setMinAlpha(float minAlpha) {
16911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mMinAlpha = minAlpha;
17011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
17111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
17211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float getAlphaForOffset(View view) {
17311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        float viewSize = getSize(view);
17411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final float fadeSize = ALPHA_FADE_END * viewSize;
17511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        float result = mStartAlpha;
17611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        float pos = view.getTranslationX();
17711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (pos >= viewSize * ALPHA_FADE_START) {
17811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            result = mStartAlpha - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
17911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        } else if (pos < viewSize * (mStartAlpha - ALPHA_FADE_START)) {
18011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            result = mStartAlpha + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
18111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
18211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return Math.max(mMinAlpha, result);
18311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
18411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
18511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    // invalidate the view's own bounds all the way up the view hierarchy
18611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static void invalidateGlobalRegion(View view) {
18711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        invalidateGlobalRegion(
18811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                view,
18911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom()));
19011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
19111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
19211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    // invalidate a rectangle relative to the view's coordinate system all the way up the view
19311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    // hierarchy
19411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static void invalidateGlobalRegion(View view, RectF childBounds) {
19511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        // childBounds.offset(view.getTranslationX(), view.getTranslationY());
19611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (DEBUG_INVALIDATE)
19711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            Log.v(TAG, "-------------");
19811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        while (view.getParent() != null && view.getParent() instanceof View) {
19911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            view = (View) view.getParent();
20011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            view.getMatrix().mapRect(childBounds);
20111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            view.invalidate((int) Math.floor(childBounds.left),
20211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    (int) Math.floor(childBounds.top),
20311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    (int) Math.ceil(childBounds.right),
20411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    (int) Math.ceil(childBounds.bottom));
20511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            if (DEBUG_INVALIDATE) {
20611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                Log.v(TAG, "INVALIDATE(" + (int) Math.floor(childBounds.left)
20711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        + "," + (int) Math.floor(childBounds.top)
20811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        + "," + (int) Math.ceil(childBounds.right)
20911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        + "," + (int) Math.ceil(childBounds.bottom));
21011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
21111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
21211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
21311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
21411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public boolean onInterceptTouchEvent(MotionEvent ev) {
21511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final int action = ev.getAction();
21611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        switch (action) {
21711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_DOWN:
21811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mLastY = ev.getY();
21911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mDragging = false;
22011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mCurrView = mCallback.getChildAtPosition(ev);
22111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mVelocityTracker.clear();
22211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (mCurrView != null) {
22311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mCurrAnimView = mCallback.getChildContentView(mCurrView);
22411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mStartAlpha = mCurrAnimView.getAlpha();
22511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
22611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mVelocityTracker.addMovement(ev);
22711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mInitialTouchPosX = ev.getX();
22811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mInitialTouchPosY = ev.getY();
22911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
23011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                break;
23111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_MOVE:
23211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (mCurrView != null) {
23311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // Check the movement direction.
23411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (mLastY >= 0 && !mDragging) {
23511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float currY = ev.getY();
23611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float currX = ev.getX();
23711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float deltaY = Math.abs(currY - mInitialTouchPosY);
23811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float deltaX = Math.abs(currX - mInitialTouchPosX);
23911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        if (deltaY > SWIPE_SCROLL_SLOP && deltaY > (FACTOR * deltaX)) {
24011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            mLastY = ev.getY();
24111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            mCallback.onScroll();
24211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            return false;
24311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        }
24411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
24511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mVelocityTracker.addMovement(ev);
24611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float pos = ev.getX();
24711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float delta = pos - mInitialTouchPosX;
24811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (Math.abs(delta) > mPagingTouchSlop) {
24911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mCallback.onBeginDrag(mCallback.getChildContentView(mCurrView));
25011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mDragging = true;
25111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mInitialTouchPosX = ev.getX() - mCurrAnimView.getTranslationX();
25211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mInitialTouchPosY = ev.getY();
25311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
25411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
25511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mLastY = ev.getY();
25611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                break;
25711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_UP:
25811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_CANCEL:
25911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mDragging = false;
26011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mCurrView = null;
26111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mCurrAnimView = null;
26211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mLastY = -1;
26311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                break;
26411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
26511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return mDragging;
26611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
26711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
26811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    /**
26911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     * @param view The view to be dismissed
27011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     * @param velocity The desired pixels/second speed at which the view should
27111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     *            move
27211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee     */
27311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private void dismissChild(final View view, float velocity) {
27411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final View animView = mCallback.getChildContentView(view);
27511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
27611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        float newPos = determinePos(animView, velocity);
27711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        int duration = determineDuration(animView, newPos, velocity);
27811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
27911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        animView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
28011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        ObjectAnimator anim = createDismissAnimation(animView, newPos, duration);
28111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.addListener(new AnimatorListenerAdapter() {
28211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
28311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationEnd(Animator animation) {
2843686e68c873ffc83e086797abed6c883185737f2Yorke Lee                mCallback.onChildDismissed(view);
28511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                animView.setLayerType(View.LAYER_TYPE_NONE, null);
28611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
28711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        });
28811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.addUpdateListener(new AnimatorUpdateListener() {
28911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
29011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationUpdate(ValueAnimator animation) {
29111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
29211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    animView.setAlpha(getAlphaForOffset(animView));
29311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
29411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                invalidateGlobalRegion(animView);
29511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
29611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        });
29711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.start();
29811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
29911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
30011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private int determineDuration(View animView, float newPos, float velocity) {
30111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        int duration = MAX_ESCAPE_ANIMATION_DURATION;
30211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (velocity != 0) {
30311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            duration = Math
30411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    .min(duration,
30511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            (int) (Math.abs(newPos - animView.getTranslationX()) * 1000f / Math
30611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                                    .abs(velocity)));
30711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        } else {
30811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            duration = DEFAULT_ESCAPE_ANIMATION_DURATION;
30911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
31011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return duration;
31111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
31211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
31311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    private float determinePos(View animView, float velocity) {
31411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        float newPos = 0;
31511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (velocity < 0 || (velocity == 0 && animView.getTranslationX() < 0)
31611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                // if we use the Menu to dismiss an item in landscape, animate up
31711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                || (velocity == 0 && animView.getTranslationX() == 0 && mSwipeDirection == Y)) {
31811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            newPos = -getSize(animView);
31911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        } else {
32011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            newPos = getSize(animView);
32111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
32211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return newPos;
32311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
32411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
32511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public void snapChild(final View view, float velocity) {
32611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final View animView = mCallback.getChildContentView(view);
32711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
32811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        ObjectAnimator anim = createTranslationAnimation(animView, 0);
32911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        int duration = SNAP_ANIM_LEN;
33011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.setDuration(duration);
33111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.addUpdateListener(new AnimatorUpdateListener() {
33211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
33311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationUpdate(ValueAnimator animation) {
33411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
33511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    animView.setAlpha(getAlphaForOffset(animView));
33611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
33711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                invalidateGlobalRegion(animView);
33811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
33911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        });
3405b2e21098d84e94fd629e5e07c3bbb66bf9b5d95Hongwei Wang        anim.addListener(new AnimatorListenerAdapter() {
34111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            @Override
34211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            public void onAnimationEnd(Animator animation) {
34311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                animView.setAlpha(mStartAlpha);
34411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                mCallback.onDragCancelled(mCurrView);
34511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            }
34611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        });
34711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        anim.start();
34811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
34911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
35011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public boolean onTouchEvent(MotionEvent ev) {
35111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        if (!mDragging || mProtected) {
35211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            return false;
35311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
35411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        mVelocityTracker.addMovement(ev);
35511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        final int action = ev.getAction();
35611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        switch (action) {
35711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_OUTSIDE:
35811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_MOVE:
35911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (mCurrView != null) {
36011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float deltaX = ev.getX() - mInitialTouchPosX;
36111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float deltaY = Math.abs(ev.getY() - mInitialTouchPosY);
36211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // If the user has gone vertical and not gone horizontalish AT
36311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // LEAST minBeforeLock, switch to scroll. Otherwise, cancel
36411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // the swipe.
36511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (!mDragging && deltaY > MIN_VERT && (Math.abs(deltaX)) < MIN_LOCK
36611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            && deltaY > (FACTOR * Math.abs(deltaX))) {
36711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mCallback.onScroll();
36811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        return false;
36911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
37011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float minDistance = MIN_SWIPE;
37111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (Math.abs(deltaX) < minDistance) {
37211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        // Don't start the drag until at least X distance has
37311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        // occurred.
37411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        return true;
37511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
37611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // don't let items that can't be dismissed be dragged more
37711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // than maxScrollDistance
37811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {
37911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float size = getSize(mCurrAnimView);
38011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        float maxScrollDistance = 0.15f * size;
38111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        if (Math.abs(deltaX) >= size) {
38211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            deltaX = deltaX > 0 ? maxScrollDistance : -maxScrollDistance;
38311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        } else {
38411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            deltaX = maxScrollDistance
38511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                                    * (float) Math.sin((deltaX / size) * (Math.PI / 2));
38611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        }
38711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
38811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    setTranslation(mCurrAnimView, deltaX);
38911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
39011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        mCurrAnimView.setAlpha(getAlphaForOffset(mCurrAnimView));
39111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
39211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    invalidateGlobalRegion(mCallback.getChildContentView(mCurrView));
39311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
39411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                break;
39511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_UP:
39611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee            case MotionEvent.ACTION_CANCEL:
39711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                if (mCurrView != null) {
39811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float maxVelocity = MAX_DISMISS_VELOCITY * mDensityScale;
39911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    mVelocityTracker.computeCurrentVelocity(1000 /* px/sec */, maxVelocity);
40011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float escapeVelocity = SWIPE_ESCAPE_VELOCITY * mDensityScale;
40111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float velocity = getVelocity(mVelocityTracker);
40211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float perpendicularVelocity = getPerpendicularVelocity(mVelocityTracker);
40311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
40411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // Decide whether to dismiss the current view
40511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // Tweak constants below as required to prevent erroneous
40611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    // swipe/dismiss
40711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float translation = Math.abs(mCurrAnimView.getTranslationX());
40811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    float currAnimViewSize = getSize(mCurrAnimView);
409362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang                    // Long swipe = translation of {@link #mChildSwipedFarEnoughFactor} * width
41011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH
411362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang                            && translation > mChildSwipedFarEnoughFactor * currAnimViewSize;
412362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang                    // Fast swipe = > escapeVelocity and translation of
413362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang                    // {@link #mChildSwipedFastEnoughFactor} * width
41411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity)
41511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            && (Math.abs(velocity) > Math.abs(perpendicularVelocity))
41611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            && (velocity > 0) == (mCurrAnimView.getTranslationX() > 0)
417362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang                            && translation > mChildSwipedFastEnoughFactor * currAnimViewSize;
41811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (LOG_SWIPE_DISMISS_VELOCITY) {
41911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        Log.v(TAG, "Swipe/Dismiss: " + velocity + "/" + escapeVelocity + "/"
42011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                                + perpendicularVelocity + ", x: " + translation + "/"
42111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                                + currAnimViewSize);
42211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
42311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
42411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    boolean dismissChild = mCallback.canChildBeDismissed(mCurrView)
42511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                            && (childSwipedFastEnough || childSwipedFarEnough);
42611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
42711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    if (dismissChild) {
42811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        dismissChild(mCurrView, childSwipedFastEnough ? velocity : 0f);
42911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    } else {
43011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                        snapChild(mCurrView, velocity);
43111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                    }
43211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                }
43311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee                break;
43411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        }
43511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return true;
43611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
43711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
43811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static void setSwipeable(View view, boolean swipeable) {
43911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        view.setTag(IS_SWIPEABLE_TAG, swipeable ? IS_SWIPEABLE : null);
44011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
44111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
44211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public static boolean isSwipeable(View view) {
44311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        return IS_SWIPEABLE == view.getTag(IS_SWIPEABLE_TAG);
44411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
44511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
44611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public interface SwipeHelperCallback {
44711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        View getChildAtPosition(MotionEvent ev);
44811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
44911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        View getChildContentView(View v);
45011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
45111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        void onScroll();
45211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
45311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        boolean canChildBeDismissed(View v);
45411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
45511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        void onBeginDrag(View v);
45611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
45711ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        void onChildDismissed(View v);
45811ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
45911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        void onDragCancelled(View v);
46011ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
46111ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
46211ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
46311ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    public interface OnItemGestureListener {
46411ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public void onSwipe(View view);
46511ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee
46611ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee        public void onTouch();
4675a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee
4685a1db1cd6a8addc0401529454bde6e1d15f215a0Yorke Lee        public boolean isSwipeEnabled();
46911ee58b1b8711c6d3b2ade6a71835b6c102a08a7Yorke Lee    }
470362c4441b65f63dfc3cf933d569aba6e00dc168aHongwei Wang}
471