SlidingChallengeLayout.java revision 0b1b552268bc6641cc5d01fb80c12258da9c6985
1d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller/*
2d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Copyright (C) 2012 The Android Open Source Project
3d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller *
4d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Licensed under the Apache License, Version 2.0 (the "License");
5d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * you may not use this file except in compliance with the License.
6d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * You may obtain a copy of the License at
7d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller *
8d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller *      http://www.apache.org/licenses/LICENSE-2.0
9d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller *
10d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Unless required by applicable law or agreed to in writing, software
11d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * distributed under the License is distributed on an "AS IS" BASIS,
12d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * See the License for the specific language governing permissions and
14d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * limitations under the License.
15d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */
16d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
17d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerpackage com.android.internal.policy.impl.keyguard;
18d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
19d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.content.Context;
20d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.content.res.TypedArray;
21d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.graphics.Canvas;
22d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.graphics.drawable.Drawable;
23d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.AttributeSet;
24d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.Log;
25d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.MotionEvent;
26d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.VelocityTracker;
27d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.View;
28d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewConfiguration;
29d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewGroup;
30d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.animation.Interpolator;
31d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.widget.Scroller;
32d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
33d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport com.android.internal.R;
34d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
35d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller/**
36d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * This layout handles interaction with the sliding security challenge views
37d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * that overlay/resize other keyguard contents.
38d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */
3919a5267003e7dc70100a4bd4f1f449523b2ff38bJim Millerpublic class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout {
40d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final String TAG = "SlidingChallengeLayout";
41d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
42d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Drawn to show the drag handle in closed state; crossfades to the challenge view
43d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // when challenge is fully visible
44d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private Drawable mHandleDrawable;
4586b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock    private boolean mShowHandle = true;
46d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
47d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Initialized during measurement from child layoutparams
48d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private View mChallengeView;
49eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private View mScrimView;
50d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
51d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Range: 0 (fully hidden) to 1 (fully visible)
52d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private float mChallengeOffset = 1.f;
53d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mChallengeShowing = true;
54eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private boolean mIsBouncing = false;
55d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
56d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private final Scroller mScroller;
57d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mScrollState;
580b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private OnChallengeScrolledListener mScrollListener;
590b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private OnBouncerStateChangedListener mBouncerListener;
60d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
61d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_IDLE = 0;
62d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_DRAGGING = 1;
63d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_SETTLING = 2;
64d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
65d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int MAX_SETTLE_DURATION = 600; // ms
66d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
67d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // ID of the pointer in charge of a current drag
68d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mActivePointerId = INVALID_POINTER;
69d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int INVALID_POINTER = -1;
70d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
71d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if the user is currently dragging the slider
72d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mDragging;
73d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if the user may not drag until a new gesture begins
74d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mBlockDrag;
75d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
76d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private VelocityTracker mVelocityTracker;
77d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mMinVelocity;
78d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mMaxVelocity;
79d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private float mLastTouchY;
80d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mDragHandleSize;
810b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private int mDragHandleEdgeSlop;
82d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
83d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int DRAG_HANDLE_DEFAULT_SIZE = 32; // dp
84d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
85d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if at least one layout pass has happened since the view was attached.
86d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mHasLayout;
87d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
88d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final Interpolator sMotionInterpolator = new Interpolator() {
89d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public float getInterpolation(float t) {
90d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            t -= 1.0f;
91d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return t * t * t * t * t + 1.0f;
92d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
93d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
94d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
95d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final Interpolator sHandleFadeInterpolator = new Interpolator() {
96d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public float getInterpolation(float t) {
97d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return t * t;
98d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
99d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
100d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
101d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private final Runnable mEndScrollRunnable = new Runnable () {
102d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void run() {
103d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            completeChallengeScroll();
104d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
105d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
106d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
107eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private final OnClickListener mScrimClickListener = new OnClickListener() {
108eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        @Override
109eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public void onClick(View v) {
110eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            hideBouncer();
111eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
112eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    };
113eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
114d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
115d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Listener interface that reports changes in scroll state of the challenge area.
116d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
117d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public interface OnChallengeScrolledListener {
118d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        /**
119d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * The scroll state itself changed.
120d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
121d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>scrollState will be one of the following:</p>
122d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
123d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <ul>
124d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li>
125d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging
126d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * the challenge area.</li>
127d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating
128d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * into place.</li>
129d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * </ul>
130d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
131d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>Do not perform expensive operations (e.g. layout)
132d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p>
133d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
134d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param scrollState The new scroll state of the challenge area.
135d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         */
136d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void onScrollStateChanged(int scrollState);
137d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
138d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        /**
139d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * The precise position of the challenge area has changed.
140d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
141d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>NOTE: It is NOT safe to modify layout or call any View methods that may
142d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * result in a requestLayout anywhere in your view hierarchy as a result of this call.
143d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * It may be called during drawing.</p>
144d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
145d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param scrollPosition New relative position of the challenge area.
146d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                       1.f = fully visible/ready to be interacted with.
147d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                       0.f = fully invisible/inaccessible to the user.
148d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param challengeTop Position of the top edge of the challenge view in px in the
149d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                     SlidingChallengeLayout's coordinate system.
150d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         */
151d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void onScrollPositionChanged(float scrollPosition, int challengeTop);
152d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
153d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
154d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context) {
155d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        this(context, null);
156d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
157d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
158d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context, AttributeSet attrs) {
159d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        this(context, attrs, 0);
160d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
161d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
162d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) {
163d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super(context, attrs, defStyle);
164d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
165d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final TypedArray a = context.obtainStyledAttributes(attrs,
166d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                R.styleable.SlidingChallengeLayout, defStyle, 0);
167d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setDragHandleDrawable(a.getDrawable(R.styleable.SlidingChallengeLayout_dragHandle));
168d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
169d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        a.recycle();
170d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
171d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mScroller = new Scroller(context, sMotionInterpolator);
172d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
173d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final ViewConfiguration vc = ViewConfiguration.get(context);
174d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mMinVelocity = vc.getScaledMinimumFlingVelocity();
175d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mMaxVelocity = vc.getScaledMaximumFlingVelocity();
176d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1770b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        mDragHandleEdgeSlop = getResources().getDimensionPixelSize(
1780b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell                R.dimen.kg_edge_swipe_region_size);
1790b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
180d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setWillNotDraw(false);
181d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
182d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
183d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void setDragHandleDrawable(Drawable d) {
184d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (d != null) {
185d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mDragHandleSize = d.getIntrinsicHeight();
186d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
187d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mDragHandleSize == 0 || d == null) {
188d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final float density = getResources().getDisplayMetrics().density;
189d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mDragHandleSize = (int) (DRAG_HANDLE_DEFAULT_SIZE * density + 0.5f);
190d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
191d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHandleDrawable = d;
192d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
193d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
194d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void sendInitialListenerUpdates() {
1950b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mScrollListener != null) {
196d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0;
1970b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollPositionChanged(mChallengeOffset, challengeTop);
1980b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollStateChanged(mScrollState);
199d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
200d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
201d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
202d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) {
2030b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        mScrollListener = listener;
204d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mHasLayout) {
205d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            sendInitialListenerUpdates();
206d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
207d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
208d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
2090b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
2100b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        mBouncerListener = listener;
2110b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    }
2120b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
213d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
214d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void onAttachedToWindow() {
215d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.onAttachedToWindow();
216d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
217d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = false;
218d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
219d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
220d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
221d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void onDetachedFromWindow() {
222d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.onDetachedFromWindow();
223d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
224d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        removeCallbacks(mEndScrollRunnable);
225d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = false;
226d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
227d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
2280b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    @Override
2290b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    public void requestChildFocus(View child, View focused) {
2300b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mIsBouncing && child != mChallengeView) {
2310b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            // Clear out of the bouncer if the user tries to move focus outside of
2320b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            // the security challenge view.
2330b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            hideBouncer();
2340b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
2350b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        super.requestChildFocus(child, focused);
2360b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    }
2370b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
238d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // We want the duration of the page snap animation to be influenced by the distance that
239d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // the screen has to travel, however, we don't want this duration to be effected in a
240d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
241d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // of travel has on the overall snap duration.
242d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    float distanceInfluenceForSnapDuration(float f) {
243d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        f -= 0.5f; // center the values about 0.
244d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        f *= 0.3f * Math.PI / 2.0f;
245d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return (float) Math.sin(f);
246d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
247d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
248d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void setScrollState(int state) {
249d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mScrollState != state) {
250d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mScrollState = state;
251d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
2520b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            if (mScrollListener != null) {
2530b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell                mScrollListener.onScrollStateChanged(state);
254d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
255d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
256d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
257d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
258d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void completeChallengeScroll() {
259d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setChallengeShowing(mChallengeOffset != 0);
260d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setScrollState(SCROLL_STATE_IDLE);
261d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
262d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
263eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    void setScrimView(View scrim) {
264eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
265eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            mScrimView.setOnClickListener(null);
266eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
267eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView = scrim;
268eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
269eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setFocusable(true);
270eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setOnClickListener(mScrimClickListener);
271eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
272eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
273d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
274d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Animate the bottom edge of the challenge view to the given position.
275d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     *
276d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param y desired final position for the bottom edge of the challenge view in px
277d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param velocity velocity in
278d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
279d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void animateChallengeTo(int y, int velocity) {
280d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) {
281d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // Nothing to do.
282d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
283d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
284d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int sy = mChallengeView.getBottom();
285d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int dy = y - sy;
286d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (dy == 0) {
287d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            completeChallengeScroll();
288d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
289d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
290d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
291d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setScrollState(SCROLL_STATE_SETTLING);
292d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
293d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int childHeight = mChallengeView.getHeight();
294d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int halfHeight = childHeight / 2;
295d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight);
296d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final float distance = halfHeight + halfHeight *
297d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                distanceInfluenceForSnapDuration(distanceRatio);
298d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
299d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int duration = 0;
300d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        velocity = Math.abs(velocity);
301d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (velocity > 0) {
302d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
303d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        } else {
304d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final float childDelta = (float) Math.abs(dy) / childHeight;
305d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            duration = (int) ((childDelta + 1) * 100);
306d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
307d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        duration = Math.min(duration, MAX_SETTLE_DURATION);
308d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
309d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mScroller.startScroll(0, sy, 0, dy, duration);
310d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        postInvalidateOnAnimation();
311d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
312d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
313d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void setChallengeShowing(boolean showChallenge) {
314d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeShowing != showChallenge) {
315d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mChallengeShowing = showChallenge;
316d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (mChallengeView != null) {
317d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                mChallengeView.setVisibility(showChallenge ? VISIBLE : INVISIBLE);
318d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
319d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
320d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
321d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
322d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
323d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @return true if the challenge is at all visible.
324d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
325d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean isChallengeShowing() {
326d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mChallengeShowing;
327d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
328d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
329d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
33019a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    public boolean isChallengeOverlapping() {
33119a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller        return mChallengeShowing;
33219a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    }
33319a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller
33419a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    @Override
335eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public boolean isBouncing() {
336eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        return mIsBouncing;
337eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
338eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
339eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
340eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public void showBouncer() {
341eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mIsBouncing) return;
342eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        showChallenge(true);
343eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mIsBouncing = true;
344eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
345eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            mScrimView.setVisibility(GONE);
346eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
3470b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mBouncerListener != null) {
3480b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mBouncerListener.onBouncerStateChanged(true);
3490b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
350eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
351eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
352eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
353eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public void hideBouncer() {
354eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (!mIsBouncing) return;
355eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        setChallengeShowing(false);
356eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mIsBouncing = false;
357eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
358eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            mScrimView.setVisibility(GONE);
359eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
3600b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mBouncerListener != null) {
3610b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mBouncerListener.onBouncerStateChanged(false);
3620b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
363eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
364eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
365eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
366d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void requestDisallowInterceptTouchEvent(boolean allowIntercept) {
367d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
368d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // If there are one or more pointers in the challenge view before we take over
369d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // touch events, onInterceptTouchEvent will set mBlockDrag.
370d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
371d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
372d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
373d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean onInterceptTouchEvent(MotionEvent ev) {
374d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mVelocityTracker == null) {
375d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mVelocityTracker = VelocityTracker.obtain();
376d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
377d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.addMovement(ev);
378d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
379d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int action = ev.getActionMasked();
380d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        switch (action) {
381022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller            case MotionEvent.ACTION_DOWN:
382022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                mLastTouchY = ev.getY();
383022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                break;
384022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
385d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_CANCEL:
386d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_UP:
387d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
388d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
389d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
390d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_MOVE:
391d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int count = ev.getPointerCount();
392d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                for (int i = 0; i < count; i++) {
393d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float x = ev.getX(i);
394d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float y = ev.getY(i);
395d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
396022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                    if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mLastTouchY) ||
397d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) &&
398d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mActivePointerId == INVALID_POINTER) {
399d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mActivePointerId = ev.getPointerId(i);
400d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mLastTouchY = y;
401d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mDragging = true;
402d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    } else if (isInChallengeView(x, y)) {
403d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mBlockDrag = true;
404d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
405d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
406d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
407d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
408d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
409d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mBlockDrag) {
410d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mActivePointerId = INVALID_POINTER;
411d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mDragging = false;
412d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
413d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
414d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mDragging;
415d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
416d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
417d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void resetTouch() {
418d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.recycle();
419d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker = null;
420d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mActivePointerId = INVALID_POINTER;
421d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mDragging = mBlockDrag = false;
422d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
423d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
424d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
425d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean onTouchEvent(MotionEvent ev) {
426d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mVelocityTracker == null) {
427d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mVelocityTracker = VelocityTracker.obtain();
428d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
429d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.addMovement(ev);
430d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
431d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int action = ev.getActionMasked();
432d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        switch (action) {
433d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_CANCEL:
434d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
435d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    showChallenge(0);
436d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
437d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
438d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
439d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
440d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_POINTER_UP:
441d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) {
442d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    break;
443d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
444d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_UP:
445d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
446d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
447d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId));
448d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
449d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
450d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
451d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
452d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_MOVE:
453d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (!mDragging && !mBlockDrag) {
454d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int count = ev.getPointerCount();
455d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    for (int i = 0; i < count; i++) {
456d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        final float x = ev.getX(i);
457d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        final float y = ev.getY(i);
458d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
459022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                        if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mLastTouchY) ||
460d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                                (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
461d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                                && mActivePointerId == INVALID_POINTER) {
462d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mActivePointerId = ev.getPointerId(i);
463d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mDragging = true;
464d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            break;
465d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        }
466d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
467d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
468d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Not an else; this can be set above.
469d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
470d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // No-op if already in this state, but set it here in case we arrived
471d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // at this point from either intercept or the above.
472d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    setScrollState(SCROLL_STATE_DRAGGING);
473d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
474d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int index = ev.findPointerIndex(mActivePointerId);
475d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    if (index < 0) {
476d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        // Oops, bogus state. We lost some touch events somewhere.
477d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        // Just drop it with no velocity and let things settle.
478d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        resetTouch();
479d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        showChallenge(0);
480d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        return true;
481d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
482022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                    final float y = Math.max(ev.getY(index), getChallengeOpenedTop());
483d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float delta = y - mLastTouchY;
484d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int idelta = (int) delta;
485d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // Don't lose the rounded component
486d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mLastTouchY = y + delta - idelta;
487d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
488d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    moveChallengeBy(idelta);
489d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
490d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
491d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
492d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return true;
493d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
494d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
495d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean isInChallengeView(float x, float y) {
496d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return false;
497d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
498d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return x >= mChallengeView.getLeft() && y >= mChallengeView.getTop() &&
499d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                x < mChallengeView.getRight() && y < mChallengeView.getBottom();
500d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
501d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
502d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean isInDragHandle(float x, float y) {
503d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return false;
504d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
5050b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        return x >= mDragHandleEdgeSlop &&
5060b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell                y >= mChallengeView.getTop() &&
5070b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell                x < getWidth() - mDragHandleEdgeSlop &&
508d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                y < mChallengeView.getTop() + mDragHandleSize;
509d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
510d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
511022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    private boolean crossedDragHandle(float x, float y, float lastY) {
512022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int challengeTop = mChallengeView.getTop();
513022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        return x >= 0 && x < getWidth() && lastY < challengeTop &&
514022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                y > challengeTop + mDragHandleSize;
515022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    }
516022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
517d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
518d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected void onMeasure(int widthSpec, int heightSpec) {
519d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
520d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
521d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            throw new IllegalArgumentException(
522d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    "SlidingChallengeLayout must be measured with an exact size");
523d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
524d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
525d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int width = MeasureSpec.getSize(widthSpec);
526d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int height = MeasureSpec.getSize(heightSpec);
527d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setMeasuredDimension(width, height);
528d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
529d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // Find one and only one challenge view.
530d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final View oldChallengeView = mChallengeView;
531d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView = null;
532d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int count = getChildCount();
533d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        for (int i = 0; i < count; i++) {
534d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final View child = getChildAt(i);
535d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
536d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
537eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
538d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mChallengeView != null) {
539d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    throw new IllegalStateException(
540d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            "There may only be one child with layout_isChallenge=\"true\"");
541d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
542d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                mChallengeView = child;
543d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mChallengeView != oldChallengeView) {
544d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
545d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
546eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
547eee209313d564be37b8fc068f785b3844cd6597eAdam Powell                setScrimView(child);
548d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
549d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
550d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (child.getVisibility() == GONE) continue;
551d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
552d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            measureChildWithMargins(child, widthSpec, 0, heightSpec, 0);
553d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
554d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
555d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
556d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
557d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected void onLayout(boolean changed, int l, int t, int r, int b) {
558d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingLeft = getPaddingLeft();
559d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingTop = getPaddingTop();
560d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingRight = getPaddingRight();
561d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingBottom = getPaddingBottom();
562d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int width = r - l;
563d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int height = b - t;
564d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
565d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int count = getChildCount();
566d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        for (int i = 0; i < count; i++) {
567d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final View child = getChildAt(i);
568d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
569d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (child.getVisibility() == GONE) continue;
570d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
571d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
572d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
573eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
574d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Challenge views pin to the bottom, offset by a portion of their height,
575d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // and center horizontally.
576d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int center = (paddingLeft + width - paddingRight) / 2;
577d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int childWidth = child.getMeasuredWidth();
578d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int childHeight = child.getMeasuredHeight();
579d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int left = center - childWidth / 2;
580d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int layoutBottom = height - paddingBottom - lp.bottomMargin;
581d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // We use the top of the challenge view to position the handle, so
582d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // we never want less than the handle size showing at the bottom.
583d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int bottom = layoutBottom + (int) ((childHeight - mDragHandleSize)
584d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        * (1 - mChallengeOffset));
585d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                float offset = 1.f - (bottom - layoutBottom) / childHeight;
586d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.setAlpha(offset);
587d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.layout(left, bottom - childHeight, left + childWidth, bottom);
588d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            } else {
589d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Non-challenge views lay out from the upper left, layered.
590d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.layout(paddingLeft + lp.leftMargin,
591d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingTop + lp.topMargin,
592d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingLeft + child.getMeasuredWidth(),
593d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingTop + child.getMeasuredHeight());
594d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
595d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
596d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
597d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (!mHasLayout) {
598d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // We want to trigger the initial listener updates outside of layout pass,
599d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // in case the listeners trigger requestLayout().
600d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            post(new Runnable() {
601d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                @Override
602d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                public void run() {
603d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    sendInitialListenerUpdates();
604d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
605d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            });
606d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
607d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = true;
608d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
609d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
610d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void computeScroll() {
611d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.computeScroll();
612d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
613d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (!mScroller.isFinished()) {
614d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (mChallengeView == null) {
615d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Can't scroll if the view is missing.
616d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                Log.e(TAG, "Challenge view missing in computeScroll");
617d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                mScroller.abortAnimation();
618d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                return;
619d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
620d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
621d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mScroller.computeScrollOffset();
622d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            moveChallengeTo(mScroller.getCurrY());
623d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
624d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (mScroller.isFinished()) {
625d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                post(mEndScrollRunnable);
626d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
627d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
628d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
629d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
630d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
631d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void draw(Canvas c) {
632d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.draw(c);
63386b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock        if (mChallengeOffset < 1.f
63486b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock                && mChallengeView != null && mHandleDrawable != null && mShowHandle) {
635d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final int top = mChallengeView.getTop();
636d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mHandleDrawable.setBounds(0, top, getWidth(), top + mDragHandleSize);
637d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final float alpha = sHandleFadeInterpolator.getInterpolation(1 - mChallengeOffset);
638d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mHandleDrawable.setAlpha((int) (alpha * 0xFF));
639d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mHandleDrawable.draw(c);
640d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
641d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
642d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
643d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
644d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Move the bottom edge of mChallengeView to a new position and notify the listener
645d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * if it represents a change in position. Changes made through this method will
646d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * be stable across layout passes. If this method is called before first layout of
647d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * this SlidingChallengeLayout it will have no effect.
648d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     *
649d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system.
650d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @return true if the challenge view was moved
651d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
652d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean moveChallengeTo(int bottom) {
653d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null || !mHasLayout) {
654d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return false;
655d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
656d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
657d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
658d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int layoutBottom = getHeight() - getPaddingBottom() - bottomMargin;
659d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int challengeHeight = mChallengeView.getHeight();
660d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
661d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        bottom = Math.max(layoutBottom,
662d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                Math.min(bottom, layoutBottom + challengeHeight - mDragHandleSize));
663d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
664d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        float offset = 1.f - (float) (bottom - layoutBottom) / (challengeHeight - mDragHandleSize);
665d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeOffset = offset;
666d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (offset > 0 && !mChallengeShowing) {
667d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            setChallengeShowing(true);
668d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
669d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
670d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView.layout(mChallengeView.getLeft(),
671d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom);
672d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
673d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView.setAlpha(offset);
6740b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mScrollListener != null) {
6750b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop());
676d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
677d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        postInvalidateOnAnimation();
678d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return true;
679d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
680d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
681d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int getChallengeBottom() {
682d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return 0;
683d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
684d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mChallengeView.getBottom();
685d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
686d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
687022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    private int getChallengeOpenedTop() {
688022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int paddedBottom = getHeight() - getPaddingBottom();
689022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        if (mChallengeView == null) return paddedBottom;
690022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
691022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
692022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int layoutBottom = paddedBottom - bottomMargin;
693022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
694022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        return layoutBottom - mChallengeView.getHeight();
695022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    }
696022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
697d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void moveChallengeBy(int delta) {
698d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        moveChallengeTo(getChallengeBottom() + delta);
699d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
700d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
701d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
702d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Show or hide the challenge view, animating it if necessary.
703d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param show true to show, false to hide
704d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
705d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void showChallenge(boolean show) {
706d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        showChallenge(show, 0);
707d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
708d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
709d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void showChallenge(int velocity) {
710d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        boolean show = false;
711d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (Math.abs(velocity) > mMinVelocity) {
712d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            show = velocity < 0;
713d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        } else {
714d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            show = mChallengeOffset >= 0.5f;
715d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
716d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        showChallenge(show, velocity);
717d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
718d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
719d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void showChallenge(boolean show, int velocity) {
720d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) {
721d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            setChallengeShowing(false);
722d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
723d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
724d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
725d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mHasLayout) {
726d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
727d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final int layoutBottom = getHeight() - getPaddingBottom() - bottomMargin;
728d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            animateChallengeTo(show ? layoutBottom :
729d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                layoutBottom + mChallengeView.getHeight() - mDragHandleSize, velocity);
730d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
731d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
732d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
73386b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock    public void showHandle(boolean show) {
73486b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock        mShowHandle = show;
73586b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock        invalidate();
73686b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock    }
73786b6357e5eb91950eac7de7ffe29e5a4ad32903bJohn Spurlock
73819a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    @Override
739d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
740d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return new LayoutParams(getContext(), attrs);
741d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
742d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
743d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
744d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
745d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
746d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
747d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                new LayoutParams(p);
748d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
749d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
750d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
751d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
752d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return new LayoutParams();
753d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
754d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
755d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
756d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
757d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return p instanceof LayoutParams;
758d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
759d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
760d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static class LayoutParams extends MarginLayoutParams {
761eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public int childType = CHILD_TYPE_NONE;
762eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_NONE = 0;
763eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_CHALLENGE = 2;
764eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_SCRIM = 4;
765d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
766d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams() {
767d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            this(MATCH_PARENT, WRAP_CONTENT);
768d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
769d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
770d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(int width, int height) {
771d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(width, height);
772d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
773d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
774d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(android.view.ViewGroup.LayoutParams source) {
775d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
776d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
777d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
778d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(MarginLayoutParams source) {
779d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
780d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
781d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
782d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(LayoutParams source) {
783d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
784d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
785eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            childType = source.childType;
786d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
787d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
788d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(Context c, AttributeSet attrs) {
789d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(c, attrs);
790d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
791d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final TypedArray a = c.obtainStyledAttributes(attrs,
792d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    R.styleable.SlidingChallengeLayout_Layout);
793eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            childType = a.getInt(R.styleable.SlidingChallengeLayout_Layout_layout_childType,
794eee209313d564be37b8fc068f785b3844cd6597eAdam Powell                    CHILD_TYPE_NONE);
795d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            a.recycle();
796d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
797d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
798d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller}
799