StackView.java revision 44729e3d1c01265858eec566c7b7c676c46a7916
144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen/*
244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Copyright (C) 2010 The Android Open Source Project
344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen *
444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Licensed under the Apache License, Version 2.0 (the "License");
544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * you may not use this file except in compliance with the License.
644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * You may obtain a copy of the License at
744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen *
844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen *      http://www.apache.org/licenses/LICENSE-2.0
944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen *
1044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * Unless required by applicable law or agreed to in writing, software
1144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * distributed under the License is distributed on an "AS IS" BASIS,
1244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * See the License for the specific language governing permissions and
1444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * limitations under the License.
1544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */
1644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
1744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenpackage android.widget;
1844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
1944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport java.util.WeakHashMap;
2044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
2144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.animation.PropertyAnimator;
2244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.content.Context;
2344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.graphics.Rect;
2444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.util.AttributeSet;
2544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.util.Log;
2644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.MotionEvent;
2744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.VelocityTracker;
2844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.View;
2944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.ViewConfiguration;
3044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.view.ViewGroup;
3144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenimport android.widget.RemoteViews.RemoteView;
3244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
3344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen@RemoteView
3444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen/**
3544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * A view that displays its children in a stack and allows users to discretely swipe
3644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen * through the children.
3744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen */
3844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohenpublic class StackView extends AdapterViewAnimator {
3944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final String TAG = "StackView";
4044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
4144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
4244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Default animation parameters
4344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
4444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final int DEFAULT_ANIMATION_DURATION = 400;
4544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final int MINIMUM_ANIMATION_DURATION = 50;
4644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
4744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
4844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * These specify the different gesture states
4944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
5044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final int GESTURE_NONE = 0;
5144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final int GESTURE_SLIDE_UP = 1;
5244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final int GESTURE_SLIDE_DOWN = 2;
5344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
5444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
5544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Specifies how far you need to swipe (up or down) before it
5644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * will be consider a completed gesture when you lift your finger
5744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
5844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final float SWIPE_THRESHOLD_RATIO = 0.35f;
5944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final float SLIDE_UP_RATIO = 0.7f;
6044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
6144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final WeakHashMap<View, Float> mRotations = new WeakHashMap<View, Float>();
6244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final WeakHashMap<View, Integer>
6344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mChildrenToApplyTransformsTo = new WeakHashMap<View, Integer>();
6444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
6544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
6644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Sentinel value for no current active pointer.
6744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Used by {@link #mActivePointerId}.
6844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
6944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private static final int INVALID_POINTER = -1;
7044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
7144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
7244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * These variables are all related to the current state of touch interaction
7344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * with the stack
7444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
7544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private boolean mGestureComplete = false;
7644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private float mInitialY;
7744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private float mInitialX;
7844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private int mActivePointerId;
7944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private int mYOffset = 0;
8044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private int mYVelocity = 0;
8144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private int mSwipeGestureType = GESTURE_NONE;
8244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private int mViewHeight;
8344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private int mSwipeThreshold;
8444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private int mTouchSlop;
8544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private int mMaximumVelocity;
8644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private VelocityTracker mVelocityTracker;
8744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
8844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private boolean mFirstLayoutHappened = false;
8944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
9044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    // TODO: temp hack to get this thing started
9144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    int mIndex = 5;
9244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
9344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    public StackView(Context context) {
9444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        super(context);
9544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        initStackView();
9644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
9744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
9844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    public StackView(Context context, AttributeSet attrs) {
9944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        super(context, attrs);
10044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        initStackView();
10144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
10244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
10344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private void initStackView() {
10444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        configureViewAnimator(4, 2);
10544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        setStaticTransformationsEnabled(true);
10644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        final ViewConfiguration configuration = ViewConfiguration.get(getContext());
10744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mTouchSlop = configuration.getScaledTouchSlop();// + 5;
10844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
10944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mActivePointerId = INVALID_POINTER;
11044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
11144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
11244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
11344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Animate the views between different relative indexes within the {@link AdapterViewAnimator}
11444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
11544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    void animateViewForTransition(int fromIndex, int toIndex, View view) {
11644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (fromIndex == -1 && toIndex == 0) {
11744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Fade item in
11844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            if (view.getAlpha() == 1) {
11944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                view.setAlpha(0);
12044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
12144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            PropertyAnimator fadeIn = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
12244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    view, "alpha", view.getAlpha(), 1.0f);
12344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            fadeIn.start();
12444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else if (fromIndex == mNumActiveViews - 1 && toIndex == mNumActiveViews - 2) {
12544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Slide item in
12644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            view.setVisibility(VISIBLE);
12744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            LayoutParams lp = (LayoutParams) view.getLayoutParams();
12844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
12944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            int largestDuration = (int) Math.round(
13044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    (lp.verticalOffset*1.0f/-mViewHeight)*DEFAULT_ANIMATION_DURATION);
13144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            int duration = largestDuration;
13244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            if (mYVelocity != 0) {
13344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                duration = 1000*(0 - lp.verticalOffset)/Math.abs(mYVelocity);
13444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
13544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
13644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            duration = Math.min(duration, largestDuration);
13744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
13844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
13944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            PropertyAnimator slideDown = new PropertyAnimator(duration, lp,
14044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    "verticalOffset", lp.verticalOffset, 0);
14144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            slideDown.start();
14244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
14344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            PropertyAnimator fadeIn = new PropertyAnimator(duration, view,
14444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    "alpha", view.getAlpha(), 1.0f);
14544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            fadeIn.start();
14644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else if (fromIndex == mNumActiveViews - 2 && toIndex == mNumActiveViews - 1) {
14744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Slide item out
14844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            LayoutParams lp = (LayoutParams) view.getLayoutParams();
14944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
15044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            int largestDuration = (int) Math.round(
15144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    (1 - (lp.verticalOffset*1.0f/-mViewHeight))*DEFAULT_ANIMATION_DURATION);
15244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            int duration = largestDuration;
15344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            if (mYVelocity != 0) {
15444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                duration = 1000*(lp.verticalOffset + mViewHeight)/Math.abs(mYVelocity);
15544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
15644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
15744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            duration = Math.min(duration, largestDuration);
15844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            duration = Math.max(duration, MINIMUM_ANIMATION_DURATION);
15944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
16044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            PropertyAnimator slideUp = new PropertyAnimator(duration, lp,
16144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    "verticalOffset", lp.verticalOffset, -mViewHeight);
16244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            slideUp.start();
16344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
16444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            PropertyAnimator fadeOut = new PropertyAnimator(duration, view,
16544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    "alpha", view.getAlpha(), 0.0f);
16644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            fadeOut.start();
16744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else if (fromIndex == -1 && toIndex == mNumActiveViews - 1) {
16844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Make sure this view that is "waiting in the wings" is invisible
16944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            view.setAlpha(0.0f);
17044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else if (toIndex == -1) {
17144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Fade item out
17244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            PropertyAnimator fadeOut = new PropertyAnimator(DEFAULT_ANIMATION_DURATION,
17344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    view, "alpha", view.getAlpha(), 0);
17444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            fadeOut.start();
17544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
17644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
17744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
17844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    /**
17944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     * Apply any necessary tranforms for the child that is being added.
18044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen     */
18144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    void applyTransformForChildAtIndex(View child, int relativeIndex) {
18244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        float rotation;
18344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
18444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (!mRotations.containsKey(child)) {
18544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            rotation = (float) (Math.random()*26 - 13);
18644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mRotations.put(child, rotation);
18744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else {
18844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            rotation = mRotations.get(child);
18944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
19044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
19144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // Child has been removed
19244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (relativeIndex == -1) {
19344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            if (mRotations.containsKey(child)) {
19444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mRotations.remove(child);
19544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
19644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            if (mChildrenToApplyTransformsTo.containsKey(child)) {
19744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mChildrenToApplyTransformsTo.remove(child);
19844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
19944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
20044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
20144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // if this view is already in the layout, we need to
20244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // wait until layout has finished in order to set the
20344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        // pivot point of the rotation (requiring getMeasuredWidth/Height())
20444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mChildrenToApplyTransformsTo.put(child, relativeIndex);
20544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
20644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
20744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    @Override
20844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
20944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        super.onLayout(changed, left, top, right, bottom);
21044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
21144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (!mChildrenToApplyTransformsTo.isEmpty()) {
21244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            for (View child: mChildrenToApplyTransformsTo.keySet()) {
21344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                if (mRotations.containsKey(child)) {
21444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    child.setPivotX(child.getMeasuredWidth()/2);
21544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    child.setPivotY(child.getMeasuredHeight()/2);
21644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    child.setRotation(mRotations.get(child));
21744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                }
21844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
21944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mChildrenToApplyTransformsTo.clear();
22044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
22144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
22244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (!mFirstLayoutHappened) {
22344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mViewHeight = (int) Math.round(SLIDE_UP_RATIO*getMeasuredHeight());
22444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mSwipeThreshold = (int) Math.round(SWIPE_THRESHOLD_RATIO*mViewHeight);
22544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
22644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // TODO: Right now this walks all the way up the view hierarchy and disables
22744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // ClipChildren and ClipToPadding. We're probably going  to want to reset
22844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // these flags as well.
22944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            setClipChildren(false);
23044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            ViewGroup view = this;
23144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            while (view.getParent() != null && view.getParent() instanceof ViewGroup) {
23244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                view = (ViewGroup) view.getParent();
23344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                view.setClipChildren(false);
23444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                view.setClipToPadding(false);
23544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
23644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
23744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mFirstLayoutHappened = true;
23844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
23944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
24044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
24144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    @Override
24244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    public boolean onInterceptTouchEvent(MotionEvent ev) {
24344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int action = ev.getAction();
24444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        switch(action & MotionEvent.ACTION_MASK) {
24544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
24644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            case MotionEvent.ACTION_DOWN: {
24744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                if (mActivePointerId == INVALID_POINTER) {
24844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    mInitialX = ev.getX();
24944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    mInitialY = ev.getY();
25044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    mActivePointerId = ev.getPointerId(0);
25144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                }
25244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                break;
25344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
25444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            case MotionEvent.ACTION_MOVE: {
25544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int pointerIndex = ev.findPointerIndex(mActivePointerId);
25644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                if (pointerIndex == INVALID_POINTER) {
25744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    // no data for our primary pointer, this shouldn't happen, log it
25844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    Log.d(TAG, "Error: No data for our primary pointer.");
25944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    return false;
26044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                }
26144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
26244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                float newY = ev.getY(pointerIndex);
26344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                float deltaY = newY - mInitialY;
26444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
26544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) {
26644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    mSwipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
26744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    mGestureComplete = false;
26844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    cancelLongPress();
26944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    requestDisallowInterceptTouchEvent(true);
27044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                }
27144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                break;
27244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
27344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            case MotionEvent.ACTION_POINTER_UP: {
27444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                onSecondaryPointerUp(ev);
27544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                break;
27644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
27744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            case MotionEvent.ACTION_UP:
27844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            case MotionEvent.ACTION_CANCEL: {
27944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mActivePointerId = INVALID_POINTER;
28044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mSwipeGestureType = GESTURE_NONE;
28144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mGestureComplete = true;
28244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
28344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
28444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
28544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return mSwipeGestureType != GESTURE_NONE;
28644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
28744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
28844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    @Override
28944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    public boolean onTouchEvent(MotionEvent ev) {
29044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int action = ev.getAction();
29144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int pointerIndex = ev.findPointerIndex(mActivePointerId);
29244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (pointerIndex == INVALID_POINTER) {
29344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // no data for our primary pointer, this shouldn't happen, log it
29444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            Log.d(TAG, "Error: No data for our primary pointer.");
29544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            return false;
29644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
29744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
29844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        float newY = ev.getY(pointerIndex);
29944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        float deltaY = newY - mInitialY;
30044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
30144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (mVelocityTracker == null) {
30244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mVelocityTracker = VelocityTracker.obtain();
30344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
30444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mVelocityTracker.addMovement(ev);
30544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
30644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        switch (action & MotionEvent.ACTION_MASK) {
30744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            case MotionEvent.ACTION_MOVE: {
30844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) {
30944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    mSwipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
31044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    mGestureComplete = false;
31144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    cancelLongPress();
31244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    requestDisallowInterceptTouchEvent(true);
31344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                }
31444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
31544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                if (!mGestureComplete) {
31644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
31744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        View v = getViewAtRelativeIndex(mNumActiveViews - 1);
31844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        if (v != null) {
31944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            // This view is present but hidden, make sure it's visible
32044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            // if they pull down
32144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            v.setVisibility(VISIBLE);
32244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
32344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            float r = (deltaY-mTouchSlop)*1.0f / (mSwipeThreshold);
32444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            mYOffset = Math.min(-mViewHeight + (int)  Math.round(
32544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                                    r*mSwipeThreshold) - mTouchSlop, 0);
32644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            LayoutParams lp = (LayoutParams) v.getLayoutParams();
32744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            lp.setVerticalOffset(mYOffset);
32844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
32944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            float alpha = Math.max(0.0f, 1.0f - (1.0f*mYOffset/-mViewHeight));
33044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            alpha = Math.min(1.0f, alpha);
33144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            v.setAlpha(alpha);
33244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        }
33344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        return true;
33444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    } else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
33544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        View v = getViewAtRelativeIndex(mNumActiveViews - 2);
33644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
33744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        if (v != null) {
33844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            float r = -(deltaY*1.0f + mTouchSlop) / (mSwipeThreshold);
33944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            mYOffset = Math.min((int) Math.round(r*-mSwipeThreshold), 0);
34044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            LayoutParams lp = (LayoutParams) v.getLayoutParams();
34144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            lp.setVerticalOffset(mYOffset);
34244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
34344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            float alpha = Math.max(0.0f, 1.0f - (1.0f*mYOffset/-mViewHeight));
34444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            alpha = Math.min(1.0f, alpha);
34544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            v.setAlpha(alpha);
34644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        }
34744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        return true;
34844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    }
34944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                }
35044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                break;
35144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
35244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            case MotionEvent.ACTION_UP: {
35344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                handlePointerUp(ev);
35444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                break;
35544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
35644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            case MotionEvent.ACTION_POINTER_UP: {
35744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                onSecondaryPointerUp(ev);
35844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                break;
35944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
36044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            case MotionEvent.ACTION_CANCEL: {
36144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mActivePointerId = INVALID_POINTER;
36244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mGestureComplete = true;
36344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mSwipeGestureType = GESTURE_NONE;
36444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                mYOffset = 0;
36544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                break;
36644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
36744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
36844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        return true;
36944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
37044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
37144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private final Rect touchRect = new Rect();
37244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private void onSecondaryPointerUp(MotionEvent ev) {
37344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        final int activePointerIndex = ev.getActionIndex();
37444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        final int pointerId = ev.getPointerId(activePointerIndex);
37544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (pointerId == mActivePointerId) {
37644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
37744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            int activeViewIndex = (mSwipeGestureType == GESTURE_SLIDE_DOWN) ? mNumActiveViews - 1
37844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    : mNumActiveViews - 2;
37944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
38044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            View v = getViewAtRelativeIndex(activeViewIndex);
38144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            if (v == null) return;
38244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
38344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Our primary pointer has gone up -- let's see if we can find
38444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // another pointer on the view. If so, then we should replace
38544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // our primary pointer with this new pointer and adjust things
38644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // so that the view doesn't jump
38744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            for (int index = 0; index < ev.getPointerCount(); index++) {
38844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                if (index != activePointerIndex) {
38944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
39044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    float x = ev.getX(index);
39144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    float y = ev.getY(index);
39244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
39344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    touchRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
39444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    if (touchRect.contains((int) Math.round(x), (int) Math.round(y))) {
39544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        float oldX = ev.getX(activePointerIndex);
39644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        float oldY = ev.getY(activePointerIndex);
39744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
39844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        // adjust our frame of reference to avoid a jump
39944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        mInitialY += (y - oldY);
40044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        mInitialX += (x - oldX);
40144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
40244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        mActivePointerId = ev.getPointerId(index);
40344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        if (mVelocityTracker != null) {
40444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                            mVelocityTracker.clear();
40544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        }
40644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        // ok, we're good, we found a new pointer which is touching the active view
40744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        return;
40844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                    }
40944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                }
41044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
41144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // if we made it this far, it means we didn't find a satisfactory new pointer :(,
41244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // so end the
41344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            handlePointerUp(ev);
41444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
41544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
41644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
41744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    private void handlePointerUp(MotionEvent ev) {
41844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int pointerIndex = ev.findPointerIndex(mActivePointerId);
41944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        float newY = ev.getY(pointerIndex);
42044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        int deltaY = (int) (newY - mInitialY);
42144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
42244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
42344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mYVelocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
42444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
42544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (mVelocityTracker != null) {
42644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mVelocityTracker.recycle();
42744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            mVelocityTracker = null;
42844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
42944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
43044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN &&
43144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                !mGestureComplete) {
43244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Swipe threshold exceeded, swipe down
43344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            showNext();
43444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP &&
43544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                !mGestureComplete) {
43644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Swipe threshold exceeded, swipe up
43744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            showPrevious();
43844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else if (mSwipeGestureType == GESTURE_SLIDE_UP && !mGestureComplete) {
43944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Didn't swipe up far enough, snap back down
44044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            View v = getViewAtRelativeIndex(mNumActiveViews - 2);
44144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            if (v != null) {
44244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // Compute the animation duration based on how far they pulled it up
44344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                LayoutParams lp = (LayoutParams) v.getLayoutParams();
44444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int duration = (int) Math.round(
44544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        lp.verticalOffset*1.0f/-mViewHeight*DEFAULT_ANIMATION_DURATION);
44644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                duration = Math.max(MINIMUM_ANIMATION_DURATION, duration);
44744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
44844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // Animate back down
44944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                PropertyAnimator slideDown = new PropertyAnimator(duration, lp,
45044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        "verticalOffset", lp.verticalOffset, 0);
45144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                slideDown.start();
45244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                PropertyAnimator fadeIn = new PropertyAnimator(duration, v,
45344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        "alpha",v.getAlpha(), 1.0f);
45444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                fadeIn.start();
45544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
45644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        } else if (mSwipeGestureType == GESTURE_SLIDE_DOWN && !mGestureComplete) {
45744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            // Didn't swipe down far enough, snap back up
45844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            View v = getViewAtRelativeIndex(mNumActiveViews - 1);
45944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            if (v != null) {
46044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // Compute the animation duration based on how far they pulled it down
46144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                LayoutParams lp = (LayoutParams) v.getLayoutParams();
46244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                int duration = (int) Math.round(
46344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        (1 - lp.verticalOffset*1.0f/-mViewHeight)*DEFAULT_ANIMATION_DURATION);
46444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                duration = Math.max(MINIMUM_ANIMATION_DURATION, duration);
46544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
46644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                // Animate back up
46744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                PropertyAnimator slideUp = new PropertyAnimator(duration, lp,
46844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        "verticalOffset", lp.verticalOffset, -mViewHeight);
46944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                slideUp.start();
47044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                PropertyAnimator fadeOut = new PropertyAnimator(duration, v,
47144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                        "alpha",v.getAlpha(), 0.0f);
47244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen                fadeOut.start();
47344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen            }
47444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        }
47544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
47644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mActivePointerId = INVALID_POINTER;
47744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mGestureComplete = true;
47844729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mSwipeGestureType = GESTURE_NONE;
47944729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        mYOffset = 0;
48044729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
48144729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen
48244729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    @Override
48344729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    public void onRemoteAdapterConnected() {
48444729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        super.onRemoteAdapterConnected();
48544729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen        setDisplayedChild(mIndex);
48644729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen    }
48744729e3d1c01265858eec566c7b7c676c46a7916Adam Cohen}
488