SlidingChallengeLayout.java revision 86b6357e5eb91950eac7de7ffe29e5a4ad32903b
1d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller/*
2d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Copyright (C) 2012 The Android Open Source Project
3d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller *
4d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Licensed under the Apache License, Version 2.0 (the "License");
5d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * you may not use this file except in compliance with the License.
6d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * You may obtain a copy of the License at
7d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller *
8d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller *      http://www.apache.org/licenses/LICENSE-2.0
9d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller *
10d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Unless required by applicable law or agreed to in writing, software
11d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * distributed under the License is distributed on an "AS IS" BASIS,
12d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * See the License for the specific language governing permissions and
14d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * limitations under the License.
15d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */
16d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
17d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerpackage com.android.internal.policy.impl.keyguard;
18d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
19d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.content.Context;
20d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.content.res.TypedArray;
21d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.graphics.Canvas;
22d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.graphics.drawable.Drawable;
23d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.AttributeSet;
24d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.Log;
25d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.MotionEvent;
26d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.VelocityTracker;
27d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.View;
28d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewConfiguration;
29d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewGroup;
30d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.animation.Interpolator;
31d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.widget.Scroller;
32d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
33d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport com.android.internal.R;
34d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
35d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller/**
36d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * This layout handles interaction with the sliding security challenge views
37d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * that overlay/resize other keyguard contents.
38d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */
3919a5267003e7dc70100a4bd4f1f449523b2ff38bJim Millerpublic class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout {
40d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final String TAG = "SlidingChallengeLayout";
41d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
42d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Drawn to show the drag handle in closed state; crossfades to the challenge view
43d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // when challenge is fully visible
44d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private Drawable mHandleDrawable;
4586b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock    private boolean mShowHandle = true;
46d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
47d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Initialized during measurement from child layoutparams
48d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private View mChallengeView;
49d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
50d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Range: 0 (fully hidden) to 1 (fully visible)
51d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private float mChallengeOffset = 1.f;
52d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mChallengeShowing = true;
53d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
54d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private final Scroller mScroller;
55d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mScrollState;
56d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private OnChallengeScrolledListener mListener;
57d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
58d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_IDLE = 0;
59d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_DRAGGING = 1;
60d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_SETTLING = 2;
61d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
62d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int MAX_SETTLE_DURATION = 600; // ms
63d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
64d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // ID of the pointer in charge of a current drag
65d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mActivePointerId = INVALID_POINTER;
66d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int INVALID_POINTER = -1;
67d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
68d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if the user is currently dragging the slider
69d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mDragging;
70d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if the user may not drag until a new gesture begins
71d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mBlockDrag;
72d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
73d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private VelocityTracker mVelocityTracker;
74d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mMinVelocity;
75d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mMaxVelocity;
76d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private float mLastTouchY;
77d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mDragHandleSize;
78d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
79d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int DRAG_HANDLE_DEFAULT_SIZE = 32; // dp
80d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
81d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if at least one layout pass has happened since the view was attached.
82d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mHasLayout;
83d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
84d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final Interpolator sMotionInterpolator = new Interpolator() {
85d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public float getInterpolation(float t) {
86d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            t -= 1.0f;
87d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return t * t * t * t * t + 1.0f;
88d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
89d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
90d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
91d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final Interpolator sHandleFadeInterpolator = new Interpolator() {
92d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public float getInterpolation(float t) {
93d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return t * t;
94d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
95d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
96d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
97d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private final Runnable mEndScrollRunnable = new Runnable () {
98d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void run() {
99d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            completeChallengeScroll();
100d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
101d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
102d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
103d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
104d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Listener interface that reports changes in scroll state of the challenge area.
105d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
106d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public interface OnChallengeScrolledListener {
107d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        /**
108d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * The scroll state itself changed.
109d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
110d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>scrollState will be one of the following:</p>
111d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
112d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <ul>
113d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li>
114d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging
115d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * the challenge area.</li>
116d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating
117d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * into place.</li>
118d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * </ul>
119d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
120d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>Do not perform expensive operations (e.g. layout)
121d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p>
122d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
123d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param scrollState The new scroll state of the challenge area.
124d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         */
125d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void onScrollStateChanged(int scrollState);
126d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
127d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        /**
128d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * The precise position of the challenge area has changed.
129d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
130d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>NOTE: It is NOT safe to modify layout or call any View methods that may
131d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * result in a requestLayout anywhere in your view hierarchy as a result of this call.
132d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * It may be called during drawing.</p>
133d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
134d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param scrollPosition New relative position of the challenge area.
135d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                       1.f = fully visible/ready to be interacted with.
136d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                       0.f = fully invisible/inaccessible to the user.
137d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param challengeTop Position of the top edge of the challenge view in px in the
138d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                     SlidingChallengeLayout's coordinate system.
139d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         */
140d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void onScrollPositionChanged(float scrollPosition, int challengeTop);
141d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
142d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
143d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context) {
144d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        this(context, null);
145d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
146d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
147d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context, AttributeSet attrs) {
148d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        this(context, attrs, 0);
149d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
150d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
151d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) {
152d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super(context, attrs, defStyle);
153d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
154d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final TypedArray a = context.obtainStyledAttributes(attrs,
155d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                R.styleable.SlidingChallengeLayout, defStyle, 0);
156d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setDragHandleDrawable(a.getDrawable(R.styleable.SlidingChallengeLayout_dragHandle));
157d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
158d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        a.recycle();
159d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
160d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mScroller = new Scroller(context, sMotionInterpolator);
161d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
162d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final ViewConfiguration vc = ViewConfiguration.get(context);
163d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mMinVelocity = vc.getScaledMinimumFlingVelocity();
164d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mMaxVelocity = vc.getScaledMaximumFlingVelocity();
165d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
166d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setWillNotDraw(false);
167d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
168d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
169d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void setDragHandleDrawable(Drawable d) {
170d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (d != null) {
171d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mDragHandleSize = d.getIntrinsicHeight();
172d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
173d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mDragHandleSize == 0 || d == null) {
174d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final float density = getResources().getDisplayMetrics().density;
175d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mDragHandleSize = (int) (DRAG_HANDLE_DEFAULT_SIZE * density + 0.5f);
176d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
177d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHandleDrawable = d;
178d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
179d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
180d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void sendInitialListenerUpdates() {
181d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mListener != null) {
182d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0;
183d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mListener.onScrollPositionChanged(mChallengeOffset, challengeTop);
184d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mListener.onScrollStateChanged(mScrollState);
185d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
186d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
187d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
188d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) {
189d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mListener = listener;
190d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mHasLayout) {
191d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            sendInitialListenerUpdates();
192d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
193d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
194d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
195d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
196d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void onAttachedToWindow() {
197d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.onAttachedToWindow();
198d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
199d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = false;
200d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
201d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
202d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
203d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void onDetachedFromWindow() {
204d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.onDetachedFromWindow();
205d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
206d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        removeCallbacks(mEndScrollRunnable);
207d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = false;
208d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
209d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
210d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // We want the duration of the page snap animation to be influenced by the distance that
211d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // the screen has to travel, however, we don't want this duration to be effected in a
212d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
213d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // of travel has on the overall snap duration.
214d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    float distanceInfluenceForSnapDuration(float f) {
215d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        f -= 0.5f; // center the values about 0.
216d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        f *= 0.3f * Math.PI / 2.0f;
217d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return (float) Math.sin(f);
218d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
219d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
220d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void setScrollState(int state) {
221d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mScrollState != state) {
222d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mScrollState = state;
223d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
224d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (mListener != null) {
225d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                mListener.onScrollStateChanged(state);
226d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
227d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
228d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
229d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
230d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void completeChallengeScroll() {
231d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setChallengeShowing(mChallengeOffset != 0);
232d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setScrollState(SCROLL_STATE_IDLE);
233d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
234d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
235d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
236d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Animate the bottom edge of the challenge view to the given position.
237d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     *
238d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param y desired final position for the bottom edge of the challenge view in px
239d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param velocity velocity in
240d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
241d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void animateChallengeTo(int y, int velocity) {
242d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) {
243d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // Nothing to do.
244d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
245d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
246d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int sy = mChallengeView.getBottom();
247d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int dy = y - sy;
248d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (dy == 0) {
249d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            completeChallengeScroll();
250d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
251d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
252d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
253d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setScrollState(SCROLL_STATE_SETTLING);
254d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
255d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int childHeight = mChallengeView.getHeight();
256d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int halfHeight = childHeight / 2;
257d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight);
258d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final float distance = halfHeight + halfHeight *
259d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                distanceInfluenceForSnapDuration(distanceRatio);
260d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
261d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int duration = 0;
262d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        velocity = Math.abs(velocity);
263d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (velocity > 0) {
264d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
265d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        } else {
266d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final float childDelta = (float) Math.abs(dy) / childHeight;
267d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            duration = (int) ((childDelta + 1) * 100);
268d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
269d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        duration = Math.min(duration, MAX_SETTLE_DURATION);
270d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
271d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mScroller.startScroll(0, sy, 0, dy, duration);
272d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        postInvalidateOnAnimation();
273d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
274d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
275d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void setChallengeShowing(boolean showChallenge) {
276d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeShowing != showChallenge) {
277d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mChallengeShowing = showChallenge;
278d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (mChallengeView != null) {
279d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                mChallengeView.setVisibility(showChallenge ? VISIBLE : INVISIBLE);
280d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
281d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
282d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
283d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
284d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
285d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @return true if the challenge is at all visible.
286d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
287d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean isChallengeShowing() {
288d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mChallengeShowing;
289d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
290d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
291d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
29219a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    public boolean isChallengeOverlapping() {
29319a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller        return mChallengeShowing;
29419a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    }
29519a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller
29619a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    @Override
297d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void requestDisallowInterceptTouchEvent(boolean allowIntercept) {
298d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
299d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // If there are one or more pointers in the challenge view before we take over
300d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // touch events, onInterceptTouchEvent will set mBlockDrag.
301d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
302d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
303d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
304d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean onInterceptTouchEvent(MotionEvent ev) {
305d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mVelocityTracker == null) {
306d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mVelocityTracker = VelocityTracker.obtain();
307d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
308d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.addMovement(ev);
309d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
310d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int action = ev.getActionMasked();
311d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        switch (action) {
312022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller            case MotionEvent.ACTION_DOWN:
313022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                mLastTouchY = ev.getY();
314022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                break;
315022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
316d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_CANCEL:
317d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_UP:
318d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
319d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
320d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
321d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_MOVE:
322d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int count = ev.getPointerCount();
323d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                for (int i = 0; i < count; i++) {
324d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float x = ev.getX(i);
325d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float y = ev.getY(i);
326d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
327022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                    if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mLastTouchY) ||
328d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) &&
329d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mActivePointerId == INVALID_POINTER) {
330d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mActivePointerId = ev.getPointerId(i);
331d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mLastTouchY = y;
332d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mDragging = true;
333d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    } else if (isInChallengeView(x, y)) {
334d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mBlockDrag = true;
335d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
336d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
337d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
338d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
339d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
340d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mBlockDrag) {
341d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mActivePointerId = INVALID_POINTER;
342d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mDragging = false;
343d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
344d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
345d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mDragging;
346d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
347d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
348d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void resetTouch() {
349d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.recycle();
350d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker = null;
351d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mActivePointerId = INVALID_POINTER;
352d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mDragging = mBlockDrag = false;
353d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
354d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
355d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
356d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean onTouchEvent(MotionEvent ev) {
357d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mVelocityTracker == null) {
358d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mVelocityTracker = VelocityTracker.obtain();
359d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
360d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.addMovement(ev);
361d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
362d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int action = ev.getActionMasked();
363d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        switch (action) {
364d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_CANCEL:
365d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
366d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    showChallenge(0);
367d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
368d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
369d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
370d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
371d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_POINTER_UP:
372d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) {
373d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    break;
374d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
375d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_UP:
376d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
377d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
378d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId));
379d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
380d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
381d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
382d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
383d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_MOVE:
384d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (!mDragging && !mBlockDrag) {
385d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int count = ev.getPointerCount();
386d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    for (int i = 0; i < count; i++) {
387d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        final float x = ev.getX(i);
388d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        final float y = ev.getY(i);
389d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
390022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                        if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mLastTouchY) ||
391d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                                (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
392d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                                && mActivePointerId == INVALID_POINTER) {
393d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mActivePointerId = ev.getPointerId(i);
394d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mDragging = true;
395d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            break;
396d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        }
397d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
398d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
399d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Not an else; this can be set above.
400d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
401d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // No-op if already in this state, but set it here in case we arrived
402d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // at this point from either intercept or the above.
403d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    setScrollState(SCROLL_STATE_DRAGGING);
404d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
405d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int index = ev.findPointerIndex(mActivePointerId);
406d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    if (index < 0) {
407d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        // Oops, bogus state. We lost some touch events somewhere.
408d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        // Just drop it with no velocity and let things settle.
409d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        resetTouch();
410d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        showChallenge(0);
411d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        return true;
412d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
413022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                    final float y = Math.max(ev.getY(index), getChallengeOpenedTop());
414d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float delta = y - mLastTouchY;
415d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int idelta = (int) delta;
416d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // Don't lose the rounded component
417d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mLastTouchY = y + delta - idelta;
418d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
419d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    moveChallengeBy(idelta);
420d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
421d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
422d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
423d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return true;
424d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
425d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
426d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean isInChallengeView(float x, float y) {
427d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return false;
428d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
429d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return x >= mChallengeView.getLeft() && y >= mChallengeView.getTop() &&
430d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                x < mChallengeView.getRight() && y < mChallengeView.getBottom();
431d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
432d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
433d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean isInDragHandle(float x, float y) {
434d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return false;
435d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
436d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return x >= 0 && y >= mChallengeView.getTop() && x < getWidth() &&
437d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                y < mChallengeView.getTop() + mDragHandleSize;
438d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
439d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
440022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    private boolean crossedDragHandle(float x, float y, float lastY) {
441022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int challengeTop = mChallengeView.getTop();
442022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        return x >= 0 && x < getWidth() && lastY < challengeTop &&
443022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                y > challengeTop + mDragHandleSize;
444022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    }
445022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
446d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
447d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected void onMeasure(int widthSpec, int heightSpec) {
448d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
449d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
450d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            throw new IllegalArgumentException(
451d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    "SlidingChallengeLayout must be measured with an exact size");
452d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
453d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
454d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int width = MeasureSpec.getSize(widthSpec);
455d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int height = MeasureSpec.getSize(heightSpec);
456d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setMeasuredDimension(width, height);
457d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
458d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // Find one and only one challenge view.
459d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final View oldChallengeView = mChallengeView;
460d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView = null;
461d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int count = getChildCount();
462d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        for (int i = 0; i < count; i++) {
463d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final View child = getChildAt(i);
464d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
465d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
466d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (lp.isChallenge) {
467d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mChallengeView != null) {
468d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    throw new IllegalStateException(
469d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            "There may only be one child with layout_isChallenge=\"true\"");
470d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
471d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                mChallengeView = child;
472d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mChallengeView != oldChallengeView) {
473d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
474d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
475d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
476d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
477d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (child.getVisibility() == GONE) continue;
478d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
479d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            measureChildWithMargins(child, widthSpec, 0, heightSpec, 0);
480d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
481d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
482d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
483d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
484d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected void onLayout(boolean changed, int l, int t, int r, int b) {
485d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingLeft = getPaddingLeft();
486d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingTop = getPaddingTop();
487d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingRight = getPaddingRight();
488d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingBottom = getPaddingBottom();
489d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int width = r - l;
490d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int height = b - t;
491d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
492d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int count = getChildCount();
493d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        for (int i = 0; i < count; i++) {
494d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final View child = getChildAt(i);
495d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
496d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (child.getVisibility() == GONE) continue;
497d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
498d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
499d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
500d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (lp.isChallenge) {
501d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Challenge views pin to the bottom, offset by a portion of their height,
502d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // and center horizontally.
503d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int center = (paddingLeft + width - paddingRight) / 2;
504d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int childWidth = child.getMeasuredWidth();
505d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int childHeight = child.getMeasuredHeight();
506d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int left = center - childWidth / 2;
507d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int layoutBottom = height - paddingBottom - lp.bottomMargin;
508d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // We use the top of the challenge view to position the handle, so
509d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // we never want less than the handle size showing at the bottom.
510d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int bottom = layoutBottom + (int) ((childHeight - mDragHandleSize)
511d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        * (1 - mChallengeOffset));
512d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                float offset = 1.f - (bottom - layoutBottom) / childHeight;
513d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.setAlpha(offset);
514d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.layout(left, bottom - childHeight, left + childWidth, bottom);
515d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            } else {
516d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Non-challenge views lay out from the upper left, layered.
517d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.layout(paddingLeft + lp.leftMargin,
518d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingTop + lp.topMargin,
519d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingLeft + child.getMeasuredWidth(),
520d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingTop + child.getMeasuredHeight());
521d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
522d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
523d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
524d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (!mHasLayout) {
525d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // We want to trigger the initial listener updates outside of layout pass,
526d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // in case the listeners trigger requestLayout().
527d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            post(new Runnable() {
528d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                @Override
529d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                public void run() {
530d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    sendInitialListenerUpdates();
531d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
532d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            });
533d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
534d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = true;
535d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
536d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
537d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void computeScroll() {
538d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.computeScroll();
539d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
540d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (!mScroller.isFinished()) {
541d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (mChallengeView == null) {
542d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Can't scroll if the view is missing.
543d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                Log.e(TAG, "Challenge view missing in computeScroll");
544d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                mScroller.abortAnimation();
545d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                return;
546d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
547d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
548d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mScroller.computeScrollOffset();
549d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            moveChallengeTo(mScroller.getCurrY());
550d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
551d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (mScroller.isFinished()) {
552d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                post(mEndScrollRunnable);
553d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
554d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
555d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
556d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
557d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
558d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void draw(Canvas c) {
559d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.draw(c);
56086b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock        if (mChallengeOffset < 1.f
56186b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock                && mChallengeView != null && mHandleDrawable != null && mShowHandle) {
562d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final int top = mChallengeView.getTop();
563d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mHandleDrawable.setBounds(0, top, getWidth(), top + mDragHandleSize);
564d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final float alpha = sHandleFadeInterpolator.getInterpolation(1 - mChallengeOffset);
565d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mHandleDrawable.setAlpha((int) (alpha * 0xFF));
566d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mHandleDrawable.draw(c);
567d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
568d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
569d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
570d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
571d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Move the bottom edge of mChallengeView to a new position and notify the listener
572d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * if it represents a change in position. Changes made through this method will
573d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * be stable across layout passes. If this method is called before first layout of
574d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * this SlidingChallengeLayout it will have no effect.
575d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     *
576d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system.
577d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @return true if the challenge view was moved
578d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
579d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean moveChallengeTo(int bottom) {
580d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null || !mHasLayout) {
581d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return false;
582d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
583d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
584d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
585d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int layoutBottom = getHeight() - getPaddingBottom() - bottomMargin;
586d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int challengeHeight = mChallengeView.getHeight();
587d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
588d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        bottom = Math.max(layoutBottom,
589d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                Math.min(bottom, layoutBottom + challengeHeight - mDragHandleSize));
590d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
591d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        float offset = 1.f - (float) (bottom - layoutBottom) / (challengeHeight - mDragHandleSize);
592d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeOffset = offset;
593d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (offset > 0 && !mChallengeShowing) {
594d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            setChallengeShowing(true);
595d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
596d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
597d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView.layout(mChallengeView.getLeft(),
598d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom);
599d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
600d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView.setAlpha(offset);
601d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mListener != null) {
602d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mListener.onScrollPositionChanged(offset, mChallengeView.getTop());
603d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
604d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        postInvalidateOnAnimation();
605d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return true;
606d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
607d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
608d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int getChallengeBottom() {
609d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return 0;
610d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
611d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mChallengeView.getBottom();
612d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
613d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
614022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    private int getChallengeOpenedTop() {
615022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int paddedBottom = getHeight() - getPaddingBottom();
616022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        if (mChallengeView == null) return paddedBottom;
617022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
618022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
619022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int layoutBottom = paddedBottom - bottomMargin;
620022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
621022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        return layoutBottom - mChallengeView.getHeight();
622022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    }
623022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
624d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void moveChallengeBy(int delta) {
625d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        moveChallengeTo(getChallengeBottom() + delta);
626d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
627d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
628d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
629d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Show or hide the challenge view, animating it if necessary.
630d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param show true to show, false to hide
631d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
632d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void showChallenge(boolean show) {
633d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        showChallenge(show, 0);
634d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
635d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
636d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void showChallenge(int velocity) {
637d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        boolean show = false;
638d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (Math.abs(velocity) > mMinVelocity) {
639d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            show = velocity < 0;
640d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        } else {
641d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            show = mChallengeOffset >= 0.5f;
642d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
643d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        showChallenge(show, velocity);
644d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
645d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
646d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void showChallenge(boolean show, int velocity) {
647d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) {
648d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            setChallengeShowing(false);
649d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
650d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
651d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
652d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mHasLayout) {
653d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
654d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final int layoutBottom = getHeight() - getPaddingBottom() - bottomMargin;
655d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            animateChallengeTo(show ? layoutBottom :
656d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                layoutBottom + mChallengeView.getHeight() - mDragHandleSize, velocity);
657d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
658d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
659d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
660d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
66119a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    public void showBouncer() {
66219a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller        // TODO Block access to other views
66319a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller        showChallenge(true);
66419a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    }
66519a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller
66686b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock    public void showHandle(boolean show) {
66786b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock        mShowHandle = show;
66886b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock        invalidate();
66986b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock    }
67086b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock
67119a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    @Override
672d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
673d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return new LayoutParams(getContext(), attrs);
674d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
675d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
676d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
677d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
678d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
679d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
680d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                new LayoutParams(p);
681d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
682d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
683d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
684d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
685d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return new LayoutParams();
686d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
687d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
688d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
689d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
690d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return p instanceof LayoutParams;
691d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
692d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
693d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static class LayoutParams extends MarginLayoutParams {
694d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public boolean isChallenge = false;
695d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
696d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams() {
697d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            this(MATCH_PARENT, WRAP_CONTENT);
698d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
699d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
700d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(int width, int height) {
701d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(width, height);
702d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
703d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
704d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(android.view.ViewGroup.LayoutParams source) {
705d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
706d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
707d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
708d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(MarginLayoutParams source) {
709d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
710d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
711d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
712d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(LayoutParams source) {
713d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
714d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
715d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            isChallenge = source.isChallenge;
716d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
717d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
718d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(Context c, AttributeSet attrs) {
719d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(c, attrs);
720d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
721d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final TypedArray a = c.obtainStyledAttributes(attrs,
722d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    R.styleable.SlidingChallengeLayout_Layout);
723d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            isChallenge = a.getBoolean(R.styleable.SlidingChallengeLayout_Layout_layout_isChallenge,
724d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    false);
725d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            a.recycle();
726d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
727d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
728d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller}
729