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