SlidingChallengeLayout.java revision 1bfcc82184824c8dc648a0948303f4b3611ac087
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
196f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.animation.ObjectAnimator;
20d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.content.Context;
21d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.content.res.TypedArray;
22d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.graphics.Canvas;
231bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandlerimport android.graphics.Paint;
24d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.graphics.drawable.Drawable;
25d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.AttributeSet;
266f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.util.FloatProperty;
27d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.Log;
286f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.util.Property;
29d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.MotionEvent;
30d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.VelocityTracker;
31d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.View;
32d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewConfiguration;
33d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewGroup;
34d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.animation.Interpolator;
35d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.widget.Scroller;
36d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
37d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport com.android.internal.R;
38d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
39d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller/**
40d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * This layout handles interaction with the sliding security challenge views
41d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * that overlay/resize other keyguard contents.
42d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */
4319a5267003e7dc70100a4bd4f1f449523b2ff38bJim Millerpublic class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout {
44d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final String TAG = "SlidingChallengeLayout";
451bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler    private static final boolean DEBUG = false;
46d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
47d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Drawn to show the drag handle in closed state; crossfades to the challenge view
48d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // when challenge is fully visible
49d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private Drawable mHandleDrawable;
506f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    private Drawable mFrameDrawable;
516f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    private Drawable mDragIconDrawable;
52d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
53d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Initialized during measurement from child layoutparams
54d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private View mChallengeView;
55eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private View mScrimView;
56d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
57d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Range: 0 (fully hidden) to 1 (fully visible)
58d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private float mChallengeOffset = 1.f;
59d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mChallengeShowing = true;
60eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private boolean mIsBouncing = false;
61d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
62d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private final Scroller mScroller;
63d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mScrollState;
640b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private OnChallengeScrolledListener mScrollListener;
650b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private OnBouncerStateChangedListener mBouncerListener;
66d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
67d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_IDLE = 0;
68d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_DRAGGING = 1;
69d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_SETTLING = 2;
70d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
71d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int MAX_SETTLE_DURATION = 600; // ms
72d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
73d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // ID of the pointer in charge of a current drag
74d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mActivePointerId = INVALID_POINTER;
75d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int INVALID_POINTER = -1;
76d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
77d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if the user is currently dragging the slider
78d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mDragging;
79d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if the user may not drag until a new gesture begins
80d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mBlockDrag;
81d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
82d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private VelocityTracker mVelocityTracker;
83d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mMinVelocity;
84d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mMaxVelocity;
85d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private float mLastTouchY;
861bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler    private int mDragHandleSize; // handle hitrect extension into the challenge view
871bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler    private int mDragHandleHeadroom; // extend the handle's hitrect this far above the line
880b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private int mDragHandleEdgeSlop;
896f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    float mHandleAlpha;
906f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    float mFrameAlpha;
916f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    private ObjectAnimator mHandleAnimation;
926f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    private ObjectAnimator mFrameAnimation;
936f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
946f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA =
956f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            new FloatProperty<SlidingChallengeLayout>("handleAlpha") {
966f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        @Override
976f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        public void setValue(SlidingChallengeLayout view, float value) {
986f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            view.mHandleAlpha = value;
996f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            view.invalidate();
1006f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
1016f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
1026f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        @Override
1036f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        public Float get(SlidingChallengeLayout view) {
1046f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            return view.mHandleAlpha;
1056f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
1066f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    };
1076f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
1086f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    static final Property<SlidingChallengeLayout, Float> FRAME_ALPHA =
1096f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            new FloatProperty<SlidingChallengeLayout>("frameAlpha") {
1106f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        @Override
1116f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        public void setValue(SlidingChallengeLayout view, float value) {
1126f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            if (view.mFrameDrawable != null) {
1136f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                view.mFrameAlpha = value;
1146f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                view.mFrameDrawable.setAlpha((int) (value * 0xFF));
1156f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                view.mFrameDrawable.invalidateSelf();
1166f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            }
1176f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
1186f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
1196f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        @Override
1206f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        public Float get(SlidingChallengeLayout view) {
1216f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            return view.mFrameAlpha;
1226f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
1236f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    };
124d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
125d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int DRAG_HANDLE_DEFAULT_SIZE = 32; // dp
1266f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    private static final int HANDLE_ANIMATE_DURATION = 200; // ms
127d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
128d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if at least one layout pass has happened since the view was attached.
129d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mHasLayout;
130d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
131d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final Interpolator sMotionInterpolator = new Interpolator() {
132d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public float getInterpolation(float t) {
133d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            t -= 1.0f;
134d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return t * t * t * t * t + 1.0f;
135d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
136d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
137d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
138d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final Interpolator sHandleFadeInterpolator = new Interpolator() {
139d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public float getInterpolation(float t) {
140d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return t * t;
141d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
142d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
143d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
144d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private final Runnable mEndScrollRunnable = new Runnable () {
145d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void run() {
146d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            completeChallengeScroll();
147d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
148d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
149d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
150eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private final OnClickListener mScrimClickListener = new OnClickListener() {
151eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        @Override
152eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public void onClick(View v) {
153eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            hideBouncer();
154eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
155eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    };
156eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
157d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
158d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Listener interface that reports changes in scroll state of the challenge area.
159d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
160d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public interface OnChallengeScrolledListener {
161d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        /**
162d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * The scroll state itself changed.
163d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
164d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>scrollState will be one of the following:</p>
165d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
166d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <ul>
167d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li>
168d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging
169d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * the challenge area.</li>
170d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating
171d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * into place.</li>
172d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * </ul>
173d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
174d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>Do not perform expensive operations (e.g. layout)
175d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p>
176d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
177d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param scrollState The new scroll state of the challenge area.
178d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         */
179d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void onScrollStateChanged(int scrollState);
180d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
181d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        /**
182d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * The precise position of the challenge area has changed.
183d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
184d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>NOTE: It is NOT safe to modify layout or call any View methods that may
185d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * result in a requestLayout anywhere in your view hierarchy as a result of this call.
186d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * It may be called during drawing.</p>
187d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
188d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param scrollPosition New relative position of the challenge area.
189d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                       1.f = fully visible/ready to be interacted with.
190d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                       0.f = fully invisible/inaccessible to the user.
191d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param challengeTop Position of the top edge of the challenge view in px in the
192d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                     SlidingChallengeLayout's coordinate system.
193d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         */
194d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void onScrollPositionChanged(float scrollPosition, int challengeTop);
195d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
196d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
197d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context) {
198d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        this(context, null);
199d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
200d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
201d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context, AttributeSet attrs) {
202d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        this(context, attrs, 0);
203d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
204d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
205d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) {
206d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super(context, attrs, defStyle);
207d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
208d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final TypedArray a = context.obtainStyledAttributes(attrs,
209d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                R.styleable.SlidingChallengeLayout, defStyle, 0);
2106f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        setDragDrawables(a.getDrawable(R.styleable.SlidingChallengeLayout_dragHandle),
2116f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                a.getDrawable(R.styleable.SlidingChallengeLayout_dragIcon));
212d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
213d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        a.recycle();
214d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
215d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mScroller = new Scroller(context, sMotionInterpolator);
216d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
217d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final ViewConfiguration vc = ViewConfiguration.get(context);
218d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mMinVelocity = vc.getScaledMinimumFlingVelocity();
219d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mMaxVelocity = vc.getScaledMaximumFlingVelocity();
220d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
2210b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        mDragHandleEdgeSlop = getResources().getDimensionPixelSize(
2220b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell                R.dimen.kg_edge_swipe_region_size);
2230b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
224d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setWillNotDraw(false);
225d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
226d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
2276f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    public void setDragDrawables(Drawable handle, Drawable icon) {
2286f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        final float density = getResources().getDisplayMetrics().density;
2296f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        final int defaultSize = (int) (DRAG_HANDLE_DEFAULT_SIZE * density + 0.5f);
2306f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mDragHandleSize = Math.max(handle != null ? handle.getIntrinsicHeight() : defaultSize,
2316f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                icon != null ? icon.getIntrinsicHeight() : defaultSize);
2321bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
2331bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        // top half of the lock icon, plus another 25% to be sure
2341bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        mDragHandleHeadroom = (icon != null) ? (int)(icon.getIntrinsicHeight() * 0.75f) : 0;
2351bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
2366f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleDrawable = handle;
2376f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mDragIconDrawable = icon;
2386f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    }
2396f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
2406f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    public void setDragIconDrawable(Drawable d) {
2416f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mDragIconDrawable = d;
2426f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    }
2436f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
2446f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    public void showHandle(boolean visible) {
2456f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        if (visible) {
2466f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            if (mHandleAnimation != null) {
2476f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                mHandleAnimation.cancel();
2486f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                mHandleAnimation = null;
2496f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            }
2506f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mHandleAlpha = 1.f;
2516f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            invalidate();
2526f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        } else {
2536f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            animateHandle(false);
2546f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
2556f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    }
2566f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
2576f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    void animateHandle(boolean visible) {
2586f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        if (mHandleAnimation != null) {
2596f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mHandleAnimation.cancel();
260d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
2616f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, visible ? 1.f : 0.f);
2626f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleAnimation.setInterpolator(sHandleFadeInterpolator);
2636f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleAnimation.setDuration(HANDLE_ANIMATE_DURATION);
2646f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleAnimation.start();
2656f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    }
2666f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
2676f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    void animateFrame(boolean visible, boolean full) {
2686f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        if (mFrameDrawable == null) return;
2696f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
2706f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        if (mFrameAnimation != null) {
2716f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mFrameAnimation.cancel();
272d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
2736f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mFrameAnimation = ObjectAnimator.ofFloat(this, FRAME_ALPHA,
2746f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                visible ? (full ? 1.f : 0.5f) : 0.f);
2756f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mFrameAnimation.setInterpolator(sHandleFadeInterpolator);
2766f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mFrameAnimation.setDuration(HANDLE_ANIMATE_DURATION);
2776f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mFrameAnimation.start();
278d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
279d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
280d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void sendInitialListenerUpdates() {
2810b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mScrollListener != null) {
282d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0;
2830b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollPositionChanged(mChallengeOffset, challengeTop);
2840b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollStateChanged(mScrollState);
285d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
286d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
287d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
288d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) {
2890b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        mScrollListener = listener;
290d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mHasLayout) {
291d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            sendInitialListenerUpdates();
292d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
293d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
294d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
2950b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
2960b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        mBouncerListener = listener;
2970b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    }
2980b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
299d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
300d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void onAttachedToWindow() {
301d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.onAttachedToWindow();
302d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
303d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = false;
304d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
305d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
306d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
307d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void onDetachedFromWindow() {
308d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.onDetachedFromWindow();
309d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
310d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        removeCallbacks(mEndScrollRunnable);
311d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = false;
312d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
313d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
3140b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    @Override
3150b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    public void requestChildFocus(View child, View focused) {
3160b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mIsBouncing && child != mChallengeView) {
3170b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            // Clear out of the bouncer if the user tries to move focus outside of
3180b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            // the security challenge view.
3190b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            hideBouncer();
3200b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
3210b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        super.requestChildFocus(child, focused);
3220b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    }
3230b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
324d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // We want the duration of the page snap animation to be influenced by the distance that
325d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // the screen has to travel, however, we don't want this duration to be effected in a
326d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
327d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // of travel has on the overall snap duration.
328d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    float distanceInfluenceForSnapDuration(float f) {
329d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        f -= 0.5f; // center the values about 0.
330d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        f *= 0.3f * Math.PI / 2.0f;
331d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return (float) Math.sin(f);
332d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
333d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
334d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void setScrollState(int state) {
335d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mScrollState != state) {
336d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mScrollState = state;
337d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
3386f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing);
3396f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            animateFrame(state == SCROLL_STATE_DRAGGING, false);
3400b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            if (mScrollListener != null) {
3410b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell                mScrollListener.onScrollStateChanged(state);
342d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
343d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
344d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
345d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
346d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void completeChallengeScroll() {
347d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setChallengeShowing(mChallengeOffset != 0);
348d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setScrollState(SCROLL_STATE_IDLE);
349d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
350d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
351eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    void setScrimView(View scrim) {
352eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
353eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            mScrimView.setOnClickListener(null);
354eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
355eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView = scrim;
356eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
357eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setFocusable(true);
358eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setOnClickListener(mScrimClickListener);
359eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
360eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
361d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
362d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Animate the bottom edge of the challenge view to the given position.
363d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     *
364d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param y desired final position for the bottom edge of the challenge view in px
365d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param velocity velocity in
366d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
367d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void animateChallengeTo(int y, int velocity) {
368d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) {
369d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // Nothing to do.
370d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
371d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
372d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int sy = mChallengeView.getBottom();
373d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int dy = y - sy;
374d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (dy == 0) {
375d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            completeChallengeScroll();
376d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
377d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
378d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
379d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setScrollState(SCROLL_STATE_SETTLING);
380d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
381d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int childHeight = mChallengeView.getHeight();
382d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int halfHeight = childHeight / 2;
383d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight);
384d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final float distance = halfHeight + halfHeight *
385d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                distanceInfluenceForSnapDuration(distanceRatio);
386d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
387d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int duration = 0;
388d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        velocity = Math.abs(velocity);
389d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (velocity > 0) {
390d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
391d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        } else {
392d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final float childDelta = (float) Math.abs(dy) / childHeight;
393d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            duration = (int) ((childDelta + 1) * 100);
394d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
395d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        duration = Math.min(duration, MAX_SETTLE_DURATION);
396d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
397d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mScroller.startScroll(0, sy, 0, dy, duration);
398d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        postInvalidateOnAnimation();
399d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
400d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
401d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void setChallengeShowing(boolean showChallenge) {
402d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeShowing != showChallenge) {
403d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mChallengeShowing = showChallenge;
404d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (mChallengeView != null) {
405d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                mChallengeView.setVisibility(showChallenge ? VISIBLE : INVISIBLE);
406d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
407d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
408d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
409d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
410d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
411d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @return true if the challenge is at all visible.
412d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
413d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean isChallengeShowing() {
414d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mChallengeShowing;
415d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
416d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
417d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
41819a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    public boolean isChallengeOverlapping() {
41919a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller        return mChallengeShowing;
42019a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    }
42119a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller
42219a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    @Override
423eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public boolean isBouncing() {
424eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        return mIsBouncing;
425eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
426eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
427eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
428eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public void showBouncer() {
429eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mIsBouncing) return;
430eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        showChallenge(true);
431eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mIsBouncing = true;
432eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
4336f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mScrimView.setVisibility(VISIBLE);
434eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
4356f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        animateFrame(true, true);
4360b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mBouncerListener != null) {
4370b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mBouncerListener.onBouncerStateChanged(true);
4380b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
439eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
440eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
441eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
442eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public void hideBouncer() {
443eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (!mIsBouncing) return;
444eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        setChallengeShowing(false);
445eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mIsBouncing = false;
446eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
447eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            mScrimView.setVisibility(GONE);
448eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
4496f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        animateFrame(false, false);
4500b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mBouncerListener != null) {
4510b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mBouncerListener.onBouncerStateChanged(false);
4520b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
453eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
454eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
455eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
456d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void requestDisallowInterceptTouchEvent(boolean allowIntercept) {
457d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
458d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // If there are one or more pointers in the challenge view before we take over
459d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // touch events, onInterceptTouchEvent will set mBlockDrag.
460d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
461d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
462d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
463d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean onInterceptTouchEvent(MotionEvent ev) {
464d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mVelocityTracker == null) {
465d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mVelocityTracker = VelocityTracker.obtain();
466d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
467d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.addMovement(ev);
468d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
469d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int action = ev.getActionMasked();
470d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        switch (action) {
471022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller            case MotionEvent.ACTION_DOWN:
472022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                mLastTouchY = ev.getY();
473022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                break;
474022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
475d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_CANCEL:
476d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_UP:
477d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
478d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
479d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
480d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_MOVE:
481d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int count = ev.getPointerCount();
482d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                for (int i = 0; i < count; i++) {
483d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float x = ev.getX(i);
484d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float y = ev.getY(i);
485d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
4866f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                    if (!mIsBouncing &&
4876f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                            (isInDragHandle(x, y) || crossedDragHandle(x, y, mLastTouchY) ||
488d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) &&
489d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mActivePointerId == INVALID_POINTER) {
490d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mActivePointerId = ev.getPointerId(i);
491d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mLastTouchY = y;
492d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mDragging = true;
493d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    } else if (isInChallengeView(x, y)) {
494d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mBlockDrag = true;
495d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
496d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
497d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
498d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
499d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
500d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mBlockDrag) {
501d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mActivePointerId = INVALID_POINTER;
502d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mDragging = false;
503d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
504d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
505d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mDragging;
506d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
507d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
508d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void resetTouch() {
509d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.recycle();
510d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker = null;
511d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mActivePointerId = INVALID_POINTER;
512d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mDragging = mBlockDrag = false;
513d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
514d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
515d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
516d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean onTouchEvent(MotionEvent ev) {
517d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mVelocityTracker == null) {
518d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mVelocityTracker = VelocityTracker.obtain();
519d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
520d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.addMovement(ev);
521d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
522d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int action = ev.getActionMasked();
523d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        switch (action) {
524d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_CANCEL:
525d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
526d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    showChallenge(0);
527d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
528d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
529d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
530d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
531d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_POINTER_UP:
532d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) {
533d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    break;
534d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
535d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_UP:
536d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
537d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
538d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId));
539d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
540d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
541d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
542d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
543d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_MOVE:
5446f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                if (!mDragging && !mBlockDrag && !mIsBouncing) {
545d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int count = ev.getPointerCount();
546d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    for (int i = 0; i < count; i++) {
547d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        final float x = ev.getX(i);
548d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        final float y = ev.getY(i);
549d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
550022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                        if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mLastTouchY) ||
551d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                                (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
552d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                                && mActivePointerId == INVALID_POINTER) {
553d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mActivePointerId = ev.getPointerId(i);
554d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mDragging = true;
555d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            break;
556d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        }
557d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
558d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
559d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Not an else; this can be set above.
560d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
561d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // No-op if already in this state, but set it here in case we arrived
562d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // at this point from either intercept or the above.
563d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    setScrollState(SCROLL_STATE_DRAGGING);
564d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
565d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int index = ev.findPointerIndex(mActivePointerId);
566d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    if (index < 0) {
567d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        // Oops, bogus state. We lost some touch events somewhere.
568d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        // Just drop it with no velocity and let things settle.
569d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        resetTouch();
570d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        showChallenge(0);
571d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        return true;
572d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
5731bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    final float y = Math.max(ev.getY(index),
5741bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                            getChallengeOpenedTop() - mDragHandleHeadroom);
575d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float delta = y - mLastTouchY;
576d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int idelta = (int) delta;
577d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // Don't lose the rounded component
578d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mLastTouchY = y + delta - idelta;
579d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
580d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    moveChallengeBy(idelta);
581d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
582d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
583d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
584d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return true;
585d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
586d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
587d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean isInChallengeView(float x, float y) {
588d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return false;
589d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
590d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return x >= mChallengeView.getLeft() && y >= mChallengeView.getTop() &&
591d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                x < mChallengeView.getRight() && y < mChallengeView.getBottom();
592d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
593d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
594d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean isInDragHandle(float x, float y) {
595d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return false;
596d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
5970b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        return x >= mDragHandleEdgeSlop &&
5981bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                y >= mChallengeView.getTop() - mDragHandleHeadroom &&
5990b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell                x < getWidth() - mDragHandleEdgeSlop &&
600d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                y < mChallengeView.getTop() + mDragHandleSize;
601d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
602d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
603022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    private boolean crossedDragHandle(float x, float y, float lastY) {
604022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int challengeTop = mChallengeView.getTop();
6051bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        return  x >= 0 &&
6061bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                x < getWidth() &&
6071bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                lastY < (challengeTop - mDragHandleHeadroom) &&
608022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                y > challengeTop + mDragHandleSize;
609022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    }
610022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
611d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
612d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected void onMeasure(int widthSpec, int heightSpec) {
613d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
614d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
615d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            throw new IllegalArgumentException(
616d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    "SlidingChallengeLayout must be measured with an exact size");
617d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
618d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
619d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int width = MeasureSpec.getSize(widthSpec);
620d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int height = MeasureSpec.getSize(heightSpec);
621d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setMeasuredDimension(width, height);
622d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
623d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // Find one and only one challenge view.
624d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final View oldChallengeView = mChallengeView;
625d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView = null;
626d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int count = getChildCount();
627d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        for (int i = 0; i < count; i++) {
628d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final View child = getChildAt(i);
629d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
630d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
631eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
632d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mChallengeView != null) {
633d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    throw new IllegalStateException(
634d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            "There may only be one child with layout_isChallenge=\"true\"");
635d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
636d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                mChallengeView = child;
637d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mChallengeView != oldChallengeView) {
638d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
639d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
6406f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                // We're going to play silly games with the frame's background drawable later.
6416f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                mFrameDrawable = mChallengeView.getBackground();
6426f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                mFrameDrawable.setAlpha(0);
643eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
644eee209313d564be37b8fc068f785b3844cd6597eAdam Powell                setScrimView(child);
645d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
646d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
647d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (child.getVisibility() == GONE) continue;
648d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
649d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            measureChildWithMargins(child, widthSpec, 0, heightSpec, 0);
650d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
651d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
652d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
653d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
654d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected void onLayout(boolean changed, int l, int t, int r, int b) {
655d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingLeft = getPaddingLeft();
656d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingTop = getPaddingTop();
657d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingRight = getPaddingRight();
658d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingBottom = getPaddingBottom();
659d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int width = r - l;
660d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int height = b - t;
661d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
662d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int count = getChildCount();
663d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        for (int i = 0; i < count; i++) {
664d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final View child = getChildAt(i);
665d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
666d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (child.getVisibility() == GONE) continue;
667d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
668d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
669d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
670eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
671d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Challenge views pin to the bottom, offset by a portion of their height,
672d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // and center horizontally.
673d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int center = (paddingLeft + width - paddingRight) / 2;
674d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int childWidth = child.getMeasuredWidth();
675d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int childHeight = child.getMeasuredHeight();
676d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int left = center - childWidth / 2;
677d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int layoutBottom = height - paddingBottom - lp.bottomMargin;
678d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // We use the top of the challenge view to position the handle, so
679d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // we never want less than the handle size showing at the bottom.
680d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int bottom = layoutBottom + (int) ((childHeight - mDragHandleSize)
681d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        * (1 - mChallengeOffset));
682d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                float offset = 1.f - (bottom - layoutBottom) / childHeight;
683d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.setAlpha(offset);
684d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.layout(left, bottom - childHeight, left + childWidth, bottom);
685d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            } else {
686d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Non-challenge views lay out from the upper left, layered.
687d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.layout(paddingLeft + lp.leftMargin,
688d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingTop + lp.topMargin,
689d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingLeft + child.getMeasuredWidth(),
690d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingTop + child.getMeasuredHeight());
691d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
692d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
693d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
694d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (!mHasLayout) {
695d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // We want to trigger the initial listener updates outside of layout pass,
696d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // in case the listeners trigger requestLayout().
697d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            post(new Runnable() {
698d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                @Override
699d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                public void run() {
700d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    sendInitialListenerUpdates();
701d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
702d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            });
7036f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mHasLayout = true;
704d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
705d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
706d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
707d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void computeScroll() {
708d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.computeScroll();
709d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
710d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (!mScroller.isFinished()) {
711d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (mChallengeView == null) {
712d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Can't scroll if the view is missing.
713d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                Log.e(TAG, "Challenge view missing in computeScroll");
714d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                mScroller.abortAnimation();
715d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                return;
716d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
717d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
718d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mScroller.computeScrollOffset();
719d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            moveChallengeTo(mScroller.getCurrY());
720d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
721d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (mScroller.isFinished()) {
722d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                post(mEndScrollRunnable);
723d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
724d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
725d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
726d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
727d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
728d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void draw(Canvas c) {
729d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.draw(c);
7301bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
7311bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        final Paint debugPaint;
7321bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        if (DEBUG) {
7331bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            debugPaint = new Paint();
7341bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            debugPaint.setColor(0x40FF00CC);
7351bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            // show the isInDragHandle() rect
7361bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            c.drawRect(mDragHandleEdgeSlop,
7371bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    mChallengeView.getTop() - mDragHandleHeadroom,
7381bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    getWidth() - mDragHandleEdgeSlop,
7391bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    mChallengeView.getTop() + mDragHandleSize,
7401bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    debugPaint);
7411bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        }
7421bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
7436f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        if (mChallengeView != null && mHandleAlpha > 0 && mHandleDrawable != null) {
744d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final int top = mChallengeView.getTop();
7456f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            final int handleHeight = mHandleDrawable.getIntrinsicHeight();
7466f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            final int challengeLeft = mChallengeView.getLeft();
7476f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            final int challengeRight = mChallengeView.getRight();
7486f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mHandleDrawable.setBounds(challengeLeft, top, challengeRight, top + handleHeight);
7496f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mHandleDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
750d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mHandleDrawable.draw(c);
7516f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
7521bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            if (DEBUG) {
7531bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                // now show the actual drag handle
7541bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                debugPaint.setStyle(Paint.Style.STROKE);
7551bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                debugPaint.setStrokeWidth(1);
7561bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                debugPaint.setColor(0xFF80FF00);
7571bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                c.drawRect(challengeLeft, top, challengeRight, top + handleHeight, debugPaint);
7581bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            }
7591bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
7606f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            if (mDragIconDrawable != null) {
7616f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                final int iconWidth = mDragIconDrawable.getIntrinsicWidth();
7626f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                final int iconHeight = mDragIconDrawable.getIntrinsicHeight();
7636f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                final int iconLeft = (challengeLeft + challengeRight - iconWidth) / 2;
7646f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                final int iconTop = top + (handleHeight - iconHeight) / 2;
7656f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                mDragIconDrawable.setBounds(iconLeft, iconTop, iconLeft + iconWidth,
7666f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                        iconTop + iconHeight);
7676f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                mDragIconDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
7686f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                mDragIconDrawable.draw(c);
7691bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
7701bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                if (DEBUG) {
7711bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    debugPaint.setColor(0xFF00FF00);
7721bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    c.drawRect(iconLeft, iconTop, iconLeft + iconWidth,
7731bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                        iconTop + iconHeight, debugPaint);
7741bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                }
7756f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            }
776d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
777d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
778d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
779d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
780d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Move the bottom edge of mChallengeView to a new position and notify the listener
781d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * if it represents a change in position. Changes made through this method will
782d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * be stable across layout passes. If this method is called before first layout of
783d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * this SlidingChallengeLayout it will have no effect.
784d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     *
785d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system.
786d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @return true if the challenge view was moved
787d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
788d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean moveChallengeTo(int bottom) {
789d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null || !mHasLayout) {
790d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return false;
791d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
792d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
793d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
794d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int layoutBottom = getHeight() - getPaddingBottom() - bottomMargin;
795d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int challengeHeight = mChallengeView.getHeight();
796d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
797d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        bottom = Math.max(layoutBottom,
798d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                Math.min(bottom, layoutBottom + challengeHeight - mDragHandleSize));
799d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
800d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        float offset = 1.f - (float) (bottom - layoutBottom) / (challengeHeight - mDragHandleSize);
801d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeOffset = offset;
802d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (offset > 0 && !mChallengeShowing) {
803d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            setChallengeShowing(true);
804d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
805d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
806d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView.layout(mChallengeView.getLeft(),
807d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom);
808d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
809d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView.setAlpha(offset);
8100b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mScrollListener != null) {
8110b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop());
812d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
813d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        postInvalidateOnAnimation();
814d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return true;
815d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
816d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
817d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int getChallengeBottom() {
818d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return 0;
819d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
820d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mChallengeView.getBottom();
821d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
822d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
823022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    private int getChallengeOpenedTop() {
824022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int paddedBottom = getHeight() - getPaddingBottom();
825022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        if (mChallengeView == null) return paddedBottom;
826022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
827022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
828022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int layoutBottom = paddedBottom - bottomMargin;
829022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
830022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        return layoutBottom - mChallengeView.getHeight();
831022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    }
832022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
833d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void moveChallengeBy(int delta) {
834d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        moveChallengeTo(getChallengeBottom() + delta);
835d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
836d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
837d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
838d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Show or hide the challenge view, animating it if necessary.
839d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param show true to show, false to hide
840d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
841d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void showChallenge(boolean show) {
842d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        showChallenge(show, 0);
843d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
844d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
845d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void showChallenge(int velocity) {
846d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        boolean show = false;
847d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (Math.abs(velocity) > mMinVelocity) {
848d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            show = velocity < 0;
849d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        } else {
850d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            show = mChallengeOffset >= 0.5f;
851d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
852d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        showChallenge(show, velocity);
853d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
854d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
855d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void showChallenge(boolean show, int velocity) {
856d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) {
857d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            setChallengeShowing(false);
858d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
859d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
860d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
861d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mHasLayout) {
862d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
863d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final int layoutBottom = getHeight() - getPaddingBottom() - bottomMargin;
864d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            animateChallengeTo(show ? layoutBottom :
865d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                layoutBottom + mChallengeView.getHeight() - mDragHandleSize, velocity);
866d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
867d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
868d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
86919a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    @Override
870d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
871d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return new LayoutParams(getContext(), attrs);
872d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
873d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
874d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
875d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
876d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
877d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
878d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                new LayoutParams(p);
879d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
880d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
881d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
882d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
883d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return new LayoutParams();
884d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
885d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
886d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
887d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
888d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return p instanceof LayoutParams;
889d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
890d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
891d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static class LayoutParams extends MarginLayoutParams {
892eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public int childType = CHILD_TYPE_NONE;
893eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_NONE = 0;
894eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_CHALLENGE = 2;
895eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_SCRIM = 4;
896d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
897d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams() {
898d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            this(MATCH_PARENT, WRAP_CONTENT);
899d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
900d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
901d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(int width, int height) {
902d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(width, height);
903d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
904d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
905d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(android.view.ViewGroup.LayoutParams source) {
906d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
907d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
908d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
909d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(MarginLayoutParams source) {
910d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
911d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
912d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
913d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(LayoutParams source) {
914d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
915d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
916eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            childType = source.childType;
917d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
918d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
919d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(Context c, AttributeSet attrs) {
920d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(c, attrs);
921d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
922d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final TypedArray a = c.obtainStyledAttributes(attrs,
923d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    R.styleable.SlidingChallengeLayout_Layout);
924eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            childType = a.getInt(R.styleable.SlidingChallengeLayout_Layout_layout_childType,
925eee209313d564be37b8fc068f785b3844cd6597eAdam Powell                    CHILD_TYPE_NONE);
926d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            a.recycle();
927d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
928d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
929d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller}
930