SlidingChallengeLayout.java revision db1c5d596e91713132131601536aaad32e693325
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;
29cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powellimport android.graphics.Rect;
30d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.graphics.drawable.Drawable;
31d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.AttributeSet;
326f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.util.FloatProperty;
33d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.Log;
346f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.util.Property;
35d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.MotionEvent;
36d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.VelocityTracker;
37d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.View;
38d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewConfiguration;
39d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewGroup;
4074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganovimport android.view.accessibility.AccessibilityManager;
41d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.animation.Interpolator;
42d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.widget.Scroller;
43d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
44d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller/**
45d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * This layout handles interaction with the sliding security challenge views
46d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * that overlay/resize other keyguard contents.
47d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */
4819a5267003e7dc70100a4bd4f1f449523b2ff38bJim Millerpublic class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout {
49d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final String TAG = "SlidingChallengeLayout";
501bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler    private static final boolean DEBUG = false;
51d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
52c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    // The drag handle is measured in dp above & below the top edge of the
53c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    // challenge view; these parameters change based on whether the challenge
54c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    // is open or closed.
559c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell    private static final int DRAG_HANDLE_CLOSED_ABOVE = 8; // dp
56cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell    private static final int DRAG_HANDLE_CLOSED_BELOW = 0; // dp
57c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private static final int DRAG_HANDLE_OPEN_ABOVE = 8; // dp
58c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private static final int DRAG_HANDLE_OPEN_BELOW = 0; // dp
59c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
60c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private static final int HANDLE_ANIMATE_DURATION = 200; // ms
61c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
62d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // Drawn to show the drag handle in closed state; crossfades to the challenge view
63d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // when challenge is fully visible
646f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    private Drawable mFrameDrawable;
65cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    private boolean mEdgeCaptured;
66d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
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;
76eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private boolean mIsBouncing = false;
77d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
78d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private final Scroller mScroller;
79db1c5d596e91713132131601536aaad32e693325Adam Cohen    private ObjectAnimator mFader;
80d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mScrollState;
810b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private OnChallengeScrolledListener mScrollListener;
820b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private OnBouncerStateChangedListener mBouncerListener;
83d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
84d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_IDLE = 0;
85d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_DRAGGING = 1;
86d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static final int SCROLL_STATE_SETTLING = 2;
87db1c5d596e91713132131601536aaad32e693325Adam Cohen    public static final int SCROLL_STATE_FADING = 3;
88db1c5d596e91713132131601536aaad32e693325Adam Cohen
89db1c5d596e91713132131601536aaad32e693325Adam Cohen    private static final int CHALLENGE_FADE_DURATION = 70;
90d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
91d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int MAX_SETTLE_DURATION = 600; // ms
92d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
93d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // ID of the pointer in charge of a current drag
94d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mActivePointerId = INVALID_POINTER;
95d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final int INVALID_POINTER = -1;
96d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
97d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if the user is currently dragging the slider
98d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mDragging;
99d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if the user may not drag until a new gesture begins
100d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mBlockDrag;
101d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
102d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private VelocityTracker mVelocityTracker;
103d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mMinVelocity;
104d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int mMaxVelocity;
105c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private float mGestureStartX, mGestureStartY; // where did you first touch the screen?
106b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    private int mGestureStartChallengeBottom; // where was the challenge at that time?
107c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
108c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int mDragHandleClosedBelow; // handle hitrect extension into the challenge view
109c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int mDragHandleClosedAbove; // extend the handle's hitrect this far above the line
110c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int mDragHandleOpenBelow; // handle hitrect extension into the challenge view
111c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int mDragHandleOpenAbove; // extend the handle's hitrect this far above the line
112c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
1130b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    private int mDragHandleEdgeSlop;
11467625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell    private int mChallengeBottomBound; // Number of pixels from the top of the challenge view
11567625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                                       // that should remain on-screen
116c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
117c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int mTouchSlop;
11874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    private int mTouchSlopSquare;
119c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
1206f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    float mHandleAlpha;
1216f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    float mFrameAlpha;
122cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    float mFrameAnimationTarget = Float.MIN_VALUE;
1236f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    private ObjectAnimator mHandleAnimation;
1246f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    private ObjectAnimator mFrameAnimation;
1256f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
126cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    private final Rect mTempRect = new Rect();
127cdf8b487456115a1bcc626f53b8da63308dc42ddAdam 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
1486f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    static final Property<SlidingChallengeLayout, Float> FRAME_ALPHA =
1496f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            new FloatProperty<SlidingChallengeLayout>("frameAlpha") {
1506f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        @Override
1516f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        public void setValue(SlidingChallengeLayout view, float value) {
1526f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            if (view.mFrameDrawable != null) {
1536f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                view.mFrameAlpha = value;
1546f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                view.mFrameDrawable.setAlpha((int) (value * 0xFF));
1556f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                view.mFrameDrawable.invalidateSelf();
1566f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            }
1576f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
1586f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
1596f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        @Override
1606f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        public Float get(SlidingChallengeLayout view) {
1616f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            return view.mFrameAlpha;
1626f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
1636f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    };
164d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
165d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // True if at least one layout pass has happened since the view was attached.
166d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean mHasLayout;
167d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
168d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final Interpolator sMotionInterpolator = new Interpolator() {
169d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public float getInterpolation(float t) {
170d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            t -= 1.0f;
171d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return t * t * t * t * t + 1.0f;
172d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
173d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
174d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
175d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private static final Interpolator sHandleFadeInterpolator = new Interpolator() {
176d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public float getInterpolation(float t) {
177d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return t * t;
178d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
179d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
180d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
181d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private final Runnable mEndScrollRunnable = new Runnable () {
182d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void run() {
183d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            completeChallengeScroll();
184d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
185d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    };
186d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
187eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    private final OnClickListener mScrimClickListener = new OnClickListener() {
188eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        @Override
189eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public void onClick(View v) {
190eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            hideBouncer();
191eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
192eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    };
193eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
19474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    private final OnClickListener mExpandChallengeClickListener = new OnClickListener() {
19574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        @Override
19674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        public void onClick(View v) {
19778a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock            if (!isChallengeShowing()) {
19874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                showChallenge(true);
19974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            }
20074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        }
20174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    };
20274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov
203d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
204d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Listener interface that reports changes in scroll state of the challenge area.
205d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
206d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public interface OnChallengeScrolledListener {
207d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        /**
208d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * The scroll state itself changed.
209d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
210d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>scrollState will be one of the following:</p>
211d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
212d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <ul>
213d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li>
214d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging
215d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * the challenge area.</li>
216d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating
217d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * into place.</li>
218d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * </ul>
219d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
220d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>Do not perform expensive operations (e.g. layout)
221d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p>
222d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
223d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param scrollState The new scroll state of the challenge area.
224d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         */
225d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void onScrollStateChanged(int scrollState);
226d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
227d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        /**
228d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * The precise position of the challenge area has changed.
229d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
230d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * <p>NOTE: It is NOT safe to modify layout or call any View methods that may
231d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * result in a requestLayout anywhere in your view hierarchy as a result of this call.
232d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * It may be called during drawing.</p>
233d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *
234d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param scrollPosition New relative position of the challenge area.
235d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                       1.f = fully visible/ready to be interacted with.
236d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                       0.f = fully invisible/inaccessible to the user.
237d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         * @param challengeTop Position of the top edge of the challenge view in px in the
238d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         *                     SlidingChallengeLayout's coordinate system.
239d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller         */
240d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public void onScrollPositionChanged(float scrollPosition, int challengeTop);
241d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
242d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
243d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context) {
244d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        this(context, null);
245d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
246d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
247d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context, AttributeSet attrs) {
248d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        this(context, attrs, 0);
249d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
250d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
251d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) {
252d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super(context, attrs, defStyle);
253d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
254d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mScroller = new Scroller(context, sMotionInterpolator);
255d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
256d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final ViewConfiguration vc = ViewConfiguration.get(context);
257d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mMinVelocity = vc.getScaledMinimumFlingVelocity();
258d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mMaxVelocity = vc.getScaledMaximumFlingVelocity();
259d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
260fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        final Resources res = getResources();
261fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        mDragHandleEdgeSlop = res.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size);
2620b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
263c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
26474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        mTouchSlopSquare = mTouchSlop * mTouchSlop;
265c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
266fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        final float density = res.getDisplayMetrics().density;
2671bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
2681bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        // top half of the lock icon, plus another 25% to be sure
269c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        mDragHandleClosedAbove = (int) (DRAG_HANDLE_CLOSED_ABOVE * density + 0.5f);
270c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        mDragHandleClosedBelow = (int) (DRAG_HANDLE_CLOSED_BELOW * density + 0.5f);
271c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        mDragHandleOpenAbove = (int) (DRAG_HANDLE_OPEN_ABOVE * density + 0.5f);
272c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        mDragHandleOpenBelow = (int) (DRAG_HANDLE_OPEN_BELOW * density + 0.5f);
273c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler
274c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        // how much space to account for in the handle when closed
275fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        mChallengeBottomBound = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding);
2761bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
277cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell        setWillNotDraw(false);
278cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell    }
279cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell
2806e36f1019cb6d7098b49e036e1640c8cfc6880cfJohn Spurlock    public void setHandleAlpha(float alpha) {
2816e36f1019cb6d7098b49e036e1640c8cfc6880cfJohn Spurlock        if (mExpandChallengeView != null) {
2826e36f1019cb6d7098b49e036e1640c8cfc6880cfJohn Spurlock            mExpandChallengeView.setAlpha(alpha);
2836f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        }
2846f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    }
2856f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
286bb5c941149b66c0192736468bb60f47984dd5e1fJohn Spurlock    public void setChallengeInteractive(boolean interactive) {
287db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeInteractiveExternal = interactive;
28878a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock        if (mExpandChallengeView != null) {
28978a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock            mExpandChallengeView.setEnabled(interactive);
29078a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock        }
291bb5c941149b66c0192736468bb60f47984dd5e1fJohn Spurlock    }
292bb5c941149b66c0192736468bb60f47984dd5e1fJohn Spurlock
2936f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    void animateHandle(boolean visible) {
2946f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        if (mHandleAnimation != null) {
2956f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mHandleAnimation.cancel();
29667625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell            mHandleAnimation = null;
297d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
29867625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        final float targetAlpha = visible ? 1.f : 0.f;
29967625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        if (targetAlpha == mHandleAlpha) {
30067625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell            return;
30167625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        }
30267625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, targetAlpha);
3036f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleAnimation.setInterpolator(sHandleFadeInterpolator);
3046f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleAnimation.setDuration(HANDLE_ANIMATE_DURATION);
3056f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mHandleAnimation.start();
3066f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell    }
3076f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
308cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    void animateFrame(final boolean visible, final boolean full) {
3096f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        if (mFrameDrawable == null) return;
3106f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
311cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        final float targetAlpha = visible ? (full ? 1.f : 0.5f) : 0.f;
312cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (mFrameAnimation != null && targetAlpha != mFrameAnimationTarget) {
3136f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mFrameAnimation.cancel();
314cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            mFrameAnimationTarget = Float.MIN_VALUE;
31567625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        }
316cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (targetAlpha == mFrameAlpha || targetAlpha == mFrameAnimationTarget) {
31767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell            return;
318d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
319cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        mFrameAnimationTarget = targetAlpha;
32067625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell
32167625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        mFrameAnimation = ObjectAnimator.ofFloat(this, FRAME_ALPHA, targetAlpha);
3226f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mFrameAnimation.setInterpolator(sHandleFadeInterpolator);
3236f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mFrameAnimation.setDuration(HANDLE_ANIMATE_DURATION);
324cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        mFrameAnimation.addListener(new AnimatorListenerAdapter() {
325cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            @Override
326cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            public void onAnimationEnd(Animator animation) {
327cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                mFrameAnimationTarget = Float.MIN_VALUE;
328cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
329cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                if (!visible && full && mChallengeView != null) {
330cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    // Mess with padding/margin to remove insets on the bouncer frame.
331cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    mChallengeView.setPadding(0, 0, 0, 0);
332cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams();
333cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    lp.leftMargin = lp.rightMargin = getChallengeMargin(true);
334cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    mChallengeView.setLayoutParams(lp);
335cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                }
336cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                mFrameAnimation = null;
337cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            }
338cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
339cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            @Override
340cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            public void onAnimationCancel(Animator animation) {
341cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                mFrameAnimationTarget = Float.MIN_VALUE;
342cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                mFrameAnimation = null;
343cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            }
344cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        });
3456f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        mFrameAnimation.start();
346d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
347d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
348d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void sendInitialListenerUpdates() {
3490b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mScrollListener != null) {
350d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0;
3510b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollPositionChanged(mChallengeOffset, challengeTop);
3520b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollStateChanged(mScrollState);
353d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
354d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
355d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
356d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) {
3570b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        mScrollListener = listener;
358d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mHasLayout) {
359d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            sendInitialListenerUpdates();
360d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
361d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
362d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
3630b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) {
3640b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        mBouncerListener = listener;
3650b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    }
3660b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
367d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
368d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void onAttachedToWindow() {
369d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.onAttachedToWindow();
370d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
371d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = false;
372d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
373d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
374d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
375d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void onDetachedFromWindow() {
376d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.onDetachedFromWindow();
377d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
378d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        removeCallbacks(mEndScrollRunnable);
379d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mHasLayout = false;
380d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
381d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
3820b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    @Override
3830b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    public void requestChildFocus(View child, View focused) {
3840b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mIsBouncing && child != mChallengeView) {
3850b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            // Clear out of the bouncer if the user tries to move focus outside of
3860b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            // the security challenge view.
3870b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            hideBouncer();
3880b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
3890b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        super.requestChildFocus(child, focused);
3900b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell    }
3910b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell
392d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // We want the duration of the page snap animation to be influenced by the distance that
393d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // the screen has to travel, however, we don't want this duration to be effected in a
394d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // purely linear fashion. Instead, we use this method to moderate the effect that the distance
395d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    // of travel has on the overall snap duration.
396d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    float distanceInfluenceForSnapDuration(float f) {
397d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        f -= 0.5f; // center the values about 0.
398d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        f *= 0.3f * Math.PI / 2.0f;
399d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return (float) Math.sin(f);
400d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
401d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
402d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void setScrollState(int state) {
403d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mScrollState != state) {
404d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mScrollState = state;
405d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
4066f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing);
407cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            if (!mIsBouncing) {
408cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                animateFrame(false, false);
409cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            }
4100b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            if (mScrollListener != null) {
4110b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell                mScrollListener.onScrollStateChanged(state);
412d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
413d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
414d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
415d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
416d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void completeChallengeScroll() {
417d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setChallengeShowing(mChallengeOffset != 0);
418d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setScrollState(SCROLL_STATE_IDLE);
419db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeInteractiveInternal = true;
420cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        mChallengeView.setLayerType(LAYER_TYPE_NONE, null);
421d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
422d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
423eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    void setScrimView(View scrim) {
424eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
425eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            mScrimView.setOnClickListener(null);
426eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
427eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView = scrim;
428eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE);
429eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setFocusable(true);
430eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mScrimView.setOnClickListener(mScrimClickListener);
431eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
432eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
433d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
434d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Animate the bottom edge of the challenge view to the given position.
435d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     *
436d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param y desired final position for the bottom edge of the challenge view in px
437d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param velocity velocity in
438d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
439d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    void animateChallengeTo(int y, int velocity) {
440d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) {
441d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            // Nothing to do.
442d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
443d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
444db1c5d596e91713132131601536aaad32e693325Adam Cohen
445db1c5d596e91713132131601536aaad32e693325Adam Cohen        if (mFader != null) {
446db1c5d596e91713132131601536aaad32e693325Adam Cohen            mFader.cancel();
447db1c5d596e91713132131601536aaad32e693325Adam Cohen        }
448db1c5d596e91713132131601536aaad32e693325Adam Cohen
449db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeInteractiveInternal = false;
450db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
45167625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        final int sy = mChallengeView.getBottom();
45267625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        final int dy = y - sy;
453d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (dy == 0) {
454d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            completeChallengeScroll();
455d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
456d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
457d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
458d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setScrollState(SCROLL_STATE_SETTLING);
459d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
460d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int childHeight = mChallengeView.getHeight();
461d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int halfHeight = childHeight / 2;
462d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight);
463d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final float distance = halfHeight + halfHeight *
464d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                distanceInfluenceForSnapDuration(distanceRatio);
465d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
466d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        int duration = 0;
467d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        velocity = Math.abs(velocity);
468d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (velocity > 0) {
469d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            duration = 4 * Math.round(1000 * Math.abs(distance / velocity));
470d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        } else {
471d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final float childDelta = (float) Math.abs(dy) / childHeight;
472d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            duration = (int) ((childDelta + 1) * 100);
473d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
474d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        duration = Math.min(duration, MAX_SETTLE_DURATION);
475d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
476d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mScroller.startScroll(0, sy, 0, dy, duration);
477d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        postInvalidateOnAnimation();
478d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
479d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
480d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void setChallengeShowing(boolean showChallenge) {
48174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        if (mChallengeShowing == showChallenge) {
48274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            return;
48374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        }
48467625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        mChallengeShowing = showChallenge;
48574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        if (mChallengeShowing) {
48674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            mExpandChallengeView.setVisibility(View.INVISIBLE);
48774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            mChallengeView.setVisibility(View.VISIBLE);
48874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
48974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mChallengeView.requestAccessibilityFocus();
49074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mChallengeView.announceForAccessibility(mContext.getString(
49174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                        R.string.keyguard_accessibility_unlock_area_expanded));
49274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            }
49374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        } else {
49474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            mExpandChallengeView.setVisibility(View.VISIBLE);
49574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            mChallengeView.setVisibility(View.INVISIBLE);
49674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
49774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mExpandChallengeView.requestAccessibilityFocus();
49874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mChallengeView.announceForAccessibility(mContext.getString(
49974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                        R.string.keyguard_accessibility_unlock_area_collapsed));
50074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            }
50174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        }
502d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
503d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
504d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
505d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @return true if the challenge is at all visible.
506d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
507d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean isChallengeShowing() {
508d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mChallengeShowing;
509d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
510d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
511d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
51219a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    public boolean isChallengeOverlapping() {
51319a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller        return mChallengeShowing;
51419a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    }
51519a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller
51619a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    @Override
517eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public boolean isBouncing() {
518eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        return mIsBouncing;
519eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
520eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
521eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
522eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public void showBouncer() {
523eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mIsBouncing) return;
524eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        showChallenge(true);
525eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mIsBouncing = true;
526eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
5276f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mScrimView.setVisibility(VISIBLE);
528eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
529052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren        if (mChallengeView != null) {
530052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren            mChallengeView.showBouncer(HANDLE_ANIMATE_DURATION);
531052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren        }
532cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        // Mess with padding/margin to inset the bouncer frame.
533cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        // We have more space available to us otherwise.
534cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (mChallengeView != null) {
535cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            if (mFrameDrawable == null || !mFrameDrawable.getPadding(mTempRect)) {
536cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                mTempRect.set(0, 0, 0, 0);
537cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            }
538cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            mChallengeView.setPadding(mTempRect.left, mTempRect.top, mTempRect.right,
539cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    mTempRect.bottom);
540cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams();
541cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            lp.leftMargin = lp.rightMargin = getChallengeMargin(false);
542cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            mChallengeView.setLayoutParams(lp);
543cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
544cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
5456f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell        animateFrame(true, true);
546cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
5470b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mBouncerListener != null) {
5480b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mBouncerListener.onBouncerStateChanged(true);
5490b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
550eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
551eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
552eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
553eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    public void hideBouncer() {
554eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (!mIsBouncing) return;
555cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        showChallenge(false);
556eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        mIsBouncing = false;
557eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        if (mScrimView != null) {
558eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            mScrimView.setVisibility(GONE);
559eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        }
560052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren        if (mChallengeView != null) {
561052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren            mChallengeView.hideBouncer(HANDLE_ANIMATE_DURATION);
562052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren        }
563cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        animateFrame(false, true);
5640b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mBouncerListener != null) {
5650b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mBouncerListener.onBouncerStateChanged(false);
5660b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        }
567eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    }
568eee209313d564be37b8fc068f785b3844cd6597eAdam Powell
569cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    private int getChallengeMargin(boolean expanded) {
570cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        return expanded && mHasGlowpad ? 0 : mDragHandleEdgeSlop;
571cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    }
572cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
573fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell    private float getChallengeAlpha() {
574fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        float x = mChallengeOffset - 1;
575fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        return x * x * x + 1.f;
576fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell    }
577fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell
578eee209313d564be37b8fc068f785b3844cd6597eAdam Powell    @Override
579d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void requestDisallowInterceptTouchEvent(boolean allowIntercept) {
580d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
581d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // If there are one or more pointers in the challenge view before we take over
582d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // touch events, onInterceptTouchEvent will set mBlockDrag.
583d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
584d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
585d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
586d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean onInterceptTouchEvent(MotionEvent ev) {
587d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mVelocityTracker == null) {
588d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mVelocityTracker = VelocityTracker.obtain();
589d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
590d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.addMovement(ev);
591d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
592d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int action = ev.getActionMasked();
593d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        switch (action) {
594022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller            case MotionEvent.ACTION_DOWN:
595c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                mGestureStartX = ev.getX();
596b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                mGestureStartY = ev.getY();
597995270f01175234d0615a0e1900576f142a3b4adAdam Powell                mBlockDrag = false;
598022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller                break;
599022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
600d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_CANCEL:
601d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_UP:
602d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
603d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
604d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
605d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_MOVE:
606d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int count = ev.getPointerCount();
607d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                for (int i = 0; i < count; i++) {
608d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float x = ev.getX(i);
609d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final float y = ev.getY(i);
610db1c5d596e91713132131601536aaad32e693325Adam Cohen                    if (!mIsBouncing && mActivePointerId == INVALID_POINTER
6119c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell                                && (crossedDragHandle(x, y, mGestureStartY)
612c238af5112bee38b3143a65849bd41785c3744b0Adam Powell                                || (isInChallengeView(x, y) &&
6139c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell                                        mScrollState == SCROLL_STATE_SETTLING))) {
614d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mActivePointerId = ev.getPointerId(i);
615c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                        mGestureStartX = x;
616c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                        mGestureStartY = y;
617b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                        mGestureStartChallengeBottom = getChallengeBottom();
618d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mDragging = true;
619cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                        mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
6209c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell                    } else if (mChallengeShowing && isInChallengeView(x, y)) {
621d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        mBlockDrag = true;
622d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
623d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
624d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
625d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
626d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
627db1c5d596e91713132131601536aaad32e693325Adam Cohen        if (mBlockDrag || isChallengeInteractionBlocked()) {
628d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mActivePointerId = INVALID_POINTER;
629d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mDragging = false;
630d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
631d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
632d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mDragging;
633d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
634d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
635db1c5d596e91713132131601536aaad32e693325Adam Cohen    private boolean isChallengeInteractionBlocked() {
636db1c5d596e91713132131601536aaad32e693325Adam Cohen        return !mChallengeInteractiveExternal || !mChallengeInteractiveInternal;
637db1c5d596e91713132131601536aaad32e693325Adam Cohen    }
638db1c5d596e91713132131601536aaad32e693325Adam Cohen
639d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void resetTouch() {
640d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.recycle();
641d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker = null;
642d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mActivePointerId = INVALID_POINTER;
643d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mDragging = mBlockDrag = false;
644d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
645d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
646d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
647d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public boolean onTouchEvent(MotionEvent ev) {
648d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mVelocityTracker == null) {
649d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            mVelocityTracker = VelocityTracker.obtain();
650d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
651d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mVelocityTracker.addMovement(ev);
652d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
653d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int action = ev.getActionMasked();
654d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        switch (action) {
655995270f01175234d0615a0e1900576f142a3b4adAdam Powell            case MotionEvent.ACTION_DOWN:
656995270f01175234d0615a0e1900576f142a3b4adAdam Powell                mBlockDrag = false;
657c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                mGestureStartX = ev.getX();
658995270f01175234d0615a0e1900576f142a3b4adAdam Powell                mGestureStartY = ev.getY();
659995270f01175234d0615a0e1900576f142a3b4adAdam Powell                break;
660995270f01175234d0615a0e1900576f142a3b4adAdam Powell
661d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_CANCEL:
662db1c5d596e91713132131601536aaad32e693325Adam Cohen                if (mDragging && !isChallengeInteractionBlocked()) {
663d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    showChallenge(0);
664d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
665d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
666d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
667d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
668d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_POINTER_UP:
669d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) {
670d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    break;
671d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
672d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_UP:
673db1c5d596e91713132131601536aaad32e693325Adam Cohen                if (mDragging && !isChallengeInteractionBlocked()) {
674d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
675d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId));
676d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
677d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                resetTouch();
678d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
679d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
680d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            case MotionEvent.ACTION_MOVE:
6816f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                if (!mDragging && !mBlockDrag && !mIsBouncing) {
682d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int count = ev.getPointerCount();
683d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    for (int i = 0; i < count; i++) {
684d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        final float x = ev.getX(i);
685d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        final float y = ev.getY(i);
686d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
687b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                        if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
688d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                                (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
6891ec464679f731827f626856084f19643fff64a94John Spurlock                                && mActivePointerId == INVALID_POINTER
690db1c5d596e91713132131601536aaad32e693325Adam Cohen                                && !isChallengeInteractionBlocked()) {
691c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                            mGestureStartX = x;
692b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                            mGestureStartY = y;
693d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mActivePointerId = ev.getPointerId(i);
694b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                            mGestureStartChallengeBottom = getChallengeBottom();
695d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            mDragging = true;
696cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                            mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
697d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            break;
698d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        }
699d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
700d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
701d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Not an else; this can be set above.
702d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mDragging) {
703d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // No-op if already in this state, but set it here in case we arrived
704d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    // at this point from either intercept or the above.
705d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    setScrollState(SCROLL_STATE_DRAGGING);
706d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
707d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    final int index = ev.findPointerIndex(mActivePointerId);
708d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    if (index < 0) {
709d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        // Oops, bogus state. We lost some touch events somewhere.
710d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        // Just drop it with no velocity and let things settle.
711d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        resetTouch();
712d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        showChallenge(0);
713d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        return true;
714d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    }
715b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                    final float y = ev.getY(index);
716b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                    final float pos = Math.min(y - mGestureStartY,
71767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                            getLayoutBottom() - mChallengeBottomBound);
718b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler
719b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                    moveChallengeTo(mGestureStartChallengeBottom + (int) pos);
720d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
721d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                break;
722d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
723d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return true;
724d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
725d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
726b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    /**
727cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     * The lifecycle of touch events is subtle and it's very easy to do something
728cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     * that will cause bugs that will be nasty to track when overriding this method.
729cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     * Normally one should always override onInterceptTouchEvent instead.
730cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     *
731cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     * To put it another way, don't try this at home.
732cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell     */
733cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    @Override
734cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    public boolean dispatchTouchEvent(MotionEvent ev) {
735cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        final int action = ev.getActionMasked();
736cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        boolean handled = false;
737cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (action == MotionEvent.ACTION_DOWN) {
738cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // Defensive programming: if we didn't get the UP or CANCEL, reset anyway.
739cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            mEdgeCaptured = false;
740cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
741cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (mWidgetsView != null && !mIsBouncing && (mEdgeCaptured || isEdgeSwipeBeginEvent(ev))) {
742cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // Normally we would need to do a lot of extra stuff here.
743cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // We can only get away with this because we haven't padded in
744cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // the widget pager or otherwise transformed it during layout.
745cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // We also don't support things like splitting MotionEvents.
746cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
747cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // We set handled to captured even if dispatch is returning false here so that
748cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            // we don't send a different view a busted or incomplete event stream.
749cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            handled = mEdgeCaptured |= mWidgetsView.dispatchTouchEvent(ev);
750cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
751cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
752cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (!handled && !mEdgeCaptured) {
753cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            handled = super.dispatchTouchEvent(ev);
754cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
755cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
756cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
757cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            mEdgeCaptured = false;
758cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
759cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
760cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        return handled;
761cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    }
762cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
763cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    private boolean isEdgeSwipeBeginEvent(MotionEvent ev) {
764cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
765cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            return false;
766cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        }
767cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
768cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        final float x = ev.getX();
769cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        return x < mDragHandleEdgeSlop || x >= getWidth() - mDragHandleEdgeSlop;
770cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    }
771cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell
772cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell    /**
773b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     * We only want to add additional vertical space to the drag handle when the panel is fully
774b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     * closed.
775b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     */
776c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int getDragHandleSizeAbove() {
777c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        return isChallengeShowing() ? mDragHandleOpenAbove : mDragHandleClosedAbove;
778c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    }
779c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler    private int getDragHandleSizeBelow() {
780c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler        return isChallengeShowing() ? mDragHandleOpenBelow : mDragHandleClosedBelow;
781b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    }
782b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler
783d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean isInChallengeView(float x, float y) {
78474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        return isPointInView(x, y, mChallengeView);
785d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
786d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
787d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean isInDragHandle(float x, float y) {
78874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        return isPointInView(x, y, mExpandChallengeView);
78974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    }
790d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
79174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    private boolean isPointInView(float x, float y, View view) {
79274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        if (view == null) {
79374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            return false;
79474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        }
79574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        return x >= view.getLeft() && y >= view.getTop()
79674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                && x < view.getRight() && y < view.getBottom();
797d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
798d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
799b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    private boolean crossedDragHandle(float x, float y, float initialY) {
8009c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell
801022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller        final int challengeTop = mChallengeView.getTop();
8029c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        final boolean horizOk = x >= 0 && x < getWidth();
8039c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell
8049c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        final boolean vertOk;
8059c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        if (mChallengeShowing) {
8069c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell            vertOk = initialY < (challengeTop - getDragHandleSizeAbove()) &&
8079c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell                    y > challengeTop + getDragHandleSizeBelow();
8089c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        } else {
8099c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell            vertOk = initialY > challengeTop + getDragHandleSizeBelow() &&
8109c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell                    y < challengeTop - getDragHandleSizeAbove();
8119c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        }
8129c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell        return horizOk && vertOk;
813022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller    }
814022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller
815d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
816d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected void onMeasure(int widthSpec, int heightSpec) {
817d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY ||
818d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) {
819d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            throw new IllegalArgumentException(
820d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    "SlidingChallengeLayout must be measured with an exact size");
821d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
822d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
823d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int width = MeasureSpec.getSize(widthSpec);
824d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int height = MeasureSpec.getSize(heightSpec);
825d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        setMeasuredDimension(width, height);
826d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
827d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        // Find one and only one challenge view.
828d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final View oldChallengeView = mChallengeView;
82974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        final View oldExpandChallengeView = mChallengeView;
830d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView = null;
83174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        mExpandChallengeView = null;
832d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int count = getChildCount();
8338a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen
8348a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        // First iteration through the children finds special children and sets any associated
8358a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        // state.
836d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        for (int i = 0; i < count; i++) {
837d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final View child = getChildAt(i);
838d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
839eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
840d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mChallengeView != null) {
841d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    throw new IllegalStateException(
842d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                            "There may only be one child with layout_isChallenge=\"true\"");
843d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
844052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren                if (!(child instanceof KeyguardSecurityContainer)) {
845052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren                            throw new IllegalArgumentException(
846052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren                                    "Challenge must be a KeyguardSecurityContainer");
847052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren                }
848052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren                mChallengeView = (KeyguardSecurityContainer) child;
849d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                if (mChallengeView != oldChallengeView) {
850d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE);
851d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                }
8526f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                // We're going to play silly games with the frame's background drawable later.
8536f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell                mFrameDrawable = mChallengeView.getBackground();
854cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                if (!mHasLayout) {
855cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    // Set up the margin correctly based on our content for the first run.
856cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    mHasGlowpad = child.findViewById(R.id.keyguard_selector_view) != null;
857cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                    lp.leftMargin = lp.rightMargin = getChallengeMargin(true);
858cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                }
85974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) {
86074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                if (mExpandChallengeView != null) {
86174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                    throw new IllegalStateException(
86274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                            "There may only be one child with layout_childType"
86374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                            + "=\"expandChallengeHandle\"");
86474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                }
86574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mExpandChallengeView = child;
86674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                if (mExpandChallengeView != oldExpandChallengeView) {
86774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                    mExpandChallengeView.setVisibility(mChallengeShowing ? INVISIBLE : VISIBLE);
86874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                    mExpandChallengeView.setOnClickListener(mExpandChallengeClickListener);
86974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                }
870eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
871eee209313d564be37b8fc068f785b3844cd6597eAdam Powell                setScrimView(child);
872cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell            } else if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) {
873cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell                mWidgetsView = child;
874d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
8758a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        }
8768a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen
8775be14ded6762bc51fabbbecb1b2ec01a8c758631Adam Cohen        // We want to measure the challenge view first, since the KeyguardWidgetPager
8785be14ded6762bc51fabbbecb1b2ec01a8c758631Adam Cohen        // needs to do things its measure pass that are dependent on the challenge view
8795be14ded6762bc51fabbbecb1b2ec01a8c758631Adam Cohen        // having been measured.
88074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        if (mChallengeView != null && mChallengeView.getVisibility() != View.GONE) {
8818a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen            measureChildWithMargins(mChallengeView, widthSpec, 0, heightSpec, 0);
8828a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        }
883d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
8848a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        // Measure the rest of the children
8858a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        for (int i = 0; i < count; i++) {
8868a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen            final View child = getChildAt(i);
88774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            if (child.getVisibility() == GONE) {
88874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                continue;
88974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            }
8908a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen            // Don't measure the challenge view twice!
8918a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen            if (child != mChallengeView) {
8928a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen                measureChildWithMargins(child, widthSpec, 0, heightSpec, 0);
8938a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen            }
894d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
895d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
896d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
897d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
898d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected void onLayout(boolean changed, int l, int t, int r, int b) {
899d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingLeft = getPaddingLeft();
900d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingTop = getPaddingTop();
901d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingRight = getPaddingRight();
902d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int paddingBottom = getPaddingBottom();
903d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int width = r - l;
904d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int height = b - t;
905d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
906d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int count = getChildCount();
907d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        for (int i = 0; i < count; i++) {
908d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final View child = getChildAt(i);
909d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
910d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            if (child.getVisibility() == GONE) continue;
911d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
912d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
913d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
914eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) {
915d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Challenge views pin to the bottom, offset by a portion of their height,
916d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // and center horizontally.
917d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int center = (paddingLeft + width - paddingRight) / 2;
918d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int childWidth = child.getMeasuredWidth();
919d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int childHeight = child.getMeasuredHeight();
920d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int left = center - childWidth / 2;
921d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                final int layoutBottom = height - paddingBottom - lp.bottomMargin;
922d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // We use the top of the challenge view to position the handle, so
923d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // we never want less than the handle size showing at the bottom.
92467625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                final int bottom = layoutBottom + (int) ((childHeight - mChallengeBottomBound)
925d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        * (1 - mChallengeOffset));
926fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell                child.setAlpha(getChallengeAlpha());
927d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.layout(left, bottom - childHeight, left + childWidth, bottom);
92874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) {
9290f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                final int center = (paddingLeft + width - paddingRight) / 2;
9300f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                final int left = center - child.getMeasuredWidth() / 2;
9310f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                final int right = left + child.getMeasuredWidth();
9320f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                final int bottom = height - paddingBottom - lp.bottomMargin;
9330f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                final int top = bottom - child.getMeasuredHeight();
9340f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov                child.layout(left, top, right, bottom);
935d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            } else {
936d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                // Non-challenge views lay out from the upper left, layered.
937d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                child.layout(paddingLeft + lp.leftMargin,
938d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingTop + lp.topMargin,
939d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingLeft + child.getMeasuredWidth(),
940d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                        paddingTop + child.getMeasuredHeight());
941d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            }
942d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
943d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
944d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (!mHasLayout) {
94567625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell            if (mFrameDrawable != null) {
94667625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                mFrameDrawable.setAlpha(0);
94767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell            }
9486f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            mHasLayout = true;
949d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
950d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
951d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
952d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
953d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void draw(Canvas c) {
954d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        super.draw(c);
9551bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        if (DEBUG) {
95674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            final Paint debugPaint = new Paint();
9571bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            debugPaint.setColor(0x40FF00CC);
9581bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            // show the isInDragHandle() rect
9591bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            c.drawRect(mDragHandleEdgeSlop,
960c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                    mChallengeView.getTop() - getDragHandleSizeAbove(),
9611bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    getWidth() - mDragHandleEdgeSlop,
962c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler                    mChallengeView.getTop() + getDragHandleSizeBelow(),
9631bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler                    debugPaint);
9641bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler        }
96574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    }
9661bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
96774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov    public void computeScroll() {
96874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        super.computeScroll();
9696f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell
97074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        if (!mScroller.isFinished()) {
97174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            if (mChallengeView == null) {
97274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                // Can't scroll if the view is missing.
97374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                Log.e(TAG, "Challenge view missing in computeScroll");
97474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                mScroller.abortAnimation();
97574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                return;
9761bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler            }
9771bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler
97874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            mScroller.computeScrollOffset();
97974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            moveChallengeTo(mScroller.getCurrY());
98074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov
98174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov            if (mScroller.isFinished()) {
98274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov                post(mEndScrollRunnable);
9836f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell            }
984d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
985d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
986d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
987e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen    public int getMaxChallengeTop() {
988e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen        if (mChallengeView == null) return 0;
989e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen
990e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen        final int layoutBottom = getLayoutBottom();
9918a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        final int challengeHeight = mChallengeView.getMeasuredHeight();
992e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen        return layoutBottom - challengeHeight;
993e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen    }
994e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen
995d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    /**
996d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Move the bottom edge of mChallengeView to a new position and notify the listener
997d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * if it represents a change in position. Changes made through this method will
998d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * be stable across layout passes. If this method is called before first layout of
999d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * this SlidingChallengeLayout it will have no effect.
1000d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     *
1001d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system.
1002d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @return true if the challenge view was moved
1003d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
1004d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private boolean moveChallengeTo(int bottom) {
1005d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null || !mHasLayout) {
1006d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return false;
1007d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1008d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1009b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler        final int layoutBottom = getLayoutBottom();
1010d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        final int challengeHeight = mChallengeView.getHeight();
1011d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1012d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        bottom = Math.max(layoutBottom,
101367625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                Math.min(bottom, layoutBottom + challengeHeight - mChallengeBottomBound));
1014d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
101567625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell        float offset = 1.f - (float) (bottom - layoutBottom) /
101667625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                (challengeHeight - mChallengeBottomBound);
1017d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeOffset = offset;
1018d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (offset > 0 && !mChallengeShowing) {
1019d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            setChallengeShowing(true);
1020d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1021d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1022d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        mChallengeView.layout(mChallengeView.getLeft(),
1023d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom);
1024d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1025fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell        mChallengeView.setAlpha(getChallengeAlpha());
10260b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell        if (mScrollListener != null) {
10270b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell            mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop());
1028d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1029d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        postInvalidateOnAnimation();
1030d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return true;
1031d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1032d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1033b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    /**
1034b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     * The bottom edge of this SlidingChallengeLayout's coordinate system; will coincide with
1035b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     * the bottom edge of mChallengeView when the challenge is fully opened.
1036b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     */
1037b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    private int getLayoutBottom() {
1038b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler        final int bottomMargin = (mChallengeView == null)
1039b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                ? 0
1040b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler                : ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
10418a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen        final int layoutBottom = getMeasuredHeight() - getPaddingBottom() - bottomMargin;
1042b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler        return layoutBottom;
1043b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    }
1044b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler
1045b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    /**
1046b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     * The bottom edge of mChallengeView; essentially, where the sliding challenge 'is'.
1047b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler     */
1048d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private int getChallengeBottom() {
1049d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) return 0;
1050d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1051d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return mChallengeView.getBottom();
1052d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1053d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1054b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler    /**
1055d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * Show or hide the challenge view, animating it if necessary.
1056d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     * @param show true to show, false to hide
1057d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller     */
1058d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public void showChallenge(boolean show) {
1059d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        showChallenge(show, 0);
1060995270f01175234d0615a0e1900576f142a3b4adAdam Powell        if (!show) {
1061995270f01175234d0615a0e1900576f142a3b4adAdam Powell            // Block any drags in progress so that callers can use this to disable dragging
1062995270f01175234d0615a0e1900576f142a3b4adAdam Powell            // for other touch interactions.
1063995270f01175234d0615a0e1900576f142a3b4adAdam Powell            mBlockDrag = true;
1064995270f01175234d0615a0e1900576f142a3b4adAdam Powell        }
1065d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1066d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1067db1c5d596e91713132131601536aaad32e693325Adam Cohen    public void dismissChallengeWithFade() {
1068db1c5d596e91713132131601536aaad32e693325Adam Cohen        if (mChallengeView != null) {
1069db1c5d596e91713132131601536aaad32e693325Adam Cohen            if (!mScroller.isFinished()) {
1070db1c5d596e91713132131601536aaad32e693325Adam Cohen                mScroller.abortAnimation();
1071db1c5d596e91713132131601536aaad32e693325Adam Cohen                completeChallengeScroll();
1072db1c5d596e91713132131601536aaad32e693325Adam Cohen            }
1073db1c5d596e91713132131601536aaad32e693325Adam Cohen
1074db1c5d596e91713132131601536aaad32e693325Adam Cohen            mFader = ObjectAnimator.ofFloat(mChallengeView, "alpha", 0f);
1075db1c5d596e91713132131601536aaad32e693325Adam Cohen            mFader.addListener(new AnimatorListenerAdapter() {
1076db1c5d596e91713132131601536aaad32e693325Adam Cohen                @Override
1077db1c5d596e91713132131601536aaad32e693325Adam Cohen                public void onAnimationStart(Animator animation) {
1078db1c5d596e91713132131601536aaad32e693325Adam Cohen                    onFadeStart();
1079db1c5d596e91713132131601536aaad32e693325Adam Cohen                }
1080db1c5d596e91713132131601536aaad32e693325Adam Cohen                @Override
1081db1c5d596e91713132131601536aaad32e693325Adam Cohen                public void onAnimationEnd(Animator animation) {
1082db1c5d596e91713132131601536aaad32e693325Adam Cohen                    onFadeEnd();
1083db1c5d596e91713132131601536aaad32e693325Adam Cohen                }
1084db1c5d596e91713132131601536aaad32e693325Adam Cohen            });
1085db1c5d596e91713132131601536aaad32e693325Adam Cohen            mFader.setDuration(CHALLENGE_FADE_DURATION);
1086db1c5d596e91713132131601536aaad32e693325Adam Cohen            mFader.start();
1087db1c5d596e91713132131601536aaad32e693325Adam Cohen        }
1088db1c5d596e91713132131601536aaad32e693325Adam Cohen    }
1089db1c5d596e91713132131601536aaad32e693325Adam Cohen
1090db1c5d596e91713132131601536aaad32e693325Adam Cohen    private void onFadeStart() {
1091db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeInteractiveInternal = false;
1092db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null);
1093db1c5d596e91713132131601536aaad32e693325Adam Cohen        setScrollState(SCROLL_STATE_FADING);
1094db1c5d596e91713132131601536aaad32e693325Adam Cohen    }
1095db1c5d596e91713132131601536aaad32e693325Adam Cohen
1096db1c5d596e91713132131601536aaad32e693325Adam Cohen    private void onFadeEnd() {
1097db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeInteractiveInternal = true;
1098db1c5d596e91713132131601536aaad32e693325Adam Cohen        setChallengeShowing(false);
1099db1c5d596e91713132131601536aaad32e693325Adam Cohen        moveChallengeTo(getLayoutBottom() + mChallengeView.getMeasuredHeight());
1100db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeView.setAlpha(1f);
1101db1c5d596e91713132131601536aaad32e693325Adam Cohen        mChallengeView.setLayerType(LAYER_TYPE_NONE, null);
1102db1c5d596e91713132131601536aaad32e693325Adam Cohen        mFader = null;
1103db1c5d596e91713132131601536aaad32e693325Adam Cohen        setScrollState(SCROLL_STATE_IDLE);
1104db1c5d596e91713132131601536aaad32e693325Adam Cohen    }
1105db1c5d596e91713132131601536aaad32e693325Adam Cohen
1106d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void showChallenge(int velocity) {
1107d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        boolean show = false;
1108d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (Math.abs(velocity) > mMinVelocity) {
1109d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            show = velocity < 0;
1110d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        } else {
1111d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            show = mChallengeOffset >= 0.5f;
1112d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1113d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        showChallenge(show, velocity);
1114d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1115d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1116d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    private void showChallenge(boolean show, int velocity) {
1117d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mChallengeView == null) {
1118d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            setChallengeShowing(false);
1119d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            return;
1120d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1121d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1122d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        if (mHasLayout) {
112367625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell            final int layoutBottom = getLayoutBottom();
1124d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            animateChallengeTo(show ? layoutBottom :
112567625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell                    layoutBottom + mChallengeView.getHeight() - mChallengeBottomBound, velocity);
1126d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1127d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1128d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
112919a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller    @Override
1130d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
1131d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return new LayoutParams(getContext(), attrs);
1132d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1133d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1134d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
1135d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
1136d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) :
1137d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) :
1138d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                new LayoutParams(p);
1139d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1140d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1141d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
1142d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
1143d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return new LayoutParams();
1144d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1145d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1146d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    @Override
1147d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
1148d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        return p instanceof LayoutParams;
1149d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1150d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1151d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    public static class LayoutParams extends MarginLayoutParams {
1152eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public int childType = CHILD_TYPE_NONE;
1153eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_NONE = 0;
1154eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_CHALLENGE = 2;
1155eee209313d564be37b8fc068f785b3844cd6597eAdam Powell        public static final int CHILD_TYPE_SCRIM = 4;
1156cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell        public static final int CHILD_TYPE_WIDGETS = 5;
115774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov        public static final int CHILD_TYPE_EXPAND_CHALLENGE_HANDLE = 6;
1158d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1159d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams() {
1160d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            this(MATCH_PARENT, WRAP_CONTENT);
1161d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1162d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1163d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(int width, int height) {
1164d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(width, height);
1165d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1166d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1167d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(android.view.ViewGroup.LayoutParams source) {
1168d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
1169d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1170d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1171d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(MarginLayoutParams source) {
1172d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
1173d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1174d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1175d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(LayoutParams source) {
1176d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(source);
1177d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1178eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            childType = source.childType;
1179d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1180d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1181d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        public LayoutParams(Context c, AttributeSet attrs) {
1182d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            super(c, attrs);
1183d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller
1184d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            final TypedArray a = c.obtainStyledAttributes(attrs,
1185d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller                    R.styleable.SlidingChallengeLayout_Layout);
1186eee209313d564be37b8fc068f785b3844cd6597eAdam Powell            childType = a.getInt(R.styleable.SlidingChallengeLayout_Layout_layout_childType,
1187eee209313d564be37b8fc068f785b3844cd6597eAdam Powell                    CHILD_TYPE_NONE);
1188d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller            a.recycle();
1189d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller        }
1190d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller    }
1191d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller}
1192