1616
1717package com .android .internal .policy .impl .keyguard ;
1818
19+ import android .animation .Animator ;
20+ import android .animation .AnimatorListenerAdapter ;
1921import android .animation .ObjectAnimator ;
2022import android .content .Context ;
2123import android .content .res .TypedArray ;
2224import android .graphics .Canvas ;
2325import android .graphics .Paint ;
26+ import android .graphics .Rect ;
2427import android .graphics .drawable .Drawable ;
2528import android .util .AttributeSet ;
2629import android .util .FloatProperty ;
@@ -61,10 +64,12 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
6164 private Drawable mHandleDrawable ;
6265 private Drawable mFrameDrawable ;
6366 private Drawable mDragIconDrawable ;
67+ private boolean mEdgeCaptured ;
6468
6569 // Initialized during measurement from child layoutparams
6670 private View mChallengeView ;
6771 private View mScrimView ;
72+ private View mWidgetsView ;
6873
6974 // Range: 0 (fully hidden) to 1 (fully visible)
7075 private float mChallengeOffset = 1.f ;
@@ -110,9 +115,14 @@ public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout
110115
111116 float mHandleAlpha ;
112117 float mFrameAlpha ;
118+ float mFrameAnimationTarget = Float .MIN_VALUE ;
113119 private ObjectAnimator mHandleAnimation ;
114120 private ObjectAnimator mFrameAnimation ;
115121
122+ private final Rect mTempRect = new Rect ();
123+
124+ private boolean mHasGlowpad ;
125+
116126 static final Property <SlidingChallengeLayout , Float > HANDLE_ALPHA =
117127 new FloatProperty <SlidingChallengeLayout >("handleAlpha" ) {
118128 @ Override
@@ -293,21 +303,43 @@ void animateHandle(boolean visible) {
293303 mHandleAnimation .start ();
294304 }
295305
296- void animateFrame (boolean visible , boolean full ) {
306+ void animateFrame (final boolean visible , final boolean full ) {
297307 if (mFrameDrawable == null ) return ;
298308
299- if (mFrameAnimation != null ) {
309+ final float targetAlpha = visible ? (full ? 1.f : 0.5f ) : 0.f ;
310+ if (mFrameAnimation != null && targetAlpha != mFrameAnimationTarget ) {
300311 mFrameAnimation .cancel ();
301- mFrameAnimation = null ;
312+ mFrameAnimationTarget = Float . MIN_VALUE ;
302313 }
303- final float targetAlpha = visible ? (full ? 1.f : 0.5f ) : 0.f ;
304- if (targetAlpha == mFrameAlpha ) {
314+ if (targetAlpha == mFrameAlpha || targetAlpha == mFrameAnimationTarget ) {
305315 return ;
306316 }
317+ mFrameAnimationTarget = targetAlpha ;
307318
308319 mFrameAnimation = ObjectAnimator .ofFloat (this , FRAME_ALPHA , targetAlpha );
309320 mFrameAnimation .setInterpolator (sHandleFadeInterpolator );
310321 mFrameAnimation .setDuration (HANDLE_ANIMATE_DURATION );
322+ mFrameAnimation .addListener (new AnimatorListenerAdapter () {
323+ @ Override
324+ public void onAnimationEnd (Animator animation ) {
325+ mFrameAnimationTarget = Float .MIN_VALUE ;
326+
327+ if (!visible && full && mChallengeView != null ) {
328+ // Mess with padding/margin to remove insets on the bouncer frame.
329+ mChallengeView .setPadding (0 , 0 , 0 , 0 );
330+ LayoutParams lp = (LayoutParams ) mChallengeView .getLayoutParams ();
331+ lp .leftMargin = lp .rightMargin = getChallengeMargin (true );
332+ mChallengeView .setLayoutParams (lp );
333+ }
334+ mFrameAnimation = null ;
335+ }
336+
337+ @ Override
338+ public void onAnimationCancel (Animator animation ) {
339+ mFrameAnimationTarget = Float .MIN_VALUE ;
340+ mFrameAnimation = null ;
341+ }
342+ });
311343 mFrameAnimation .start ();
312344 }
313345
@@ -370,7 +402,9 @@ void setScrollState(int state) {
370402 mScrollState = state ;
371403
372404 animateHandle (state == SCROLL_STATE_IDLE && !mChallengeShowing );
373- animateFrame (false , false );
405+ if (!mIsBouncing ) {
406+ animateFrame (false , false );
407+ }
374408 if (mScrollListener != null ) {
375409 mScrollListener .onScrollStateChanged (state );
376410 }
@@ -380,6 +414,7 @@ void setScrollState(int state) {
380414 void completeChallengeScroll () {
381415 setChallengeShowing (mChallengeOffset != 0 );
382416 setScrollState (SCROLL_STATE_IDLE );
417+ mChallengeView .setLayerType (LAYER_TYPE_NONE , null );
383418 }
384419
385420 void setScrimView (View scrim ) {
@@ -461,7 +496,22 @@ public void showBouncer() {
461496 if (mScrimView != null ) {
462497 mScrimView .setVisibility (VISIBLE );
463498 }
499+
500+ // Mess with padding/margin to inset the bouncer frame.
501+ // We have more space available to us otherwise.
502+ if (mChallengeView != null ) {
503+ if (mFrameDrawable == null || !mFrameDrawable .getPadding (mTempRect )) {
504+ mTempRect .set (0 , 0 , 0 , 0 );
505+ }
506+ mChallengeView .setPadding (mTempRect .left , mTempRect .top , mTempRect .right ,
507+ mTempRect .bottom );
508+ final LayoutParams lp = (LayoutParams ) mChallengeView .getLayoutParams ();
509+ lp .leftMargin = lp .rightMargin = getChallengeMargin (false );
510+ mChallengeView .setLayoutParams (lp );
511+ }
512+
464513 animateFrame (true , true );
514+
465515 if (mBouncerListener != null ) {
466516 mBouncerListener .onBouncerStateChanged (true );
467517 }
@@ -470,17 +520,21 @@ public void showBouncer() {
470520 @ Override
471521 public void hideBouncer () {
472522 if (!mIsBouncing ) return ;
473- setChallengeShowing (false );
523+ showChallenge (false );
474524 mIsBouncing = false ;
475525 if (mScrimView != null ) {
476526 mScrimView .setVisibility (GONE );
477527 }
478- animateFrame (false , false );
528+ animateFrame (false , true );
479529 if (mBouncerListener != null ) {
480530 mBouncerListener .onBouncerStateChanged (false );
481531 }
482532 }
483533
534+ private int getChallengeMargin (boolean expanded ) {
535+ return expanded && mHasGlowpad ? 0 : mDragHandleEdgeSlop ;
536+ }
537+
484538 @ Override
485539 public void requestDisallowInterceptTouchEvent (boolean allowIntercept ) {
486540 // We'll intercept whoever we feel like! ...as long as it isn't a challenge view.
@@ -495,8 +549,6 @@ public boolean onInterceptTouchEvent(MotionEvent ev) {
495549 }
496550 mVelocityTracker .addMovement (ev );
497551
498- //Log.v(TAG, "onIntercept: " + ev);
499-
500552 final int action = ev .getActionMasked ();
501553 switch (action ) {
502554 case MotionEvent .ACTION_DOWN :
@@ -525,6 +577,7 @@ public boolean onInterceptTouchEvent(MotionEvent ev) {
525577 mGestureStartY = y ;
526578 mGestureStartChallengeBottom = getChallengeBottom ();
527579 mDragging = true ;
580+ mChallengeView .setLayerType (LAYER_TYPE_HARDWARE , null );
528581 } else if (isInChallengeView (x , y )) {
529582 mBlockDrag = true ;
530583 }
@@ -601,6 +654,7 @@ && isInDragHandle(mGestureStartX, mGestureStartY)
601654 mActivePointerId = ev .getPointerId (i );
602655 mGestureStartChallengeBottom = getChallengeBottom ();
603656 mDragging = true ;
657+ mChallengeView .setLayerType (LAYER_TYPE_HARDWARE , null );
604658 break ;
605659 }
606660 }
@@ -630,6 +684,52 @@ && isInDragHandle(mGestureStartX, mGestureStartY)
630684 return true ;
631685 }
632686
687+ /**
688+ * The lifecycle of touch events is subtle and it's very easy to do something
689+ * that will cause bugs that will be nasty to track when overriding this method.
690+ * Normally one should always override onInterceptTouchEvent instead.
691+ *
692+ * To put it another way, don't try this at home.
693+ */
694+ @ Override
695+ public boolean dispatchTouchEvent (MotionEvent ev ) {
696+ final int action = ev .getActionMasked ();
697+ boolean handled = false ;
698+ if (action == MotionEvent .ACTION_DOWN ) {
699+ // Defensive programming: if we didn't get the UP or CANCEL, reset anyway.
700+ mEdgeCaptured = false ;
701+ }
702+ if (mWidgetsView != null && !mIsBouncing && (mEdgeCaptured || isEdgeSwipeBeginEvent (ev ))) {
703+ // Normally we would need to do a lot of extra stuff here.
704+ // We can only get away with this because we haven't padded in
705+ // the widget pager or otherwise transformed it during layout.
706+ // We also don't support things like splitting MotionEvents.
707+
708+ // We set handled to captured even if dispatch is returning false here so that
709+ // we don't send a different view a busted or incomplete event stream.
710+ handled = mEdgeCaptured |= mWidgetsView .dispatchTouchEvent (ev );
711+ }
712+
713+ if (!handled && !mEdgeCaptured ) {
714+ handled = super .dispatchTouchEvent (ev );
715+ }
716+
717+ if (action == MotionEvent .ACTION_UP || action == MotionEvent .ACTION_CANCEL ) {
718+ mEdgeCaptured = false ;
719+ }
720+
721+ return handled ;
722+ }
723+
724+ private boolean isEdgeSwipeBeginEvent (MotionEvent ev ) {
725+ if (ev .getActionMasked () != MotionEvent .ACTION_DOWN ) {
726+ return false ;
727+ }
728+
729+ final float x = ev .getX ();
730+ return x < mDragHandleEdgeSlop || x >= getWidth () - mDragHandleEdgeSlop ;
731+ }
732+
633733 /**
634734 * We only want to add additional vertical space to the drag handle when the panel is fully
635735 * closed.
@@ -699,8 +799,16 @@ protected void onMeasure(int widthSpec, int heightSpec) {
699799 }
700800 // We're going to play silly games with the frame's background drawable later.
701801 mFrameDrawable = mChallengeView .getBackground ();
802+
803+ if (!mHasLayout ) {
804+ // Set up the margin correctly based on our content for the first run.
805+ mHasGlowpad = child .findViewById (R .id .keyguard_selector_view ) != null ;
806+ lp .leftMargin = lp .rightMargin = getChallengeMargin (true );
807+ }
702808 } else if (lp .childType == LayoutParams .CHILD_TYPE_SCRIM ) {
703809 setScrimView (child );
810+ } else if (lp .childType == LayoutParams .CHILD_TYPE_WIDGETS ) {
811+ mWidgetsView = child ;
704812 }
705813
706814 if (child .getVisibility () == GONE ) continue ;
@@ -980,6 +1088,7 @@ public static class LayoutParams extends MarginLayoutParams {
9801088 public static final int CHILD_TYPE_NONE = 0 ;
9811089 public static final int CHILD_TYPE_CHALLENGE = 2 ;
9821090 public static final int CHILD_TYPE_SCRIM = 4 ;
1091+ public static final int CHILD_TYPE_WIDGETS = 5 ;
9831092
9841093 public LayoutParams () {
9851094 this (MATCH_PARENT , WRAP_CONTENT );
0 commit comments