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 175ecd81154fa039961f65bb4e36d18ac555b0d1d6Jim Millerpackage com.android.keyguard; 189c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell 19cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powellimport android.animation.Animator; 20cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powellimport android.animation.AnimatorListenerAdapter; 216f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.animation.ObjectAnimator; 22d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.content.Context; 23fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powellimport android.content.res.Resources; 24d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.content.res.TypedArray; 25d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.graphics.Canvas; 261bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandlerimport android.graphics.Paint; 27256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlockimport android.graphics.Rect; 28d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.AttributeSet; 295da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powellimport android.util.DisplayMetrics; 306f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.util.FloatProperty; 31d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.util.Log; 326f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powellimport android.util.Property; 33d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.MotionEvent; 34d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.VelocityTracker; 35d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.View; 36d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewConfiguration; 37d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.ViewGroup; 3874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganovimport android.view.accessibility.AccessibilityManager; 39d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.view.animation.Interpolator; 40d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Millerimport android.widget.Scroller; 41d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 42d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller/** 43d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * This layout handles interaction with the sliding security challenge views 44d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * that overlay/resize other keyguard contents. 45d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */ 4619a5267003e7dc70100a4bd4f1f449523b2ff38bJim Millerpublic class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout { 47d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private static final String TAG = "SlidingChallengeLayout"; 481bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler private static final boolean DEBUG = false; 49d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 50c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler // The drag handle is measured in dp above & below the top edge of the 515547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller // challenge view; these parameters change based on whether the challenge 52c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler // is open or closed. 539c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell private static final int DRAG_HANDLE_CLOSED_ABOVE = 8; // dp 54cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell private static final int DRAG_HANDLE_CLOSED_BELOW = 0; // dp 55c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler private static final int DRAG_HANDLE_OPEN_ABOVE = 8; // dp 56c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler private static final int DRAG_HANDLE_OPEN_BELOW = 0; // dp 57c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler 5870c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung private static final int HANDLE_ANIMATE_DURATION = 250; // ms 59c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler 60d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // Drawn to show the drag handle in closed state; crossfades to the challenge view 61d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // when challenge is fully visible 62cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell private boolean mEdgeCaptured; 63d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 645da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell private DisplayMetrics mDisplayMetrics; 655da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell 66d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // Initialized during measurement from child layoutparams 6774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov private View mExpandChallengeView; 68052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren private KeyguardSecurityContainer mChallengeView; 69eee209313d564be37b8fc068f785b3844cd6597eAdam Powell private View mScrimView; 70cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell private View mWidgetsView; 71d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 72d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // Range: 0 (fully hidden) to 1 (fully visible) 73d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private float mChallengeOffset = 1.f; 74d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private boolean mChallengeShowing = true; 75da8f622b908121efa4c838936fce26c11a74799dChris Wren private boolean mChallengeShowingTargetState = true; 7603ad0c09502c72c02af464f1e273af3b6ff799ccChris Wren private boolean mWasChallengeShowing = true; 77eee209313d564be37b8fc068f785b3844cd6597eAdam Powell private boolean mIsBouncing = false; 78d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 79d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private final Scroller mScroller; 80db1c5d596e91713132131601536aaad32e693325Adam Cohen private ObjectAnimator mFader; 81d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private int mScrollState; 820b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell private OnChallengeScrolledListener mScrollListener; 830b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell private OnBouncerStateChangedListener mBouncerListener; 845547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller private boolean mEnableChallengeDragging; 85d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 86d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public static final int SCROLL_STATE_IDLE = 0; 87d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public static final int SCROLL_STATE_DRAGGING = 1; 88d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public static final int SCROLL_STATE_SETTLING = 2; 89db1c5d596e91713132131601536aaad32e693325Adam Cohen public static final int SCROLL_STATE_FADING = 3; 90db1c5d596e91713132131601536aaad32e693325Adam Cohen 91c162dd049084681711526b219ea997aa23f5ad98Jim Miller public static final int CHALLENGE_FADE_OUT_DURATION = 100; 92c162dd049084681711526b219ea997aa23f5ad98Jim Miller public static final int CHALLENGE_FADE_IN_DURATION = 160; 93d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 94d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private static final int MAX_SETTLE_DURATION = 600; // ms 95d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 96d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // ID of the pointer in charge of a current drag 97d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private int mActivePointerId = INVALID_POINTER; 98d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private static final int INVALID_POINTER = -1; 99d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 100d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // True if the user is currently dragging the slider 101d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private boolean mDragging; 102d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // True if the user may not drag until a new gesture begins 103d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private boolean mBlockDrag; 104d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 105d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private VelocityTracker mVelocityTracker; 106d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private int mMinVelocity; 107d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private int mMaxVelocity; 108c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler private float mGestureStartX, mGestureStartY; // where did you first touch the screen? 109b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler private int mGestureStartChallengeBottom; // where was the challenge at that time? 110c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler 111c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler private int mDragHandleClosedBelow; // handle hitrect extension into the challenge view 112c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler private int mDragHandleClosedAbove; // extend the handle's hitrect this far above the line 113c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler private int mDragHandleOpenBelow; // handle hitrect extension into the challenge view 114c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler private int mDragHandleOpenAbove; // extend the handle's hitrect this far above the line 115c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler 1160b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell private int mDragHandleEdgeSlop; 11767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell private int mChallengeBottomBound; // Number of pixels from the top of the challenge view 11867625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell // that should remain on-screen 119c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler 120c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler private int mTouchSlop; 12174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov private int mTouchSlopSquare; 122c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler 1236f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell float mHandleAlpha; 1246f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell float mFrameAlpha; 125cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell float mFrameAnimationTarget = Float.MIN_VALUE; 1266f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell private ObjectAnimator mHandleAnimation; 1276f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell private ObjectAnimator mFrameAnimation; 1286f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell 129cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell private boolean mHasGlowpad; 130256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock private final Rect mInsets = new Rect(); 131db1c5d596e91713132131601536aaad32e693325Adam Cohen 132db1c5d596e91713132131601536aaad32e693325Adam Cohen // We have an internal and external version, and we and them together. 133db1c5d596e91713132131601536aaad32e693325Adam Cohen private boolean mChallengeInteractiveExternal = true; 134db1c5d596e91713132131601536aaad32e693325Adam Cohen private boolean mChallengeInteractiveInternal = true; 135cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell 1366f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell static final Property<SlidingChallengeLayout, Float> HANDLE_ALPHA = 1376f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell new FloatProperty<SlidingChallengeLayout>("handleAlpha") { 1386f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell @Override 1396f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell public void setValue(SlidingChallengeLayout view, float value) { 1406f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell view.mHandleAlpha = value; 1416f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell view.invalidate(); 1426f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell } 1436f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell 1446f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell @Override 1456f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell public Float get(SlidingChallengeLayout view) { 1466f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell return view.mHandleAlpha; 1476f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell } 1486f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell }; 1496f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell 150d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // True if at least one layout pass has happened since the view was attached. 151d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private boolean mHasLayout; 152d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 153d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private static final Interpolator sMotionInterpolator = new Interpolator() { 154d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public float getInterpolation(float t) { 155d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller t -= 1.0f; 156d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return t * t * t * t * t + 1.0f; 157d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 158d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller }; 159d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 160d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private static final Interpolator sHandleFadeInterpolator = new Interpolator() { 161d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public float getInterpolation(float t) { 162d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return t * t; 163d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 164d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller }; 165d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 166d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private final Runnable mEndScrollRunnable = new Runnable () { 167d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void run() { 168d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller completeChallengeScroll(); 169d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 170d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller }; 171d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 172eee209313d564be37b8fc068f785b3844cd6597eAdam Powell private final OnClickListener mScrimClickListener = new OnClickListener() { 173eee209313d564be37b8fc068f785b3844cd6597eAdam Powell @Override 174eee209313d564be37b8fc068f785b3844cd6597eAdam Powell public void onClick(View v) { 175eee209313d564be37b8fc068f785b3844cd6597eAdam Powell hideBouncer(); 176eee209313d564be37b8fc068f785b3844cd6597eAdam Powell } 177eee209313d564be37b8fc068f785b3844cd6597eAdam Powell }; 178eee209313d564be37b8fc068f785b3844cd6597eAdam Powell 17974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov private final OnClickListener mExpandChallengeClickListener = new OnClickListener() { 18074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov @Override 18174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov public void onClick(View v) { 18278a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock if (!isChallengeShowing()) { 18374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov showChallenge(true); 18474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 18574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 18674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov }; 18774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov 188d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller /** 189d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Listener interface that reports changes in scroll state of the challenge area. 190d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */ 191d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public interface OnChallengeScrolledListener { 192d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller /** 193d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * The scroll state itself changed. 194d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 195d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * <p>scrollState will be one of the following:</p> 196d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 197d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * <ul> 198d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * <li><code>SCROLL_STATE_IDLE</code> - The challenge area is stationary.</li> 199d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * <li><code>SCROLL_STATE_DRAGGING</code> - The user is actively dragging 200d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * the challenge area.</li> 201d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * <li><code>SCROLL_STATE_SETTLING</code> - The challenge area is animating 202d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * into place.</li> 203d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * </ul> 204d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 205d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * <p>Do not perform expensive operations (e.g. layout) 206d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * while the scroll state is not <code>SCROLL_STATE_IDLE</code>.</p> 207d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 208d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @param scrollState The new scroll state of the challenge area. 209d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */ 210d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void onScrollStateChanged(int scrollState); 211d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 212d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller /** 213d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * The precise position of the challenge area has changed. 214d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 215d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * <p>NOTE: It is NOT safe to modify layout or call any View methods that may 216d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * result in a requestLayout anywhere in your view hierarchy as a result of this call. 217d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * It may be called during drawing.</p> 218d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 219d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @param scrollPosition New relative position of the challenge area. 220d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 1.f = fully visible/ready to be interacted with. 221d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 0.f = fully invisible/inaccessible to the user. 222d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @param challengeTop Position of the top edge of the challenge view in px in the 223d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * SlidingChallengeLayout's coordinate system. 224d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */ 225d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void onScrollPositionChanged(float scrollPosition, int challengeTop); 226d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 227d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 228d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public SlidingChallengeLayout(Context context) { 229d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller this(context, null); 230d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 231d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 232d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public SlidingChallengeLayout(Context context, AttributeSet attrs) { 233d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller this(context, attrs, 0); 234d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 235d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 236d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public SlidingChallengeLayout(Context context, AttributeSet attrs, int defStyle) { 237d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller super(context, attrs, defStyle); 238d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 239d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mScroller = new Scroller(context, sMotionInterpolator); 240d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 241d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final ViewConfiguration vc = ViewConfiguration.get(context); 242d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mMinVelocity = vc.getScaledMinimumFlingVelocity(); 243d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mMaxVelocity = vc.getScaledMaximumFlingVelocity(); 244d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 245fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell final Resources res = getResources(); 246fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell mDragHandleEdgeSlop = res.getDimensionPixelSize(R.dimen.kg_edge_swipe_region_size); 2470b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell 248c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 24974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mTouchSlopSquare = mTouchSlop * mTouchSlop; 250c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler 2515da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell mDisplayMetrics = res.getDisplayMetrics(); 2525da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell final float density = mDisplayMetrics.density; 2531bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler 2541bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler // top half of the lock icon, plus another 25% to be sure 255c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mDragHandleClosedAbove = (int) (DRAG_HANDLE_CLOSED_ABOVE * density + 0.5f); 256c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mDragHandleClosedBelow = (int) (DRAG_HANDLE_CLOSED_BELOW * density + 0.5f); 257c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mDragHandleOpenAbove = (int) (DRAG_HANDLE_OPEN_ABOVE * density + 0.5f); 258c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mDragHandleOpenBelow = (int) (DRAG_HANDLE_OPEN_BELOW * density + 0.5f); 259c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler 260c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler // how much space to account for in the handle when closed 261fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell mChallengeBottomBound = res.getDimensionPixelSize(R.dimen.kg_widget_pager_bottom_padding); 2621bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler 263cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell setWillNotDraw(false); 264bd95740648372449a4d5c164d7050eee352d4c24John Spurlock setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_STABLE | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 265cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell } 266cfc3086b18396df03f982fb98112649dc5b5a6f2Adam Powell 2675547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller public void setEnableChallengeDragging(boolean enabled) { 2685547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller mEnableChallengeDragging = enabled; 2695547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller } 2705547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller 271256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock public void setInsets(Rect insets) { 272256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock mInsets.set(insets); 273256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock } 274256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock 2756e36f1019cb6d7098b49e036e1640c8cfc6880cfJohn Spurlock public void setHandleAlpha(float alpha) { 2766e36f1019cb6d7098b49e036e1640c8cfc6880cfJohn Spurlock if (mExpandChallengeView != null) { 2776e36f1019cb6d7098b49e036e1640c8cfc6880cfJohn Spurlock mExpandChallengeView.setAlpha(alpha); 2786f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell } 2796f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell } 2806f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell 281bb5c941149b66c0192736468bb60f47984dd5e1fJohn Spurlock public void setChallengeInteractive(boolean interactive) { 282db1c5d596e91713132131601536aaad32e693325Adam Cohen mChallengeInteractiveExternal = interactive; 28378a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock if (mExpandChallengeView != null) { 28478a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock mExpandChallengeView.setEnabled(interactive); 28578a8f126a9af0c4994188adeba1b955b5d4afd28John Spurlock } 286bb5c941149b66c0192736468bb60f47984dd5e1fJohn Spurlock } 287bb5c941149b66c0192736468bb60f47984dd5e1fJohn Spurlock 2886f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell void animateHandle(boolean visible) { 2896f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell if (mHandleAnimation != null) { 2906f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell mHandleAnimation.cancel(); 29167625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell mHandleAnimation = null; 292d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 29367625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell final float targetAlpha = visible ? 1.f : 0.f; 29467625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell if (targetAlpha == mHandleAlpha) { 29567625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell return; 29667625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell } 29767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, targetAlpha); 2986f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell mHandleAnimation.setInterpolator(sHandleFadeInterpolator); 2996f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell mHandleAnimation.setDuration(HANDLE_ANIMATE_DURATION); 3006f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell mHandleAnimation.start(); 3016f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell } 3026f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell 303d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private void sendInitialListenerUpdates() { 3040b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell if (mScrollListener != null) { 305d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller int challengeTop = mChallengeView != null ? mChallengeView.getTop() : 0; 3060b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell mScrollListener.onScrollPositionChanged(mChallengeOffset, challengeTop); 3070b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell mScrollListener.onScrollStateChanged(mScrollState); 308d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 309d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 310d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 311d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void setOnChallengeScrolledListener(OnChallengeScrolledListener listener) { 3120b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell mScrollListener = listener; 313d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mHasLayout) { 314d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller sendInitialListenerUpdates(); 315d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 316d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 317d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 3180b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell public void setOnBouncerStateChangedListener(OnBouncerStateChangedListener listener) { 3190b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell mBouncerListener = listener; 3200b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell } 3210b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell 322d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 323d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void onAttachedToWindow() { 324d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller super.onAttachedToWindow(); 325d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 326d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mHasLayout = false; 327d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 328d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 329d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 330d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void onDetachedFromWindow() { 331d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller super.onDetachedFromWindow(); 332d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 333d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller removeCallbacks(mEndScrollRunnable); 334d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mHasLayout = false; 335d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 336d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 3370b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell @Override 3380b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell public void requestChildFocus(View child, View focused) { 3390b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell if (mIsBouncing && child != mChallengeView) { 3400b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell // Clear out of the bouncer if the user tries to move focus outside of 3410b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell // the security challenge view. 3420b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell hideBouncer(); 3430b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell } 3440b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell super.requestChildFocus(child, focused); 3450b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell } 3460b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell 347d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // We want the duration of the page snap animation to be influenced by the distance that 348d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // the screen has to travel, however, we don't want this duration to be effected in a 349d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // purely linear fashion. Instead, we use this method to moderate the effect that the distance 350d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // of travel has on the overall snap duration. 351d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller float distanceInfluenceForSnapDuration(float f) { 352d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller f -= 0.5f; // center the values about 0. 353d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller f *= 0.3f * Math.PI / 2.0f; 354d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return (float) Math.sin(f); 355d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 356d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 357d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller void setScrollState(int state) { 358d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mScrollState != state) { 359d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mScrollState = state; 360d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 3616f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing); 3620b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell if (mScrollListener != null) { 3630b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell mScrollListener.onScrollStateChanged(state); 364d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 365d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 366d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 367d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 368d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller void completeChallengeScroll() { 369da8f622b908121efa4c838936fce26c11a74799dChris Wren setChallengeShowing(mChallengeShowingTargetState); 370da8f622b908121efa4c838936fce26c11a74799dChris Wren mChallengeOffset = mChallengeShowing ? 1.f : 0.f; 371d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller setScrollState(SCROLL_STATE_IDLE); 372db1c5d596e91713132131601536aaad32e693325Adam Cohen mChallengeInteractiveInternal = true; 373cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell mChallengeView.setLayerType(LAYER_TYPE_NONE, null); 374d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 375d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 376eee209313d564be37b8fc068f785b3844cd6597eAdam Powell void setScrimView(View scrim) { 377eee209313d564be37b8fc068f785b3844cd6597eAdam Powell if (mScrimView != null) { 378eee209313d564be37b8fc068f785b3844cd6597eAdam Powell mScrimView.setOnClickListener(null); 379eee209313d564be37b8fc068f785b3844cd6597eAdam Powell } 380eee209313d564be37b8fc068f785b3844cd6597eAdam Powell mScrimView = scrim; 381257f283b0e4cc1c1fa4e7f2472d3ee4ab3b451d0John Spurlock if (mScrimView != null) { 382257f283b0e4cc1c1fa4e7f2472d3ee4ab3b451d0John Spurlock mScrimView.setVisibility(mIsBouncing ? VISIBLE : GONE); 383257f283b0e4cc1c1fa4e7f2472d3ee4ab3b451d0John Spurlock mScrimView.setFocusable(true); 384257f283b0e4cc1c1fa4e7f2472d3ee4ab3b451d0John Spurlock mScrimView.setOnClickListener(mScrimClickListener); 385257f283b0e4cc1c1fa4e7f2472d3ee4ab3b451d0John Spurlock } 386eee209313d564be37b8fc068f785b3844cd6597eAdam Powell } 387eee209313d564be37b8fc068f785b3844cd6597eAdam Powell 388d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller /** 389d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Animate the bottom edge of the challenge view to the given position. 390d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 391d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @param y desired final position for the bottom edge of the challenge view in px 392d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @param velocity velocity in 393d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */ 394d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller void animateChallengeTo(int y, int velocity) { 395d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mChallengeView == null) { 396d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // Nothing to do. 397d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return; 398d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 399db1c5d596e91713132131601536aaad32e693325Adam Cohen 4008caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen cancelTransitionsInProgress(); 401db1c5d596e91713132131601536aaad32e693325Adam Cohen 402db1c5d596e91713132131601536aaad32e693325Adam Cohen mChallengeInteractiveInternal = false; 4030a4079ec0c11a3fa28ed2a82781a482372015818John Reck enableHardwareLayerForChallengeView(); 40467625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell final int sy = mChallengeView.getBottom(); 40567625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell final int dy = y - sy; 406d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (dy == 0) { 407d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller completeChallengeScroll(); 408d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return; 409d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 410d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 411d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller setScrollState(SCROLL_STATE_SETTLING); 412d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 413d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int childHeight = mChallengeView.getHeight(); 414d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int halfHeight = childHeight / 2; 415d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final float distanceRatio = Math.min(1f, 1.0f * Math.abs(dy) / childHeight); 416d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final float distance = halfHeight + halfHeight * 417d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller distanceInfluenceForSnapDuration(distanceRatio); 418d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 419d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller int duration = 0; 420d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller velocity = Math.abs(velocity); 421d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (velocity > 0) { 422d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller duration = 4 * Math.round(1000 * Math.abs(distance / velocity)); 423d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } else { 424d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final float childDelta = (float) Math.abs(dy) / childHeight; 425d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller duration = (int) ((childDelta + 1) * 100); 426d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 427d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller duration = Math.min(duration, MAX_SETTLE_DURATION); 428d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 429d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mScroller.startScroll(0, sy, 0, dy, duration); 430d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller postInvalidateOnAnimation(); 431d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 432d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 433d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private void setChallengeShowing(boolean showChallenge) { 43474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (mChallengeShowing == showChallenge) { 43574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov return; 43674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 43767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell mChallengeShowing = showChallenge; 4385a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell 4395a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell if (mExpandChallengeView == null || mChallengeView == null) { 4405a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell // These might not be here yet if we haven't been through layout. 4415a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell // If we haven't, the first layout pass will set everything up correctly 4425a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell // based on mChallengeShowing as set above. 4435a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell return; 4445a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell } 4455a2c5f0efacf5f11a928c8481f6747c3d13ddcfdAdam Powell 44674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (mChallengeShowing) { 44774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mExpandChallengeView.setVisibility(View.INVISIBLE); 44874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mChallengeView.setVisibility(View.VISIBLE); 44974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (AccessibilityManager.getInstance(mContext).isEnabled()) { 45074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mChallengeView.requestAccessibilityFocus(); 45174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mChallengeView.announceForAccessibility(mContext.getString( 45274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov R.string.keyguard_accessibility_unlock_area_expanded)); 45374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 45474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } else { 45574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mExpandChallengeView.setVisibility(View.VISIBLE); 45674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mChallengeView.setVisibility(View.INVISIBLE); 45774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (AccessibilityManager.getInstance(mContext).isEnabled()) { 45874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mExpandChallengeView.requestAccessibilityFocus(); 45974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mChallengeView.announceForAccessibility(mContext.getString( 46074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov R.string.keyguard_accessibility_unlock_area_collapsed)); 46174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 46274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 463d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 464d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 465d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller /** 466d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @return true if the challenge is at all visible. 467d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */ 468d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public boolean isChallengeShowing() { 469d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return mChallengeShowing; 470d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 471d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 472d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 47319a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller public boolean isChallengeOverlapping() { 47419a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller return mChallengeShowing; 47519a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller } 47619a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller 47719a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller @Override 478eee209313d564be37b8fc068f785b3844cd6597eAdam Powell public boolean isBouncing() { 479eee209313d564be37b8fc068f785b3844cd6597eAdam Powell return mIsBouncing; 480eee209313d564be37b8fc068f785b3844cd6597eAdam Powell } 481eee209313d564be37b8fc068f785b3844cd6597eAdam Powell 482eee209313d564be37b8fc068f785b3844cd6597eAdam Powell @Override 48370c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung public int getBouncerAnimationDuration() { 48470c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung return HANDLE_ANIMATE_DURATION; 48570c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung } 48670c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung 48770c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung @Override 488eee209313d564be37b8fc068f785b3844cd6597eAdam Powell public void showBouncer() { 489eee209313d564be37b8fc068f785b3844cd6597eAdam Powell if (mIsBouncing) return; 490e657a82ee1a53e42cced3159dd7782ca052ea9f6Jim Miller setSystemUiVisibility(getSystemUiVisibility() | STATUS_BAR_DISABLE_SEARCH); 49103ad0c09502c72c02af464f1e273af3b6ff799ccChris Wren mWasChallengeShowing = mChallengeShowing; 49203ad0c09502c72c02af464f1e273af3b6ff799ccChris Wren mIsBouncing = true; 4936cf53bb3a94ec5a6eebc96a315dac32d042f1945Winson Chung showChallenge(true); 494eee209313d564be37b8fc068f785b3844cd6597eAdam Powell if (mScrimView != null) { 49570c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 1f); 49670c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung anim.setDuration(HANDLE_ANIMATE_DURATION); 49770c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung anim.addListener(new AnimatorListenerAdapter() { 49870c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung @Override 49970c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung public void onAnimationStart(Animator animation) { 50070c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung mScrimView.setVisibility(VISIBLE); 50170c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung } 50270c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung }); 50370c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung anim.start(); 504eee209313d564be37b8fc068f785b3844cd6597eAdam Powell } 505052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren if (mChallengeView != null) { 506052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren mChallengeView.showBouncer(HANDLE_ANIMATE_DURATION); 507052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren } 508cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell 5090b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell if (mBouncerListener != null) { 5100b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell mBouncerListener.onBouncerStateChanged(true); 5110b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell } 512eee209313d564be37b8fc068f785b3844cd6597eAdam Powell } 513eee209313d564be37b8fc068f785b3844cd6597eAdam Powell 514eee209313d564be37b8fc068f785b3844cd6597eAdam Powell @Override 515eee209313d564be37b8fc068f785b3844cd6597eAdam Powell public void hideBouncer() { 516eee209313d564be37b8fc068f785b3844cd6597eAdam Powell if (!mIsBouncing) return; 517e657a82ee1a53e42cced3159dd7782ca052ea9f6Jim Miller setSystemUiVisibility(getSystemUiVisibility() & ~STATUS_BAR_DISABLE_SEARCH); 51803ad0c09502c72c02af464f1e273af3b6ff799ccChris Wren if (!mWasChallengeShowing) showChallenge(false); 519eee209313d564be37b8fc068f785b3844cd6597eAdam Powell mIsBouncing = false; 52070c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung 521eee209313d564be37b8fc068f785b3844cd6597eAdam Powell if (mScrimView != null) { 52270c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung Animator anim = ObjectAnimator.ofFloat(mScrimView, "alpha", 0f); 52370c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung anim.setDuration(HANDLE_ANIMATE_DURATION); 52470c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung anim.addListener(new AnimatorListenerAdapter() { 52570c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung @Override 52670c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung public void onAnimationEnd(Animator animation) { 52770c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung mScrimView.setVisibility(GONE); 52870c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung } 52970c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung }); 53070c2f8736437ebb6073c3b6dab2e08e6dd9f39a6Winson Chung anim.start(); 531eee209313d564be37b8fc068f785b3844cd6597eAdam Powell } 532052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren if (mChallengeView != null) { 533052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren mChallengeView.hideBouncer(HANDLE_ANIMATE_DURATION); 534052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren } 5350b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell if (mBouncerListener != null) { 5360b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell mBouncerListener.onBouncerStateChanged(false); 5370b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell } 538eee209313d564be37b8fc068f785b3844cd6597eAdam Powell } 539eee209313d564be37b8fc068f785b3844cd6597eAdam Powell 540cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell private int getChallengeMargin(boolean expanded) { 541cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell return expanded && mHasGlowpad ? 0 : mDragHandleEdgeSlop; 542cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell } 543cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell 544fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell private float getChallengeAlpha() { 545fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell float x = mChallengeOffset - 1; 546fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell return x * x * x + 1.f; 547fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell } 548fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell 549eee209313d564be37b8fc068f785b3844cd6597eAdam Powell @Override 550d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void requestDisallowInterceptTouchEvent(boolean allowIntercept) { 551d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // We'll intercept whoever we feel like! ...as long as it isn't a challenge view. 552d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // If there are one or more pointers in the challenge view before we take over 553d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // touch events, onInterceptTouchEvent will set mBlockDrag. 554d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 555d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 556d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 557d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public boolean onInterceptTouchEvent(MotionEvent ev) { 558d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mVelocityTracker == null) { 559d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mVelocityTracker = VelocityTracker.obtain(); 560d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 561d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mVelocityTracker.addMovement(ev); 562d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 563d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int action = ev.getActionMasked(); 564d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller switch (action) { 565022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller case MotionEvent.ACTION_DOWN: 566c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mGestureStartX = ev.getX(); 567b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler mGestureStartY = ev.getY(); 568995270f01175234d0615a0e1900576f142a3b4adAdam Powell mBlockDrag = false; 569022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller break; 570022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller 571d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller case MotionEvent.ACTION_CANCEL: 572d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller case MotionEvent.ACTION_UP: 573d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller resetTouch(); 574d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller break; 575d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 576d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller case MotionEvent.ACTION_MOVE: 577d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int count = ev.getPointerCount(); 578d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller for (int i = 0; i < count; i++) { 579d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final float x = ev.getX(i); 580d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final float y = ev.getY(i); 581db1c5d596e91713132131601536aaad32e693325Adam Cohen if (!mIsBouncing && mActivePointerId == INVALID_POINTER 5829c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell && (crossedDragHandle(x, y, mGestureStartY) 5835547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller && shouldEnableChallengeDragging() 5845547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller || (isInChallengeView(x, y) && 5859c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell mScrollState == SCROLL_STATE_SETTLING))) { 586d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mActivePointerId = ev.getPointerId(i); 587c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mGestureStartX = x; 588c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mGestureStartY = y; 589b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler mGestureStartChallengeBottom = getChallengeBottom(); 590d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mDragging = true; 5910a4079ec0c11a3fa28ed2a82781a482372015818John Reck enableHardwareLayerForChallengeView(); 5925547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller } else if (mChallengeShowing && isInChallengeView(x, y) 5935547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller && shouldEnableChallengeDragging()) { 594d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mBlockDrag = true; 595d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 596d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 597d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller break; 598d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 599d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 600db1c5d596e91713132131601536aaad32e693325Adam Cohen if (mBlockDrag || isChallengeInteractionBlocked()) { 601d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mActivePointerId = INVALID_POINTER; 602d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mDragging = false; 603d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 604d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 605d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return mDragging; 606d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 607d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 6085547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller private boolean shouldEnableChallengeDragging() { 6095547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller return mEnableChallengeDragging || !mChallengeShowing; 6105547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller } 6115547b55bae92e6674e528d671f7700d7f5a9b3d0Jim Miller 612db1c5d596e91713132131601536aaad32e693325Adam Cohen private boolean isChallengeInteractionBlocked() { 613db1c5d596e91713132131601536aaad32e693325Adam Cohen return !mChallengeInteractiveExternal || !mChallengeInteractiveInternal; 614db1c5d596e91713132131601536aaad32e693325Adam Cohen } 615db1c5d596e91713132131601536aaad32e693325Adam Cohen 616d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private void resetTouch() { 617d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mVelocityTracker.recycle(); 618d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mVelocityTracker = null; 619d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mActivePointerId = INVALID_POINTER; 620d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mDragging = mBlockDrag = false; 621d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 622d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 623d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 624d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public boolean onTouchEvent(MotionEvent ev) { 625d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mVelocityTracker == null) { 626d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mVelocityTracker = VelocityTracker.obtain(); 627d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 628d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mVelocityTracker.addMovement(ev); 629d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 630d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int action = ev.getActionMasked(); 631d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller switch (action) { 632995270f01175234d0615a0e1900576f142a3b4adAdam Powell case MotionEvent.ACTION_DOWN: 633995270f01175234d0615a0e1900576f142a3b4adAdam Powell mBlockDrag = false; 634c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mGestureStartX = ev.getX(); 635995270f01175234d0615a0e1900576f142a3b4adAdam Powell mGestureStartY = ev.getY(); 636995270f01175234d0615a0e1900576f142a3b4adAdam Powell break; 637995270f01175234d0615a0e1900576f142a3b4adAdam Powell 638d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller case MotionEvent.ACTION_CANCEL: 639db1c5d596e91713132131601536aaad32e693325Adam Cohen if (mDragging && !isChallengeInteractionBlocked()) { 640d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller showChallenge(0); 641d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 642d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller resetTouch(); 643d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller break; 644d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 645d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller case MotionEvent.ACTION_POINTER_UP: 646d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mActivePointerId != ev.getPointerId(ev.getActionIndex())) { 647d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller break; 648d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 649d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller case MotionEvent.ACTION_UP: 650db1c5d596e91713132131601536aaad32e693325Adam Cohen if (mDragging && !isChallengeInteractionBlocked()) { 651d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity); 652d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller showChallenge((int) mVelocityTracker.getYVelocity(mActivePointerId)); 653d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 654d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller resetTouch(); 655d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller break; 656d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 657d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller case MotionEvent.ACTION_MOVE: 6586f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell if (!mDragging && !mBlockDrag && !mIsBouncing) { 659d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int count = ev.getPointerCount(); 660d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller for (int i = 0; i < count; i++) { 661d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final float x = ev.getX(i); 662d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final float y = ev.getY(i); 663d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 664b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) || 665d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) 6661ec464679f731827f626856084f19643fff64a94John Spurlock && mActivePointerId == INVALID_POINTER 667db1c5d596e91713132131601536aaad32e693325Adam Cohen && !isChallengeInteractionBlocked()) { 668c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mGestureStartX = x; 669b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler mGestureStartY = y; 670d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mActivePointerId = ev.getPointerId(i); 671b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler mGestureStartChallengeBottom = getChallengeBottom(); 672d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mDragging = true; 6730a4079ec0c11a3fa28ed2a82781a482372015818John Reck enableHardwareLayerForChallengeView(); 674d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller break; 675d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 676d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 677d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 678d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // Not an else; this can be set above. 679d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mDragging) { 680d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // No-op if already in this state, but set it here in case we arrived 681d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // at this point from either intercept or the above. 682d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller setScrollState(SCROLL_STATE_DRAGGING); 683d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 684d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int index = ev.findPointerIndex(mActivePointerId); 685d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (index < 0) { 686d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // Oops, bogus state. We lost some touch events somewhere. 687d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // Just drop it with no velocity and let things settle. 688d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller resetTouch(); 689d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller showChallenge(0); 690d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return true; 691d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 692b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler final float y = ev.getY(index); 693b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler final float pos = Math.min(y - mGestureStartY, 69467625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell getLayoutBottom() - mChallengeBottomBound); 695b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler 696b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler moveChallengeTo(mGestureStartChallengeBottom + (int) pos); 697d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 698d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller break; 699d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 700d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return true; 701d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 702d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 703b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler /** 704cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell * The lifecycle of touch events is subtle and it's very easy to do something 705cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell * that will cause bugs that will be nasty to track when overriding this method. 706cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell * Normally one should always override onInterceptTouchEvent instead. 707cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell * 708cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell * To put it another way, don't try this at home. 709cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell */ 710cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell @Override 711cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell public boolean dispatchTouchEvent(MotionEvent ev) { 712cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell final int action = ev.getActionMasked(); 713cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell boolean handled = false; 714cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell if (action == MotionEvent.ACTION_DOWN) { 715cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell // Defensive programming: if we didn't get the UP or CANCEL, reset anyway. 716cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell mEdgeCaptured = false; 717cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell } 718cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell if (mWidgetsView != null && !mIsBouncing && (mEdgeCaptured || isEdgeSwipeBeginEvent(ev))) { 719cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell // Normally we would need to do a lot of extra stuff here. 720cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell // We can only get away with this because we haven't padded in 721cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell // the widget pager or otherwise transformed it during layout. 722cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell // We also don't support things like splitting MotionEvents. 723cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell 724cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell // We set handled to captured even if dispatch is returning false here so that 725cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell // we don't send a different view a busted or incomplete event stream. 726cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell handled = mEdgeCaptured |= mWidgetsView.dispatchTouchEvent(ev); 727cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell } 728cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell 729cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell if (!handled && !mEdgeCaptured) { 730cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell handled = super.dispatchTouchEvent(ev); 731cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell } 732cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell 733cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { 734cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell mEdgeCaptured = false; 735cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell } 736cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell 737cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell return handled; 738cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell } 739cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell 740cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell private boolean isEdgeSwipeBeginEvent(MotionEvent ev) { 741cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) { 742cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell return false; 743cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell } 744cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell 745cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell final float x = ev.getX(); 746cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell return x < mDragHandleEdgeSlop || x >= getWidth() - mDragHandleEdgeSlop; 747cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell } 748cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell 749cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell /** 750b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler * We only want to add additional vertical space to the drag handle when the panel is fully 751b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler * closed. 752b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler */ 753c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler private int getDragHandleSizeAbove() { 754c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler return isChallengeShowing() ? mDragHandleOpenAbove : mDragHandleClosedAbove; 755c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler } 756c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler private int getDragHandleSizeBelow() { 757c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler return isChallengeShowing() ? mDragHandleOpenBelow : mDragHandleClosedBelow; 758b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler } 759b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler 760d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private boolean isInChallengeView(float x, float y) { 76174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov return isPointInView(x, y, mChallengeView); 762d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 763d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 764d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private boolean isInDragHandle(float x, float y) { 76574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov return isPointInView(x, y, mExpandChallengeView); 76674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 767d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 76874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov private boolean isPointInView(float x, float y, View view) { 76974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (view == null) { 77074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov return false; 77174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 77274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov return x >= view.getLeft() && y >= view.getTop() 77374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov && x < view.getRight() && y < view.getBottom(); 774d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 775d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 776b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler private boolean crossedDragHandle(float x, float y, float initialY) { 7779c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell 778022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller final int challengeTop = mChallengeView.getTop(); 7799c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell final boolean horizOk = x >= 0 && x < getWidth(); 7809c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell 7819c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell final boolean vertOk; 7829c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell if (mChallengeShowing) { 7839c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell vertOk = initialY < (challengeTop - getDragHandleSizeAbove()) && 7849c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell y > challengeTop + getDragHandleSizeBelow(); 7859c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell } else { 7869c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell vertOk = initialY > challengeTop + getDragHandleSizeBelow() && 7879c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell y < challengeTop - getDragHandleSizeAbove(); 7889c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell } 7899c2c77f8bd8288dcafc9f8111396d46b896d1933Adam Powell return horizOk && vertOk; 790022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller } 791022554e3da6e496524387a5d3ecc67ef9749b587Jim Miller 7929799714f6c69efe50e21a6073b879140e5e792b7Adam Powell private int makeChildMeasureSpec(int maxSize, int childDimen) { 7939799714f6c69efe50e21a6073b879140e5e792b7Adam Powell final int mode; 7949799714f6c69efe50e21a6073b879140e5e792b7Adam Powell final int size; 7959799714f6c69efe50e21a6073b879140e5e792b7Adam Powell switch (childDimen) { 7969799714f6c69efe50e21a6073b879140e5e792b7Adam Powell case LayoutParams.WRAP_CONTENT: 7979799714f6c69efe50e21a6073b879140e5e792b7Adam Powell mode = MeasureSpec.AT_MOST; 7989799714f6c69efe50e21a6073b879140e5e792b7Adam Powell size = maxSize; 7999799714f6c69efe50e21a6073b879140e5e792b7Adam Powell break; 8009799714f6c69efe50e21a6073b879140e5e792b7Adam Powell case LayoutParams.MATCH_PARENT: 8019799714f6c69efe50e21a6073b879140e5e792b7Adam Powell mode = MeasureSpec.EXACTLY; 8029799714f6c69efe50e21a6073b879140e5e792b7Adam Powell size = maxSize; 8039799714f6c69efe50e21a6073b879140e5e792b7Adam Powell break; 8049799714f6c69efe50e21a6073b879140e5e792b7Adam Powell default: 8059799714f6c69efe50e21a6073b879140e5e792b7Adam Powell mode = MeasureSpec.EXACTLY; 8069799714f6c69efe50e21a6073b879140e5e792b7Adam Powell size = Math.min(maxSize, childDimen); 8079799714f6c69efe50e21a6073b879140e5e792b7Adam Powell break; 8089799714f6c69efe50e21a6073b879140e5e792b7Adam Powell } 8099799714f6c69efe50e21a6073b879140e5e792b7Adam Powell return MeasureSpec.makeMeasureSpec(size, mode); 8109799714f6c69efe50e21a6073b879140e5e792b7Adam Powell } 8119799714f6c69efe50e21a6073b879140e5e792b7Adam Powell 812d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 813d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller protected void onMeasure(int widthSpec, int heightSpec) { 814d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (MeasureSpec.getMode(widthSpec) != MeasureSpec.EXACTLY || 815d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller MeasureSpec.getMode(heightSpec) != MeasureSpec.EXACTLY) { 816d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller throw new IllegalArgumentException( 817d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller "SlidingChallengeLayout must be measured with an exact size"); 818d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 819d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int width = MeasureSpec.getSize(widthSpec); 820d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int height = MeasureSpec.getSize(heightSpec); 821d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller setMeasuredDimension(width, height); 822d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 823256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock final int insetHeight = height - mInsets.top - mInsets.bottom; 824256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock final int insetHeightSpec = MeasureSpec.makeMeasureSpec(insetHeight, MeasureSpec.EXACTLY); 825256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock 826d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // Find one and only one challenge view. 827d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final View oldChallengeView = mChallengeView; 82874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov final View oldExpandChallengeView = mChallengeView; 829d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mChallengeView = null; 83074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mExpandChallengeView = null; 831d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int count = getChildCount(); 8328a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen 8338a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen // First iteration through the children finds special children and sets any associated 8348a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen // state. 835d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller for (int i = 0; i < count; i++) { 836d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final View child = getChildAt(i); 837d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 838eee209313d564be37b8fc068f785b3844cd6597eAdam Powell if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) { 839d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mChallengeView != null) { 840d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller throw new IllegalStateException( 841d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller "There may only be one child with layout_isChallenge=\"true\""); 842d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 843052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren if (!(child instanceof KeyguardSecurityContainer)) { 844052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren throw new IllegalArgumentException( 845052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren "Challenge must be a KeyguardSecurityContainer"); 846052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren } 847052999f3c94df2659e6e3e7730a2810980f718f7Chris Wren mChallengeView = (KeyguardSecurityContainer) child; 848d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mChallengeView != oldChallengeView) { 849d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mChallengeView.setVisibility(mChallengeShowing ? VISIBLE : INVISIBLE); 850d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 8516f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell // We're going to play silly games with the frame's background drawable later. 852cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell if (!mHasLayout) { 853cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell // Set up the margin correctly based on our content for the first run. 854cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell mHasGlowpad = child.findViewById(R.id.keyguard_selector_view) != null; 855cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell lp.leftMargin = lp.rightMargin = getChallengeMargin(true); 856cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell } 85774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) { 85874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (mExpandChallengeView != null) { 85974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov throw new IllegalStateException( 86074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov "There may only be one child with layout_childType" 86174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov + "=\"expandChallengeHandle\""); 86274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 86374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mExpandChallengeView = child; 86474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (mExpandChallengeView != oldExpandChallengeView) { 86574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mExpandChallengeView.setVisibility(mChallengeShowing ? INVISIBLE : VISIBLE); 86674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mExpandChallengeView.setOnClickListener(mExpandChallengeClickListener); 86774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 868eee209313d564be37b8fc068f785b3844cd6597eAdam Powell } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) { 869eee209313d564be37b8fc068f785b3844cd6597eAdam Powell setScrimView(child); 870cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell } else if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) { 871cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell mWidgetsView = child; 872d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 8738a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen } 8748a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen 8755be14ded6762bc51fabbbecb1b2ec01a8c758631Adam Cohen // We want to measure the challenge view first, since the KeyguardWidgetPager 8765be14ded6762bc51fabbbecb1b2ec01a8c758631Adam Cohen // needs to do things its measure pass that are dependent on the challenge view 8775be14ded6762bc51fabbbecb1b2ec01a8c758631Adam Cohen // having been measured. 87874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (mChallengeView != null && mChallengeView.getVisibility() != View.GONE) { 8799799714f6c69efe50e21a6073b879140e5e792b7Adam Powell // This one's a little funny. If the IME is present - reported in the form 8809799714f6c69efe50e21a6073b879140e5e792b7Adam Powell // of insets on the root view - we only give the challenge the space it would 8819799714f6c69efe50e21a6073b879140e5e792b7Adam Powell // have had if the IME wasn't there in order to keep the rest of the layout stable. 8829799714f6c69efe50e21a6073b879140e5e792b7Adam Powell // We base this on the layout_maxHeight on the challenge view. If it comes out 8839799714f6c69efe50e21a6073b879140e5e792b7Adam Powell // negative or zero, either we didn't have a maxHeight or we're totally out of space, 8849799714f6c69efe50e21a6073b879140e5e792b7Adam Powell // so give up and measure as if this rule weren't there. 885256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock int challengeHeightSpec = insetHeightSpec; 8869799714f6c69efe50e21a6073b879140e5e792b7Adam Powell final View root = getRootView(); 8879799714f6c69efe50e21a6073b879140e5e792b7Adam Powell if (root != null) { 8889799714f6c69efe50e21a6073b879140e5e792b7Adam Powell final LayoutParams lp = (LayoutParams) mChallengeView.getLayoutParams(); 889256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock final int windowHeight = mDisplayMetrics.heightPixels 890256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock - root.getPaddingTop() - mInsets.top; 891256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock final int diff = windowHeight - insetHeight; 8929799714f6c69efe50e21a6073b879140e5e792b7Adam Powell final int maxChallengeHeight = lp.maxHeight - diff; 8939799714f6c69efe50e21a6073b879140e5e792b7Adam Powell if (maxChallengeHeight > 0) { 8949799714f6c69efe50e21a6073b879140e5e792b7Adam Powell challengeHeightSpec = makeChildMeasureSpec(maxChallengeHeight, lp.height); 8959799714f6c69efe50e21a6073b879140e5e792b7Adam Powell } 8969799714f6c69efe50e21a6073b879140e5e792b7Adam Powell } 8979799714f6c69efe50e21a6073b879140e5e792b7Adam Powell measureChildWithMargins(mChallengeView, widthSpec, 0, challengeHeightSpec, 0); 8988a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen } 899d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 9008a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen // Measure the rest of the children 9018a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen for (int i = 0; i < count; i++) { 9028a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen final View child = getChildAt(i); 90374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (child.getVisibility() == GONE) { 90474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov continue; 90574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 9068a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen // Don't measure the challenge view twice! 9075da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell if (child == mChallengeView) continue; 9085da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell 9095da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell // Measure children. Widget frame measures special, so that we can ignore 9105da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell // insets for the IME. 911256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock int parentWidthSpec = widthSpec, parentHeightSpec = insetHeightSpec; 9125da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 9135da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell if (lp.childType == LayoutParams.CHILD_TYPE_WIDGETS) { 9145da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell final View root = getRootView(); 9155da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell if (root != null) { 9165da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell // This calculation is super dodgy and relies on several assumptions. 9175da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell // Specifically that the root of the window will be padded in for insets 9185da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell // and that the window is LAYOUT_IN_SCREEN. 9195da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell final int windowWidth = mDisplayMetrics.widthPixels; 920256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock final int windowHeight = mDisplayMetrics.heightPixels 921256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock - root.getPaddingTop() - mInsets.top; 9225da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell parentWidthSpec = MeasureSpec.makeMeasureSpec( 9235da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell windowWidth, MeasureSpec.EXACTLY); 9245da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell parentHeightSpec = MeasureSpec.makeMeasureSpec( 9255da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell windowHeight, MeasureSpec.EXACTLY); 9265da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell } 927256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) { 928256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock // Allow scrim views to extend into the insets 929256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock parentWidthSpec = widthSpec; 930256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock parentHeightSpec = heightSpec; 9318a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen } 9325da6430fff7812e58ee7e9a05c62d72c36f366c8Adam Powell measureChildWithMargins(child, parentWidthSpec, 0, parentHeightSpec, 0); 933d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 934d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 935d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 936d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 937d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller protected void onLayout(boolean changed, int l, int t, int r, int b) { 938d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int paddingLeft = getPaddingLeft(); 939d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int paddingTop = getPaddingTop(); 940d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int paddingRight = getPaddingRight(); 941d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int paddingBottom = getPaddingBottom(); 942d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int width = r - l; 943d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int height = b - t; 944d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 945d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int count = getChildCount(); 946d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller for (int i = 0; i < count; i++) { 947d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final View child = getChildAt(i); 948d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 949d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (child.getVisibility() == GONE) continue; 950d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 951d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final LayoutParams lp = (LayoutParams) child.getLayoutParams(); 952d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 953eee209313d564be37b8fc068f785b3844cd6597eAdam Powell if (lp.childType == LayoutParams.CHILD_TYPE_CHALLENGE) { 954d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // Challenge views pin to the bottom, offset by a portion of their height, 955d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // and center horizontally. 956d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int center = (paddingLeft + width - paddingRight) / 2; 957d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int childWidth = child.getMeasuredWidth(); 958d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int childHeight = child.getMeasuredHeight(); 959d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int left = center - childWidth / 2; 960256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock final int layoutBottom = height - paddingBottom - lp.bottomMargin - mInsets.bottom; 961d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // We use the top of the challenge view to position the handle, so 962d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // we never want less than the handle size showing at the bottom. 96367625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell final int bottom = layoutBottom + (int) ((childHeight - mChallengeBottomBound) 964d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * (1 - mChallengeOffset)); 965fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell child.setAlpha(getChallengeAlpha()); 966d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller child.layout(left, bottom - childHeight, left + childWidth, bottom); 96774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } else if (lp.childType == LayoutParams.CHILD_TYPE_EXPAND_CHALLENGE_HANDLE) { 9680f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov final int center = (paddingLeft + width - paddingRight) / 2; 9690f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov final int left = center - child.getMeasuredWidth() / 2; 9700f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov final int right = left + child.getMeasuredWidth(); 971256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock final int bottom = height - paddingBottom - lp.bottomMargin - mInsets.bottom; 9720f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov final int top = bottom - child.getMeasuredHeight(); 9730f87a72d59a224c819e18cfed4374f4d484198d6Svetoslav Ganov child.layout(left, top, right, bottom); 974256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) { 975256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock // Scrim views use the entire area, including padding & insets 976256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock child.layout(0, 0, getMeasuredWidth(), getMeasuredHeight()); 977d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } else { 978d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller // Non-challenge views lay out from the upper left, layered. 979d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller child.layout(paddingLeft + lp.leftMargin, 980256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock paddingTop + lp.topMargin + mInsets.top, 981d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller paddingLeft + child.getMeasuredWidth(), 982256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock paddingTop + child.getMeasuredHeight() + mInsets.top); 983d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 984d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 985d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 986d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (!mHasLayout) { 9876f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell mHasLayout = true; 988d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 989d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 990d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 991d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 992d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void draw(Canvas c) { 993d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller super.draw(c); 9941bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler if (DEBUG) { 99574bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov final Paint debugPaint = new Paint(); 9961bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler debugPaint.setColor(0x40FF00CC); 9971bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler // show the isInDragHandle() rect 9981bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler c.drawRect(mDragHandleEdgeSlop, 999c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mChallengeView.getTop() - getDragHandleSizeAbove(), 10001bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler getWidth() - mDragHandleEdgeSlop, 1001c606bf1af953bb8a682589a29809e714c648c291Daniel Sandler mChallengeView.getTop() + getDragHandleSizeBelow(), 10021bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler debugPaint); 10031bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler } 100474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov } 10051bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler 1006f83324504e0d62f4cba5355459e17cdf9f1100e7Craig Mautner @Override 1007f83324504e0d62f4cba5355459e17cdf9f1100e7Craig Mautner protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { 1008f83324504e0d62f4cba5355459e17cdf9f1100e7Craig Mautner // Focus security fileds before widgets. 1009f83324504e0d62f4cba5355459e17cdf9f1100e7Craig Mautner if (mChallengeView != null && 1010f83324504e0d62f4cba5355459e17cdf9f1100e7Craig Mautner mChallengeView.requestFocus(direction, previouslyFocusedRect)) { 1011f83324504e0d62f4cba5355459e17cdf9f1100e7Craig Mautner return true; 1012f83324504e0d62f4cba5355459e17cdf9f1100e7Craig Mautner } 1013f83324504e0d62f4cba5355459e17cdf9f1100e7Craig Mautner return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); 1014f83324504e0d62f4cba5355459e17cdf9f1100e7Craig Mautner } 1015f83324504e0d62f4cba5355459e17cdf9f1100e7Craig Mautner 101674bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov public void computeScroll() { 101774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov super.computeScroll(); 10186f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell 101974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (!mScroller.isFinished()) { 102074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (mChallengeView == null) { 102174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov // Can't scroll if the view is missing. 102274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov Log.e(TAG, "Challenge view missing in computeScroll"); 102374bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mScroller.abortAnimation(); 102474bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov return; 10251bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler } 10261bfcc82184824c8dc648a0948303f4b3611ac087Daniel Sandler 102774bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov mScroller.computeScrollOffset(); 102874bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov moveChallengeTo(mScroller.getCurrY()); 102974bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov 103074bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov if (mScroller.isFinished()) { 103174bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov post(mEndScrollRunnable); 10326f35209e728a7a9a56afbbdefd3d7bfa1e05ea73Adam Powell } 1033d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1034d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1035d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 10368caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen private void cancelTransitionsInProgress() { 10378caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen if (!mScroller.isFinished()) { 10388caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen mScroller.abortAnimation(); 10398caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen completeChallengeScroll(); 10408caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10418caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen if (mFader != null) { 10428caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen mFader.cancel(); 10438caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10448caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10458caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10468caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen public void fadeInChallenge() { 10478caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen fadeChallenge(true); 10488caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10498caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10508caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen public void fadeOutChallenge() { 10518caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen fadeChallenge(false); 10528caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10538caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10548caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen public void fadeChallenge(final boolean show) { 10558caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen if (mChallengeView != null) { 10568caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10578caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen cancelTransitionsInProgress(); 10588caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen float alpha = show ? 1f : 0f; 10598caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen int duration = show ? CHALLENGE_FADE_IN_DURATION : CHALLENGE_FADE_OUT_DURATION; 10608caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen mFader = ObjectAnimator.ofFloat(mChallengeView, "alpha", alpha); 10618caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen mFader.addListener(new AnimatorListenerAdapter() { 10628caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen @Override 10638caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen public void onAnimationStart(Animator animation) { 10648caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen onFadeStart(show); 10658caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10668caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen @Override 10678caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen public void onAnimationEnd(Animator animation) { 10688caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen onFadeEnd(show); 10698caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10708caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen }); 10718caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen mFader.setDuration(duration); 10728caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen mFader.start(); 10738caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10748caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10758caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10768caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen private int getMaxChallengeBottom() { 10778caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen if (mChallengeView == null) return 0; 10788caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen final int layoutBottom = getLayoutBottom(); 10798caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen final int challengeHeight = mChallengeView.getMeasuredHeight(); 10808caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10818caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen return (layoutBottom + challengeHeight - mChallengeBottomBound); 10828caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10838caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10848caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen private int getMinChallengeBottom() { 10858caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen return getLayoutBottom(); 10868caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10878caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10888caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10898caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen private void onFadeStart(boolean show) { 10908caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen mChallengeInteractiveInternal = false; 10910a4079ec0c11a3fa28ed2a82781a482372015818John Reck enableHardwareLayerForChallengeView(); 10928caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10938caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen if (show) { 10948caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen moveChallengeTo(getMinChallengeBottom()); 10958caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10968caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 10978caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen setScrollState(SCROLL_STATE_FADING); 10988caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 10998caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 11000a4079ec0c11a3fa28ed2a82781a482372015818John Reck private void enableHardwareLayerForChallengeView() { 11010a4079ec0c11a3fa28ed2a82781a482372015818John Reck if (mChallengeView.isHardwareAccelerated()) { 11020a4079ec0c11a3fa28ed2a82781a482372015818John Reck mChallengeView.setLayerType(LAYER_TYPE_HARDWARE, null); 11030a4079ec0c11a3fa28ed2a82781a482372015818John Reck } 11040a4079ec0c11a3fa28ed2a82781a482372015818John Reck } 11050a4079ec0c11a3fa28ed2a82781a482372015818John Reck 11068caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen private void onFadeEnd(boolean show) { 11078caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen mChallengeInteractiveInternal = true; 11088caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen setChallengeShowing(show); 11098caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 11108caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen if (!show) { 11118caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen moveChallengeTo(getMaxChallengeBottom()); 11128caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 11138caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 11148caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen mChallengeView.setLayerType(LAYER_TYPE_NONE, null); 11158caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen mFader = null; 11168caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen setScrollState(SCROLL_STATE_IDLE); 11178caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen } 11188caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen 1119e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen public int getMaxChallengeTop() { 1120e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen if (mChallengeView == null) return 0; 1121e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen 1122e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen final int layoutBottom = getLayoutBottom(); 11238a7785c7aa2df74203276299e10b0d9056cd0560Adam Cohen final int challengeHeight = mChallengeView.getMeasuredHeight(); 1124256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock return layoutBottom - challengeHeight - mInsets.top; 1125e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen } 1126e3643138c89d49a01ba6a622ffaf71c9a95d5cdcAdam Cohen 1127d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller /** 1128d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Move the bottom edge of mChallengeView to a new position and notify the listener 1129d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * if it represents a change in position. Changes made through this method will 1130d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * be stable across layout passes. If this method is called before first layout of 1131d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * this SlidingChallengeLayout it will have no effect. 1132d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * 1133d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @param bottom New bottom edge in px in this SlidingChallengeLayout's coordinate system. 1134d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @return true if the challenge view was moved 1135d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */ 1136d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private boolean moveChallengeTo(int bottom) { 1137d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mChallengeView == null || !mHasLayout) { 1138d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return false; 1139d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1140d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1141b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler final int layoutBottom = getLayoutBottom(); 1142d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final int challengeHeight = mChallengeView.getHeight(); 1143d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 11448caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen bottom = Math.max(getMinChallengeBottom(), 11458caabadd02c7bef701c44dc1718a9a493cbd1261Adam Cohen Math.min(bottom, getMaxChallengeBottom())); 1146d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 114767625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell float offset = 1.f - (float) (bottom - layoutBottom) / 114867625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell (challengeHeight - mChallengeBottomBound); 1149d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mChallengeOffset = offset; 1150d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (offset > 0 && !mChallengeShowing) { 1151d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller setChallengeShowing(true); 1152d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1153d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1154d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller mChallengeView.layout(mChallengeView.getLeft(), 1155d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom); 1156d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1157fa668cc930544e8b231c1fbf3ad496374b7aa21cAdam Powell mChallengeView.setAlpha(getChallengeAlpha()); 11580b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell if (mScrollListener != null) { 11590b1b552268bc6641cc5d01fb80c12258da9c6985Adam Powell mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop()); 1160d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1161d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller postInvalidateOnAnimation(); 1162d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return true; 1163d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1164d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1165b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler /** 1166b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler * The bottom edge of this SlidingChallengeLayout's coordinate system; will coincide with 1167b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler * the bottom edge of mChallengeView when the challenge is fully opened. 1168b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler */ 1169b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler private int getLayoutBottom() { 1170b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler final int bottomMargin = (mChallengeView == null) 1171b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler ? 0 1172b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler : ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin; 1173256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock final int layoutBottom = getMeasuredHeight() - getPaddingBottom() - bottomMargin 1174256ae67b9a629178b458dfc01102c9c0b9963d03John Spurlock - mInsets.bottom; 1175b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler return layoutBottom; 1176b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler } 1177b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler 1178b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler /** 1179b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler * The bottom edge of mChallengeView; essentially, where the sliding challenge 'is'. 1180b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler */ 1181d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private int getChallengeBottom() { 1182d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mChallengeView == null) return 0; 1183d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1184d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return mChallengeView.getBottom(); 1185d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1186d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1187b42b842ee873160acf317fc3ff6339243514b008Daniel Sandler /** 1188d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * Show or hide the challenge view, animating it if necessary. 1189d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller * @param show true to show, false to hide 1190d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller */ 1191d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public void showChallenge(boolean show) { 1192d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller showChallenge(show, 0); 1193995270f01175234d0615a0e1900576f142a3b4adAdam Powell if (!show) { 1194995270f01175234d0615a0e1900576f142a3b4adAdam Powell // Block any drags in progress so that callers can use this to disable dragging 1195995270f01175234d0615a0e1900576f142a3b4adAdam Powell // for other touch interactions. 1196995270f01175234d0615a0e1900576f142a3b4adAdam Powell mBlockDrag = true; 1197995270f01175234d0615a0e1900576f142a3b4adAdam Powell } 1198d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1199d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1200d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private void showChallenge(int velocity) { 1201d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller boolean show = false; 1202d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (Math.abs(velocity) > mMinVelocity) { 1203d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller show = velocity < 0; 1204d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } else { 1205d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller show = mChallengeOffset >= 0.5f; 1206d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1207d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller showChallenge(show, velocity); 1208d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1209d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1210d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller private void showChallenge(boolean show, int velocity) { 1211d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mChallengeView == null) { 1212d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller setChallengeShowing(false); 1213d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return; 1214d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1215d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1216d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller if (mHasLayout) { 1217da8f622b908121efa4c838936fce26c11a74799dChris Wren mChallengeShowingTargetState = show; 121867625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell final int layoutBottom = getLayoutBottom(); 1219d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller animateChallengeTo(show ? layoutBottom : 122067625e29208a97afeb8dca22a73c52ad7b4619baAdam Powell layoutBottom + mChallengeView.getHeight() - mChallengeBottomBound, velocity); 1221d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1222d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1223d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 122419a5267003e7dc70100a4bd4f1f449523b2ff38bJim Miller @Override 1225d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { 1226d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return new LayoutParams(getContext(), attrs); 1227d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1228d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1229d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 1230d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { 1231d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return p instanceof LayoutParams ? new LayoutParams((LayoutParams) p) : 1232d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller p instanceof MarginLayoutParams ? new LayoutParams((MarginLayoutParams) p) : 1233d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller new LayoutParams(p); 1234d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1235d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1236d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 1237d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller protected ViewGroup.LayoutParams generateDefaultLayoutParams() { 1238d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return new LayoutParams(); 1239d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1240d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1241d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller @Override 1242d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { 1243d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller return p instanceof LayoutParams; 1244d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1245d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1246d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public static class LayoutParams extends MarginLayoutParams { 1247eee209313d564be37b8fc068f785b3844cd6597eAdam Powell public int childType = CHILD_TYPE_NONE; 1248eee209313d564be37b8fc068f785b3844cd6597eAdam Powell public static final int CHILD_TYPE_NONE = 0; 1249eee209313d564be37b8fc068f785b3844cd6597eAdam Powell public static final int CHILD_TYPE_CHALLENGE = 2; 1250eee209313d564be37b8fc068f785b3844cd6597eAdam Powell public static final int CHILD_TYPE_SCRIM = 4; 1251cdf8b487456115a1bcc626f53b8da63308dc42ddAdam Powell public static final int CHILD_TYPE_WIDGETS = 5; 125274bdd21629681528dbe7f3ea363d99c8514e026bSvetoslav Ganov public static final int CHILD_TYPE_EXPAND_CHALLENGE_HANDLE = 6; 1253d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 12549799714f6c69efe50e21a6073b879140e5e792b7Adam Powell public int maxHeight; 12559799714f6c69efe50e21a6073b879140e5e792b7Adam Powell 1256d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public LayoutParams() { 1257d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller this(MATCH_PARENT, WRAP_CONTENT); 1258d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1259d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1260d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public LayoutParams(int width, int height) { 1261d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller super(width, height); 1262d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1263d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1264d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public LayoutParams(android.view.ViewGroup.LayoutParams source) { 1265d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller super(source); 1266d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1267d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1268d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public LayoutParams(MarginLayoutParams source) { 1269d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller super(source); 1270d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1271d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1272d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public LayoutParams(LayoutParams source) { 1273d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller super(source); 1274d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1275eee209313d564be37b8fc068f785b3844cd6597eAdam Powell childType = source.childType; 1276d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1277d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1278d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller public LayoutParams(Context c, AttributeSet attrs) { 1279d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller super(c, attrs); 1280d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller 1281d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller final TypedArray a = c.obtainStyledAttributes(attrs, 1282d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller R.styleable.SlidingChallengeLayout_Layout); 1283eee209313d564be37b8fc068f785b3844cd6597eAdam Powell childType = a.getInt(R.styleable.SlidingChallengeLayout_Layout_layout_childType, 1284eee209313d564be37b8fc068f785b3844cd6597eAdam Powell CHILD_TYPE_NONE); 12859799714f6c69efe50e21a6073b879140e5e792b7Adam Powell maxHeight = a.getDimensionPixelSize( 12869799714f6c69efe50e21a6073b879140e5e792b7Adam Powell R.styleable.SlidingChallengeLayout_Layout_layout_maxHeight, 0); 1287d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller a.recycle(); 1288d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1289d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller } 1290d6523da1ad7bae805a3dbea8d7b398d86cd3c181Jim Miller} 1291