SlidingChallengeLayout.java revision 03ad0c09502c72c02af464f1e273af3b6ff799cc
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
199c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powellimport com.android.internal.R;
209c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell
21cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powellimport android.animation.Animator;
22cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powellimport android.animation.AnimatorListenerAdapter;
236f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.animation.ObjectAnimator;
24d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.content.Context;
25fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powellimport android.content.res.Resources;
26d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.content.res.TypedArray;
27d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.graphics.Canvas;
281bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandlerimport android.graphics.Paint;
29d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.AttributeSet;
305da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powellimport android.util.DisplayMetrics;
316f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.util.FloatProperty;
32d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.Log;
336f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.util.Property;
34d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.MotionEvent;
35d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.VelocityTracker;
36d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.View;
37d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewConfiguration;
38d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewGroup;
3974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganovimport android.view.accessibility.AccessibilityManager;
40d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.animation.Interpolator;
41d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.widget.Scroller;
42d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
43d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller/**
44d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * This layout handles interaction with the sliding security challenge views
45d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * that overlay/resize other keyguard contents.
46d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */
4719a5267003e7dc70100a4bd4f1f449523b2ff38bJim Millerpublic class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout {
48d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final String TAG = "SlidingChallengeLayout";
491bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler    private static final boolean DEBUG = false;
50d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
51c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    // The drag handle is measured in dp above & below the top edge of the
52c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    // challenge view; these parameters change based on whether the challenge
53c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    // is open or closed.
549c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell    private static final int DRAG_HANDLE_CLOSED_ABOVE = 8; // dp
55cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell    private static final int DRAG_HANDLE_CLOSED_BELOW = 0; // dp
56c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private static final int DRAG_HANDLE_OPEN_ABOVE = 8; // dp
57c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private static final int DRAG_HANDLE_OPEN_BELOW = 0; // dp
58c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
59c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private static final int HANDLE_ANIMATE_DURATION = 200; // ms
60c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
61d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Drawn to show the drag handle in closed state; crossfades to the challenge view
62d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // when challenge is fully visible
63cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    private boolean mEdgeCaptured;
64d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
655da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell    private DisplayMetrics mDisplayMetrics;
665da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell
67d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Initialized during measurement from child layoutparams
6874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    private View mExpandChallengeView;
69052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren    private KeyguardSecurityContainer mChallengeView;
70eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private View mScrimView;
71cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    private View mWidgetsView;
72d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
73d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Range: 0 (fully hidden) to 1 (fully visible)
74d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private float mChallengeOffset = 1.f;
75d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mChallengeShowing = true;
7603ad0c09502c72c02af464f1e273af3b6ff799ccChris Wren    private boolean mWasChallengeShowing = true;
77eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private boolean mIsBouncing = false;
78d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
79d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private final Scroller mScroller;
80db1c5d596e91713132131601536aaad32e693325Adam Cohen    private ObjectAnimator mFader;
81d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mScrollState;
820b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private OnChallengeScrolledListener mScrollListener;
830b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private OnBouncerStateChangedListener mBouncerListener;
84d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
85d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_IDLE = 0;
86d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_DRAGGING = 1;
87d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_SETTLING = 2;
88db1c5d596e91713132131601536aaad32e693325Adam Cohen    public static final int SCROLL_STATE_FADING = 3;
89db1c5d596e91713132131601536aaad32e693325Adam Cohen
908caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    private static final int CHALLENGE_FADE_OUT_DURATION = 100;
918caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    private static final int CHALLENGE_FADE_IN_DURATION = 160;
92d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
93d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int MAX_SETTLE_DURATION = 600; // ms
94d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
95d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // ID of the pointer in charge of a current drag
96d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mActivePointerId = INVALID_POINTER;
97d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int INVALID_POINTER = -1;
98d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
99d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if the user is currently dragging the slider
100d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mDragging;
101d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if the user may not drag until a new gesture begins
102d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mBlockDrag;
103d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
104d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private VelocityTracker mVelocityTracker;
105d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mMinVelocity;
106d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mMaxVelocity;
107c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private float mGestureStartX, mGestureStartY; // where did you first touch the screen?
108b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    private int mGestureStartChallengeBottom; // where was the challenge at that time?
109c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
110c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int mDragHandleClosedBelow; // handle hitrect extension into the challenge view
111c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int mDragHandleClosedAbove; // extend the handle's hitrect this far above the line
112c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int mDragHandleOpenBelow; // handle hitrect extension into the challenge view
113c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int mDragHandleOpenAbove; // extend the handle's hitrect this far above the line
114c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
1150b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private int mDragHandleEdgeSlop;
11667625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell    private int mChallengeBottomBound; // Number of pixels from the top of the challenge view
11767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                                       // that should remain on-screen
118c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
119c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int mTouchSlop;
12074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    private int mTouchSlopSquare;
121c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
1226f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    float mHandleAlpha;
1236f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    float mFrameAlpha;
124cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    float mFrameAnimationTarget = Float.MIN_VALUE;
1256f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    private ObjectAnimator mHandleAnimation;
1266f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    private ObjectAnimator mFrameAnimation;
1276f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
128cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    private boolean mHasGlowpad;
129db1c5d596e91713132131601536aaad32e693325Adam Cohen
130db1c5d596e91713132131601536aaad32e693325Adam Cohen    // We have an internal and external version, and we and them together.
131db1c5d596e91713132131601536aaad32e693325Adam Cohen    private boolean mChallengeInteractiveExternal = true;
132db1c5d596e91713132131601536aaad32e693325Adam Cohen    private boolean mChallengeInteractiveInternal = true;
133cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
1346f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA =
1356f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            new FloatProperty<SlidingChallengeLayout>("handleAlpha") {
1366f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        @Override
1376f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        public void setValue(SlidingChallengeLayout view, float value) {
1386f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            view.mHandleAlpha = value;
1396f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            view.invalidate();
1406f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
1416f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
1426f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        @Override
1436f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        public Float get(SlidingChallengeLayout view) {
1446f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            return view.mHandleAlpha;
1456f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
1466f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    };
1476f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
148d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if at least one layout pass has happened since the view was attached.
149d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mHasLayout;
150d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
151d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final Interpolator sMotionInterpolator = new Interpolator() {
152d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public float getInterpolation(float t) {
153d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            t -= 1.0f;
154d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return t * t * t * t * t + 1.0f;
155d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
156d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
157d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
158d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final Interpolator sHandleFadeInterpolator = new Interpolator() {
159d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public float getInterpolation(float t) {
160d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return t * t;
161d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
162d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
163d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
164d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private final Runnable mEndScrollRunnable = new Runnable () {
165d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void run() {
166d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            completeChallengeScroll();
167d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
168d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
169d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
170eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private final OnClickListener mScrimClickListener = new OnClickListener() {
171eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        @Override
172eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public void onClick(View v) {
173eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            hideBouncer();
174eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
175eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    };
176eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
17774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    private final OnClickListener mExpandChallengeClickListener = new OnClickListener() {
17874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        @Override
17974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        public void onClick(View v) {
18078a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock            if (!isChallengeShowing()) {
18174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                showChallenge(true);
18274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            }
18374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        }
18474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    };
18574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov
186d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
187d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Listener interface that reports changes in scroll state of the challenge area.
188d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
189d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public interface OnChallengeScrolledListener {
190d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        /**
191d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * The scroll state itself changed.
192d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
193d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>scrollState will be one of the following:</p>
194d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
195d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <ul>
196d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li>
197d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging
198d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * the challenge area.</li>
199d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating
200d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * into place.</li>
201d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * </ul>
202d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
203d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>Do not perform expensive operations (e.g. layout)
204d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p>
205d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
206d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param scrollState The new scroll state of the challenge area.
207d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         */
208d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void onScrollStateChanged(int scrollState);
209d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
210d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        /**
211d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * The precise position of the challenge area has changed.
212d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
213d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>NOTE: It is NOT safe to modify layout or call any View methods that may
214d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * result in a requestLayout anywhere in your view hierarchy as a result of this call.
215d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * It may be called during drawing.</p>
216d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
217d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param scrollPosition New relative position of the challenge area.
218d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                       1.f = fully visible/ready to be interacted with.
219d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                       0.f = fully invisible/inaccessible to the user.
220d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param challengeTop Position of the top edge of the challenge view in px in the
221d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                     SlidingChallengeLayout's coordinate system.
222d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         */
223d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void onScrollPositionChanged(float scrollPosition, int challengeTop);
224d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
225d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
226d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context) {
227d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        this(context, null);
228d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
229d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
230d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context, AttributeSet attrs) {
231d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        this(context, attrs, 0);
232d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
233d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
234d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) {
235d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super(context, attrs, defStyle);
236d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
237d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mScroller = new Scroller(context, sMotionInterpolator);
238d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
239d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final ViewConfiguration vc = ViewConfiguration.get(context);
240d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mMinVelocity = vc.getScaledMinimumFlingVelocity();
241d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mMaxVelocity = vc.getScaledMaximumFlingVelocity();
242d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
243fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        final Resources res = getResources();
244fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        mDragHandleEdgeSlop = res.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size);
2450b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
246c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
24774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        mTouchSlopSquare = mTouchSlop * mTouchSlop;
248c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
2495da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell        mDisplayMetrics = res.getDisplayMetrics();
2505da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell        final float density = mDisplayMetrics.density;
2511bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
2521bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        // top half of the lock icon, plus another 25% to be sure
253c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        mDragHandleClosedAbove = (int) (DRAG_HANDLE_CLOSED_ABOVE * density + 0.5f);
254c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        mDragHandleClosedBelow = (int) (DRAG_HANDLE_CLOSED_BELOW * density + 0.5f);
255c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        mDragHandleOpenAbove = (int) (DRAG_HANDLE_OPEN_ABOVE * density + 0.5f);
256c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        mDragHandleOpenBelow = (int) (DRAG_HANDLE_OPEN_BELOW * density + 0.5f);
257c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
258c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        // how much space to account for in the handle when closed
259fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        mChallengeBottomBound = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding);
2601bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
261cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell        setWillNotDraw(false);
262cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell    }
263cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell
2646e36f1019cb6d7098b49e036e1640c8cfc6880cfJohn Spurlock    public void setHandleAlpha(float alpha) {
2656e36f1019cb6d7098b49e036e1640c8cfc6880cfJohn Spurlock        if (mExpandChallengeView != null) {
2666e36f1019cb6d7098b49e036e1640c8cfc6880cfJohn Spurlock            mExpandChallengeView.setAlpha(alpha);
2676f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
2686f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    }
2696f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
270bb5c941149b66c0192736468bb60f47984dd5e1fJohn Spurlock    public void setChallengeInteractive(boolean interactive) {
271db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeInteractiveExternal = interactive;
27278a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock        if (mExpandChallengeView != null) {
27378a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock            mExpandChallengeView.setEnabled(interactive);
27478a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock        }
275bb5c941149b66c0192736468bb60f47984dd5e1fJohn Spurlock    }
276bb5c941149b66c0192736468bb60f47984dd5e1fJohn Spurlock
2776f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    void animateHandle(boolean visible) {
2786f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        if (mHandleAnimation != null) {
2796f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mHandleAnimation.cancel();
28067625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell            mHandleAnimation = null;
281d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
28267625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        final float targetAlpha = visible ? 1.f : 0.f;
28367625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        if (targetAlpha == mHandleAlpha) {
28467625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell            return;
28567625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        }
28667625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, targetAlpha);
2876f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleAnimation.setInterpolator(sHandleFadeInterpolator);
2886f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleAnimation.setDuration(HANDLE_ANIMATE_DURATION);
2896f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleAnimation.start();
2906f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    }
2916f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
292d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void sendInitialListenerUpdates() {
2930b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mScrollListener != null) {
294d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0;
2950b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollPositionChanged(mChallengeOffset, challengeTop);
2960b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollStateChanged(mScrollState);
297d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
298d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
299d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
300d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) {
3010b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        mScrollListener = listener;
302d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mHasLayout) {
303d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            sendInitialListenerUpdates();
304d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
305d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
306d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
3070b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
3080b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        mBouncerListener = listener;
3090b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    }
3100b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
311d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
312d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void onAttachedToWindow() {
313d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.onAttachedToWindow();
314d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
315d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = false;
316d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
317d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
318d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
319d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void onDetachedFromWindow() {
320d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.onDetachedFromWindow();
321d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
322d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        removeCallbacks(mEndScrollRunnable);
323d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = false;
324d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
325d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
3260b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    @Override
3270b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    public void requestChildFocus(View child, View focused) {
3280b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mIsBouncing && child != mChallengeView) {
3290b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            // Clear out of the bouncer if the user tries to move focus outside of
3300b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            // the security challenge view.
3310b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            hideBouncer();
3320b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
3330b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        super.requestChildFocus(child, focused);
3340b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    }
3350b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
336d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // We want the duration of the page snap animation to be influenced by the distance that
337d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // the screen has to travel, however, we don't want this duration to be effected in a
338d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
339d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // of travel has on the overall snap duration.
340d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    float distanceInfluenceForSnapDuration(float f) {
341d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        f -= 0.5f; // center the values about 0.
342d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        f *= 0.3f * Math.PI / 2.0f;
343d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return (float) Math.sin(f);
344d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
345d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
346d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void setScrollState(int state) {
347d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mScrollState != state) {
348d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mScrollState = state;
349d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
3506f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing);
3510b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            if (mScrollListener != null) {
3520b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell                mScrollListener.onScrollStateChanged(state);
353d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
354d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
355d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
356d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
357d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void completeChallengeScroll() {
358d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setChallengeShowing(mChallengeOffset != 0);
359d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setScrollState(SCROLL_STATE_IDLE);
360db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeInteractiveInternal = true;
361cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        mChallengeView.setLayerType(LAYER_TYPE_NONE, null);
362d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
363d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
364eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    void setScrimView(View scrim) {
365eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
366eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            mScrimView.setOnClickListener(null);
367eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
368eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView = scrim;
369eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
370eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setFocusable(true);
371eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setOnClickListener(mScrimClickListener);
372eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
373eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
374d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
375d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Animate the bottom edge of the challenge view to the given position.
376d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     *
377d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param y desired final position for the bottom edge of the challenge view in px
378d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param velocity velocity in
379d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
380d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void animateChallengeTo(int y, int velocity) {
381d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) {
382d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // Nothing to do.
383d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
384d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
385db1c5d596e91713132131601536aaad32e693325Adam Cohen
3868caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        cancelTransitionsInProgress();
387db1c5d596e91713132131601536aaad32e693325Adam Cohen
388db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeInteractiveInternal = false;
389db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
39067625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        final int sy = mChallengeView.getBottom();
39167625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        final int dy = y - sy;
392d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (dy == 0) {
393d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            completeChallengeScroll();
394d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
395d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
396d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
397d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setScrollState(SCROLL_STATE_SETTLING);
398d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
399d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int childHeight = mChallengeView.getHeight();
400d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int halfHeight = childHeight / 2;
401d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight);
402d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final float distance = halfHeight + halfHeight *
403d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                distanceInfluenceForSnapDuration(distanceRatio);
404d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
405d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int duration = 0;
406d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        velocity = Math.abs(velocity);
407d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (velocity > 0) {
408d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
409d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        } else {
410d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final float childDelta = (float) Math.abs(dy) / childHeight;
411d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            duration = (int) ((childDelta + 1) * 100);
412d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
413d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        duration = Math.min(duration, MAX_SETTLE_DURATION);
414d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
415d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mScroller.startScroll(0, sy, 0, dy, duration);
416d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        postInvalidateOnAnimation();
417d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
418d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
419d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void setChallengeShowing(boolean showChallenge) {
42074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        if (mChallengeShowing == showChallenge) {
42174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            return;
42274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        }
42367625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        mChallengeShowing = showChallenge;
4245a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell
4255a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell        if (mExpandChallengeView == null || mChallengeView == null) {
4265a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell            // These might not be here yet if we haven't been through layout.
4275a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell            // If we haven't, the first layout pass will set everything up correctly
4285a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell            // based on mChallengeShowing as set above.
4295a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell            return;
4305a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell        }
4315a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell
43274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        if (mChallengeShowing) {
43374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            mExpandChallengeView.setVisibility(View.INVISIBLE);
43474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            mChallengeView.setVisibility(View.VISIBLE);
43574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
43674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mChallengeView.requestAccessibilityFocus();
43774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mChallengeView.announceForAccessibility(mContext.getString(
43874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                        R.string.keyguard_accessibility_unlock_area_expanded));
43974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            }
44074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        } else {
44174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            mExpandChallengeView.setVisibility(View.VISIBLE);
44274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            mChallengeView.setVisibility(View.INVISIBLE);
44374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
44474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mExpandChallengeView.requestAccessibilityFocus();
44574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mChallengeView.announceForAccessibility(mContext.getString(
44674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                        R.string.keyguard_accessibility_unlock_area_collapsed));
44774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            }
44874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        }
449d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
450d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
451d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
452d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @return true if the challenge is at all visible.
453d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
454d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean isChallengeShowing() {
455d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mChallengeShowing;
456d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
457d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
458d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
45919a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    public boolean isChallengeOverlapping() {
46019a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller        return mChallengeShowing;
46119a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    }
46219a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller
46319a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    @Override
464eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public boolean isBouncing() {
465eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        return mIsBouncing;
466eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
467eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
468eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
469eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public void showBouncer() {
470eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mIsBouncing) return;
47103ad0c09502c72c02af464f1e273af3b6ff799ccChris Wren        mWasChallengeShowing = mChallengeShowing;
47248275d2de99ff2556031929c48f295d3f93447b6Winson Chung        showChallenge(true);
47303ad0c09502c72c02af464f1e273af3b6ff799ccChris Wren        mIsBouncing = true;
474eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
4756f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mScrimView.setVisibility(VISIBLE);
476eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
477052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren        if (mChallengeView != null) {
478052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren            mChallengeView.showBouncer(HANDLE_ANIMATE_DURATION);
479052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren        }
480cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        // Mess with padding/margin to inset the bouncer frame.
481cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        // We have more space available to us otherwise.
482cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (mChallengeView != null) {
483cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams();
484cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            lp.leftMargin = lp.rightMargin = getChallengeMargin(false);
485cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            mChallengeView.setLayoutParams(lp);
486cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
487cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
4880b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mBouncerListener != null) {
4890b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mBouncerListener.onBouncerStateChanged(true);
4900b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
491eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
492eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
493eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
494eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public void hideBouncer() {
495eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (!mIsBouncing) return;
49603ad0c09502c72c02af464f1e273af3b6ff799ccChris Wren        if (!mWasChallengeShowing) showChallenge(false);
497eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mIsBouncing = false;
498eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
499eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            mScrimView.setVisibility(GONE);
500eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
501052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren        if (mChallengeView != null) {
502052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren            mChallengeView.hideBouncer(HANDLE_ANIMATE_DURATION);
503052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren        }
5040b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mBouncerListener != null) {
5050b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mBouncerListener.onBouncerStateChanged(false);
5060b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
507eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
508eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
509cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    private int getChallengeMargin(boolean expanded) {
510cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        return expanded && mHasGlowpad ? 0 : mDragHandleEdgeSlop;
511cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    }
512cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
513fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell    private float getChallengeAlpha() {
514fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        float x = mChallengeOffset - 1;
515fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        return x * x * x + 1.f;
516fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell    }
517fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell
518eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
519d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void requestDisallowInterceptTouchEvent(boolean allowIntercept) {
520d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
521d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // If there are one or more pointers in the challenge view before we take over
522d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // touch events, onInterceptTouchEvent will set mBlockDrag.
523d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
524d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
525d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
526d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean onInterceptTouchEvent(MotionEvent ev) {
527d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mVelocityTracker == null) {
528d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mVelocityTracker = VelocityTracker.obtain();
529d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
530d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.addMovement(ev);
531d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
532d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int action = ev.getActionMasked();
533d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        switch (action) {
534022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller            case MotionEvent.ACTION_DOWN:
535c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                mGestureStartX = ev.getX();
536b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                mGestureStartY = ev.getY();
537995270f01175234d0615a0e1900576f142a3b4adAdam Powell                mBlockDrag = false;
538022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                break;
539022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
540d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_CANCEL:
541d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_UP:
542d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
543d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
544d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
545d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_MOVE:
546d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int count = ev.getPointerCount();
547d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                for (int i = 0; i < count; i++) {
548d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float x = ev.getX(i);
549d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float y = ev.getY(i);
550db1c5d596e91713132131601536aaad32e693325Adam Cohen                    if (!mIsBouncing && mActivePointerId == INVALID_POINTER
5519c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell                                && (crossedDragHandle(x, y, mGestureStartY)
552c238af5112bee38b3143a65849bd41785c3744b0Adam Powell                                || (isInChallengeView(x, y) &&
5539c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell                                        mScrollState == SCROLL_STATE_SETTLING))) {
554d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mActivePointerId = ev.getPointerId(i);
555c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                        mGestureStartX = x;
556c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                        mGestureStartY = y;
557b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                        mGestureStartChallengeBottom = getChallengeBottom();
558d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mDragging = true;
559cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                        mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
5609c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell                    } else if (mChallengeShowing && isInChallengeView(x, y)) {
561d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mBlockDrag = true;
562d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
563d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
564d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
565d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
566d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
567db1c5d596e91713132131601536aaad32e693325Adam Cohen        if (mBlockDrag || isChallengeInteractionBlocked()) {
568d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mActivePointerId = INVALID_POINTER;
569d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mDragging = false;
570d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
571d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
572d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mDragging;
573d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
574d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
575db1c5d596e91713132131601536aaad32e693325Adam Cohen    private boolean isChallengeInteractionBlocked() {
576db1c5d596e91713132131601536aaad32e693325Adam Cohen        return !mChallengeInteractiveExternal || !mChallengeInteractiveInternal;
577db1c5d596e91713132131601536aaad32e693325Adam Cohen    }
578db1c5d596e91713132131601536aaad32e693325Adam Cohen
579d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void resetTouch() {
580d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.recycle();
581d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker = null;
582d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mActivePointerId = INVALID_POINTER;
583d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mDragging = mBlockDrag = false;
584d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
585d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
586d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
587d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean onTouchEvent(MotionEvent ev) {
588d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mVelocityTracker == null) {
589d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mVelocityTracker = VelocityTracker.obtain();
590d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
591d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.addMovement(ev);
592d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
593d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int action = ev.getActionMasked();
594d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        switch (action) {
595995270f01175234d0615a0e1900576f142a3b4adAdam Powell            case MotionEvent.ACTION_DOWN:
596995270f01175234d0615a0e1900576f142a3b4adAdam Powell                mBlockDrag = false;
597c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                mGestureStartX = ev.getX();
598995270f01175234d0615a0e1900576f142a3b4adAdam Powell                mGestureStartY = ev.getY();
599995270f01175234d0615a0e1900576f142a3b4adAdam Powell                break;
600995270f01175234d0615a0e1900576f142a3b4adAdam Powell
601d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_CANCEL:
602db1c5d596e91713132131601536aaad32e693325Adam Cohen                if (mDragging && !isChallengeInteractionBlocked()) {
603d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    showChallenge(0);
604d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
605d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
606d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
607d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
608d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_POINTER_UP:
609d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) {
610d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    break;
611d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
612d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_UP:
613db1c5d596e91713132131601536aaad32e693325Adam Cohen                if (mDragging && !isChallengeInteractionBlocked()) {
614d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
615d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId));
616d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
617d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
618d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
619d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
620d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_MOVE:
6216f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                if (!mDragging && !mBlockDrag && !mIsBouncing) {
622d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int count = ev.getPointerCount();
623d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    for (int i = 0; i < count; i++) {
624d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        final float x = ev.getX(i);
625d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        final float y = ev.getY(i);
626d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
627b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                        if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
628d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                                (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
6291ec464679f731827f626856084f19643fff64a94John Spurlock                                && mActivePointerId == INVALID_POINTER
630db1c5d596e91713132131601536aaad32e693325Adam Cohen                                && !isChallengeInteractionBlocked()) {
631c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                            mGestureStartX = x;
632b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                            mGestureStartY = y;
633d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mActivePointerId = ev.getPointerId(i);
634b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                            mGestureStartChallengeBottom = getChallengeBottom();
635d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mDragging = true;
636cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                            mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
637d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            break;
638d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        }
639d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
640d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
641d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Not an else; this can be set above.
642d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
643d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // No-op if already in this state, but set it here in case we arrived
644d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // at this point from either intercept or the above.
645d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    setScrollState(SCROLL_STATE_DRAGGING);
646d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
647d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int index = ev.findPointerIndex(mActivePointerId);
648d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    if (index < 0) {
649d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        // Oops, bogus state. We lost some touch events somewhere.
650d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        // Just drop it with no velocity and let things settle.
651d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        resetTouch();
652d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        showChallenge(0);
653d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        return true;
654d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
655b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                    final float y = ev.getY(index);
656b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                    final float pos = Math.min(y - mGestureStartY,
65767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                            getLayoutBottom() - mChallengeBottomBound);
658b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler
659b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                    moveChallengeTo(mGestureStartChallengeBottom + (int) pos);
660d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
661d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
662d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
663d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return true;
664d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
665d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
666b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    /**
667cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     * The lifecycle of touch events is subtle and it's very easy to do something
668cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     * that will cause bugs that will be nasty to track when overriding this method.
669cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     * Normally one should always override onInterceptTouchEvent instead.
670cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     *
671cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     * To put it another way, don't try this at home.
672cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     */
673cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    @Override
674cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    public boolean dispatchTouchEvent(MotionEvent ev) {
675cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        final int action = ev.getActionMasked();
676cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        boolean handled = false;
677cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (action == MotionEvent.ACTION_DOWN) {
678cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // Defensive programming: if we didn't get the UP or CANCEL, reset anyway.
679cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            mEdgeCaptured = false;
680cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
681cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (mWidgetsView != null && !mIsBouncing && (mEdgeCaptured || isEdgeSwipeBeginEvent(ev))) {
682cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // Normally we would need to do a lot of extra stuff here.
683cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // We can only get away with this because we haven't padded in
684cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // the widget pager or otherwise transformed it during layout.
685cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // We also don't support things like splitting MotionEvents.
686cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
687cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // We set handled to captured even if dispatch is returning false here so that
688cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // we don't send a different view a busted or incomplete event stream.
689cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            handled = mEdgeCaptured |= mWidgetsView.dispatchTouchEvent(ev);
690cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
691cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
692cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (!handled && !mEdgeCaptured) {
693cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            handled = super.dispatchTouchEvent(ev);
694cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
695cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
696cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
697cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            mEdgeCaptured = false;
698cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
699cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
700cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        return handled;
701cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    }
702cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
703cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    private boolean isEdgeSwipeBeginEvent(MotionEvent ev) {
704cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
705cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            return false;
706cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
707cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
708cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        final float x = ev.getX();
709cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        return x < mDragHandleEdgeSlop || x >= getWidth() - mDragHandleEdgeSlop;
710cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    }
711cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
712cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    /**
713b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     * We only want to add additional vertical space to the drag handle when the panel is fully
714b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     * closed.
715b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     */
716c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int getDragHandleSizeAbove() {
717c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        return isChallengeShowing() ? mDragHandleOpenAbove : mDragHandleClosedAbove;
718c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    }
719c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int getDragHandleSizeBelow() {
720c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        return isChallengeShowing() ? mDragHandleOpenBelow : mDragHandleClosedBelow;
721b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    }
722b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler
723d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean isInChallengeView(float x, float y) {
72474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        return isPointInView(x, y, mChallengeView);
725d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
726d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
727d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean isInDragHandle(float x, float y) {
72874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        return isPointInView(x, y, mExpandChallengeView);
72974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    }
730d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
73174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    private boolean isPointInView(float x, float y, View view) {
73274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        if (view == null) {
73374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            return false;
73474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        }
73574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        return x >= view.getLeft() && y >= view.getTop()
73674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                && x < view.getRight() && y < view.getBottom();
737d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
738d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
739b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    private boolean crossedDragHandle(float x, float y, float initialY) {
7409c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell
741022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int challengeTop = mChallengeView.getTop();
7429c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        final boolean horizOk = x >= 0 && x < getWidth();
7439c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell
7449c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        final boolean vertOk;
7459c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        if (mChallengeShowing) {
7469c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell            vertOk = initialY < (challengeTop - getDragHandleSizeAbove()) &&
7479c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell                    y > challengeTop + getDragHandleSizeBelow();
7489c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        } else {
7499c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell            vertOk = initialY > challengeTop + getDragHandleSizeBelow() &&
7509c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell                    y < challengeTop - getDragHandleSizeAbove();
7519c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        }
7529c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        return horizOk && vertOk;
753022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    }
754022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
755d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
756d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected void onMeasure(int widthSpec, int heightSpec) {
757d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
758d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
759d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            throw new IllegalArgumentException(
760d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    "SlidingChallengeLayout must be measured with an exact size");
761d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
762d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
763d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int width = MeasureSpec.getSize(widthSpec);
764d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int height = MeasureSpec.getSize(heightSpec);
765d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setMeasuredDimension(width, height);
766d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
767d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // Find one and only one challenge view.
768d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final View oldChallengeView = mChallengeView;
76974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        final View oldExpandChallengeView = mChallengeView;
770d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView = null;
77174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        mExpandChallengeView = null;
772d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int count = getChildCount();
7738a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen
7748a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        // First iteration through the children finds special children and sets any associated
7758a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        // state.
776d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        for (int i = 0; i < count; i++) {
777d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final View child = getChildAt(i);
778d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
779eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
780d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mChallengeView != null) {
781d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    throw new IllegalStateException(
782d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            "There may only be one child with layout_isChallenge=\"true\"");
783d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
784052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren                if (!(child instanceof KeyguardSecurityContainer)) {
785052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren                            throw new IllegalArgumentException(
786052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren                                    "Challenge must be a KeyguardSecurityContainer");
787052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren                }
788052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren                mChallengeView = (KeyguardSecurityContainer) child;
789d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mChallengeView != oldChallengeView) {
790d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
791d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
7926f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                // We're going to play silly games with the frame's background drawable later.
793cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                if (!mHasLayout) {
794cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    // Set up the margin correctly based on our content for the first run.
795cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    mHasGlowpad = child.findViewById(R.id.keyguard_selector_view) != null;
796cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    lp.leftMargin = lp.rightMargin = getChallengeMargin(true);
797cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                }
79874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) {
79974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                if (mExpandChallengeView != null) {
80074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                    throw new IllegalStateException(
80174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                            "There may only be one child with layout_childType"
80274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                            + "=\"expandChallengeHandle\"");
80374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                }
80474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mExpandChallengeView = child;
80574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                if (mExpandChallengeView != oldExpandChallengeView) {
80674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                    mExpandChallengeView.setVisibility(mChallengeShowing ? INVISIBLE : VISIBLE);
80774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                    mExpandChallengeView.setOnClickListener(mExpandChallengeClickListener);
80874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                }
809eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
810eee209313d564be37b8fc068f785b3844cd6597eAdam Powell                setScrimView(child);
811cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            } else if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) {
812cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                mWidgetsView = child;
813d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
8148a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        }
8158a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen
8165be14ded6762bc51fabbbecb1b2ec01a8c758631Adam Cohen        // We want to measure the challenge view first, since the KeyguardWidgetPager
8175be14ded6762bc51fabbbecb1b2ec01a8c758631Adam Cohen        // needs to do things its measure pass that are dependent on the challenge view
8185be14ded6762bc51fabbbecb1b2ec01a8c758631Adam Cohen        // having been measured.
81974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        if (mChallengeView != null && mChallengeView.getVisibility() != View.GONE) {
8208a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen            measureChildWithMargins(mChallengeView, widthSpec, 0, heightSpec, 0);
8218a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        }
822d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
8238a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        // Measure the rest of the children
8248a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        for (int i = 0; i < count; i++) {
8258a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen            final View child = getChildAt(i);
82674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            if (child.getVisibility() == GONE) {
82774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                continue;
82874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            }
8298a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen            // Don't measure the challenge view twice!
8305da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell            if (child == mChallengeView) continue;
8315da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell
8325da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell            // Measure children. Widget frame measures special, so that we can ignore
8335da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell            // insets for the IME.
8345da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell            int parentWidthSpec = widthSpec, parentHeightSpec = heightSpec;
8355da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
8365da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell            if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) {
8375da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                final View root = getRootView();
8385da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                if (root != null) {
8395da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                    // This calculation is super dodgy and relies on several assumptions.
8405da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                    // Specifically that the root of the window will be padded in for insets
8415da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                    // and that the window is LAYOUT_IN_SCREEN.
8425da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                    final int windowWidth = mDisplayMetrics.widthPixels;
8435da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                    final int windowHeight = mDisplayMetrics.heightPixels - root.getPaddingTop();
8445da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                    parentWidthSpec = MeasureSpec.makeMeasureSpec(
8455da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                            windowWidth, MeasureSpec.EXACTLY);
8465da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                    parentHeightSpec = MeasureSpec.makeMeasureSpec(
8475da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                            windowHeight, MeasureSpec.EXACTLY);
8485da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell                }
8498a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen            }
8505da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell            measureChildWithMargins(child, parentWidthSpec, 0, parentHeightSpec, 0);
851d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
852d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
853d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
854d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
855d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected void onLayout(boolean changed, int l, int t, int r, int b) {
856d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingLeft = getPaddingLeft();
857d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingTop = getPaddingTop();
858d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingRight = getPaddingRight();
859d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingBottom = getPaddingBottom();
860d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int width = r - l;
861d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int height = b - t;
862d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
863d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int count = getChildCount();
864d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        for (int i = 0; i < count; i++) {
865d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final View child = getChildAt(i);
866d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
867d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (child.getVisibility() == GONE) continue;
868d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
869d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
870d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
871eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
872d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Challenge views pin to the bottom, offset by a portion of their height,
873d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // and center horizontally.
874d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int center = (paddingLeft + width - paddingRight) / 2;
875d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int childWidth = child.getMeasuredWidth();
876d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int childHeight = child.getMeasuredHeight();
877d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int left = center - childWidth / 2;
878d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int layoutBottom = height - paddingBottom - lp.bottomMargin;
879d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // We use the top of the challenge view to position the handle, so
880d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // we never want less than the handle size showing at the bottom.
88167625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                final int bottom = layoutBottom + (int) ((childHeight - mChallengeBottomBound)
882d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        * (1 - mChallengeOffset));
883fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell                child.setAlpha(getChallengeAlpha());
884d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.layout(left, bottom - childHeight, left + childWidth, bottom);
88574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) {
8860f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                final int center = (paddingLeft + width - paddingRight) / 2;
8870f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                final int left = center - child.getMeasuredWidth() / 2;
8880f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                final int right = left + child.getMeasuredWidth();
8890f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                final int bottom = height - paddingBottom - lp.bottomMargin;
8900f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                final int top = bottom - child.getMeasuredHeight();
8910f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                child.layout(left, top, right, bottom);
892d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            } else {
893d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Non-challenge views lay out from the upper left, layered.
894d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.layout(paddingLeft + lp.leftMargin,
895d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingTop + lp.topMargin,
896d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingLeft + child.getMeasuredWidth(),
897d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingTop + child.getMeasuredHeight());
898d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
899d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
900d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
901d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (!mHasLayout) {
9026f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mHasLayout = true;
903d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
904d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
905d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
906d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
907d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void draw(Canvas c) {
908d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.draw(c);
9091bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        if (DEBUG) {
91074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            final Paint debugPaint = new Paint();
9111bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            debugPaint.setColor(0x40FF00CC);
9121bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            // show the isInDragHandle() rect
9131bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            c.drawRect(mDragHandleEdgeSlop,
914c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                    mChallengeView.getTop() - getDragHandleSizeAbove(),
9151bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    getWidth() - mDragHandleEdgeSlop,
916c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                    mChallengeView.getTop() + getDragHandleSizeBelow(),
9171bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    debugPaint);
9181bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        }
91974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    }
9201bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
92174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    public void computeScroll() {
92274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        super.computeScroll();
9236f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
92474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        if (!mScroller.isFinished()) {
92574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            if (mChallengeView == null) {
92674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                // Can't scroll if the view is missing.
92774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                Log.e(TAG, "Challenge view missing in computeScroll");
92874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mScroller.abortAnimation();
92974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                return;
9301bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            }
9311bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
93274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            mScroller.computeScrollOffset();
93374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            moveChallengeTo(mScroller.getCurrY());
93474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov
93574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            if (mScroller.isFinished()) {
93674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                post(mEndScrollRunnable);
9376f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            }
938d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
939d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
940d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
9418caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    private void cancelTransitionsInProgress() {
9428caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        if (!mScroller.isFinished()) {
9438caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            mScroller.abortAnimation();
9448caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            completeChallengeScroll();
9458caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        }
9468caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        if (mFader != null) {
9478caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            mFader.cancel();
9488caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        }
9498caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    }
9508caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
9518caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    public void fadeInChallenge() {
9528caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        fadeChallenge(true);
9538caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    }
9548caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
9558caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    public void fadeOutChallenge() {
9568caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        fadeChallenge(false);
9578caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    }
9588caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
9598caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    public void fadeChallenge(final boolean show) {
9608caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        if (mChallengeView != null) {
9618caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
9628caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            cancelTransitionsInProgress();
9638caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            float alpha = show ? 1f : 0f;
9648caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            int duration = show ? CHALLENGE_FADE_IN_DURATION : CHALLENGE_FADE_OUT_DURATION;
9658caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            mFader = ObjectAnimator.ofFloat(mChallengeView, "alpha", alpha);
9668caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            mFader.addListener(new AnimatorListenerAdapter() {
9678caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen                @Override
9688caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen                public void onAnimationStart(Animator animation) {
9698caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen                    onFadeStart(show);
9708caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen                }
9718caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen                @Override
9728caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen                public void onAnimationEnd(Animator animation) {
9738caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen                    onFadeEnd(show);
9748caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen                }
9758caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            });
9768caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            mFader.setDuration(duration);
9778caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            mFader.start();
9788caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        }
9798caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    }
9808caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
9818caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    private int getMaxChallengeBottom() {
9828caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        if (mChallengeView == null) return 0;
9838caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        final int layoutBottom = getLayoutBottom();
9848caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        final int challengeHeight = mChallengeView.getMeasuredHeight();
9858caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
9868caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        return (layoutBottom + challengeHeight - mChallengeBottomBound);
9878caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    }
9888caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
9898caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    private int getMinChallengeBottom() {
9908caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        return getLayoutBottom();
9918caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    }
9928caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
9938caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
9948caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    private void onFadeStart(boolean show) {
9958caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        mChallengeInteractiveInternal = false;
9968caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
9978caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
9988caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        if (show) {
9998caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            moveChallengeTo(getMinChallengeBottom());
10008caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        }
10018caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
10028caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        setScrollState(SCROLL_STATE_FADING);
10038caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    }
10048caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
10058caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    private void onFadeEnd(boolean show) {
10068caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        mChallengeInteractiveInternal = true;
10078caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        setChallengeShowing(show);
10088caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
10098caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        if (!show) {
10108caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen            moveChallengeTo(getMaxChallengeBottom());
10118caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        }
10128caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
10138caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        mChallengeView.setLayerType(LAYER_TYPE_NONE, null);
10148caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        mFader = null;
10158caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        setScrollState(SCROLL_STATE_IDLE);
10168caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen    }
10178caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen
1018e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen    public int getMaxChallengeTop() {
1019e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen        if (mChallengeView == null) return 0;
1020e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen
1021e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen        final int layoutBottom = getLayoutBottom();
10228a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        final int challengeHeight = mChallengeView.getMeasuredHeight();
1023e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen        return layoutBottom - challengeHeight;
1024e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen    }
1025e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen
1026d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
1027d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Move the bottom edge of mChallengeView to a new position and notify the listener
1028d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * if it represents a change in position. Changes made through this method will
1029d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * be stable across layout passes. If this method is called before first layout of
1030d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * this SlidingChallengeLayout it will have no effect.
1031d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     *
1032d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system.
1033d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @return true if the challenge view was moved
1034d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
1035d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean moveChallengeTo(int bottom) {
1036d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null || !mHasLayout) {
1037d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return false;
1038d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1039d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1040b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler        final int layoutBottom = getLayoutBottom();
1041d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int challengeHeight = mChallengeView.getHeight();
1042d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
10438caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen        bottom = Math.max(getMinChallengeBottom(),
10448caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen                Math.min(bottom, getMaxChallengeBottom()));
1045d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
104667625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        float offset = 1.f - (float) (bottom - layoutBottom) /
104767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                (challengeHeight - mChallengeBottomBound);
1048d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeOffset = offset;
1049d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (offset > 0 && !mChallengeShowing) {
1050d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            setChallengeShowing(true);
1051d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1052d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1053d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView.layout(mChallengeView.getLeft(),
1054d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom);
1055d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1056fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        mChallengeView.setAlpha(getChallengeAlpha());
10570b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mScrollListener != null) {
10580b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop());
1059d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1060d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        postInvalidateOnAnimation();
1061d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return true;
1062d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1063d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1064b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    /**
1065b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     * The bottom edge of this SlidingChallengeLayout's coordinate system; will coincide with
1066b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     * the bottom edge of mChallengeView when the challenge is fully opened.
1067b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     */
1068b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    private int getLayoutBottom() {
1069b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler        final int bottomMargin = (mChallengeView == null)
1070b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                ? 0
1071b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                : ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
10728a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        final int layoutBottom = getMeasuredHeight() - getPaddingBottom() - bottomMargin;
1073b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler        return layoutBottom;
1074b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    }
1075b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler
1076b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    /**
1077b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     * The bottom edge of mChallengeView; essentially, where the sliding challenge 'is'.
1078b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     */
1079d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int getChallengeBottom() {
1080d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return 0;
1081d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1082d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mChallengeView.getBottom();
1083d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1084d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1085b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    /**
1086d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Show or hide the challenge view, animating it if necessary.
1087d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param show true to show, false to hide
1088d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
1089d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void showChallenge(boolean show) {
1090d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        showChallenge(show, 0);
1091995270f01175234d0615a0e1900576f142a3b4adAdam Powell        if (!show) {
1092995270f01175234d0615a0e1900576f142a3b4adAdam Powell            // Block any drags in progress so that callers can use this to disable dragging
1093995270f01175234d0615a0e1900576f142a3b4adAdam Powell            // for other touch interactions.
1094995270f01175234d0615a0e1900576f142a3b4adAdam Powell            mBlockDrag = true;
1095995270f01175234d0615a0e1900576f142a3b4adAdam Powell        }
1096d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1097d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1098d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void showChallenge(int velocity) {
1099d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        boolean show = false;
1100d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (Math.abs(velocity) > mMinVelocity) {
1101d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            show = velocity < 0;
1102d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        } else {
1103d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            show = mChallengeOffset >= 0.5f;
1104d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1105d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        showChallenge(show, velocity);
1106d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1107d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1108d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void showChallenge(boolean show, int velocity) {
1109d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) {
1110d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            setChallengeShowing(false);
1111d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
1112d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1113d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1114d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mHasLayout) {
111567625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell            final int layoutBottom = getLayoutBottom();
1116d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            animateChallengeTo(show ? layoutBottom :
111767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                    layoutBottom + mChallengeView.getHeight() - mChallengeBottomBound, velocity);
1118d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1119d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1120d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
112119a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    @Override
1122d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
1123d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return new LayoutParams(getContext(), attrs);
1124d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1125d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1126d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
1127d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1128d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
1129d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
1130d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                new LayoutParams(p);
1131d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1132d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1133d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
1134d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1135d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return new LayoutParams();
1136d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1137d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1138d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
1139d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1140d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return p instanceof LayoutParams;
1141d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1142d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1143d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static class LayoutParams extends MarginLayoutParams {
1144eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public int childType = CHILD_TYPE_NONE;
1145eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_NONE = 0;
1146eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_CHALLENGE = 2;
1147eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_SCRIM = 4;
1148cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        public static final int CHILD_TYPE_WIDGETS = 5;
114974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        public static final int CHILD_TYPE_EXPAND_CHALLENGE_HANDLE = 6;
1150d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1151d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams() {
1152d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            this(MATCH_PARENT, WRAP_CONTENT);
1153d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1154d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1155d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(int width, int height) {
1156d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(width, height);
1157d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1158d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1159d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(android.view.ViewGroup.LayoutParams source) {
1160d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
1161d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1162d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1163d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(MarginLayoutParams source) {
1164d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
1165d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1166d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1167d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(LayoutParams source) {
1168d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
1169d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1170eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            childType = source.childType;
1171d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1172d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1173d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(Context c, AttributeSet attrs) {
1174d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(c, attrs);
1175d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1176d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final TypedArray a = c.obtainStyledAttributes(attrs,
1177d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    R.styleable.SlidingChallengeLayout_Layout);
1178eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            childType = a.getInt(R.styleable.SlidingChallengeLayout_Layout_layout_childType,
1179eee209313d564be37b8fc068f785b3844cd6597eAdam Powell                    CHILD_TYPE_NONE);
1180d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            a.recycle();
1181d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1182d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1183d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller}
1184