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