am 50e1f9eb: Merge "Final icon for (Day)dreams." into jb-mr1-dev

* commit '50e1f9eb9333451f0e07b1878b5a78bbcdec27ab':
  Final icon for (Day)dreams.
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 50e0a47..0555f38 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -150,6 +150,7 @@
     private final ContentResolver mContentResolver;
     private DevicePolicyManager mDevicePolicyManager;
     private ILockSettings mLockSettingsService;
+    private int mStickyWidgetIndex = -1;
 
     // The current user is set by KeyguardViewMediator and shared by all LockPatternUtils.
     private static volatile int sCurrentUserId = UserHandle.USER_NULL;
@@ -1310,4 +1311,12 @@
         return getBoolean(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, true);
     }
 
+    public int getStickyWidgetIndex() {
+        return mStickyWidgetIndex;
+    }
+
+    public void setStickyWidgetIndex(int stickyWidgetIndex) {
+        mStickyWidgetIndex = stickyWidgetIndex;
+    }
+
 }
diff --git a/core/res/res/drawable-hdpi/add_widget.png b/core/res/res/drawable-hdpi/add_widget.png
index 9cf9b60..fb64a52 100644
--- a/core/res/res/drawable-hdpi/add_widget.png
+++ b/core/res/res/drawable-hdpi/add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/add_widget.png b/core/res/res/drawable-mdpi/add_widget.png
index 9cf9b60..ae26787 100644
--- a/core/res/res/drawable-mdpi/add_widget.png
+++ b/core/res/res/drawable-mdpi/add_widget.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/add_widget.png b/core/res/res/drawable-xhdpi/add_widget.png
index 9cf9b60..c02700c 100644
--- a/core/res/res/drawable-xhdpi/add_widget.png
+++ b/core/res/res/drawable-xhdpi/add_widget.png
Binary files differ
diff --git a/core/res/res/layout/keyguard_add_widget.xml b/core/res/res/layout/keyguard_add_widget.xml
index 6345e89..fa811d7 100644
--- a/core/res/res/layout/keyguard_add_widget.xml
+++ b/core/res/res/layout/keyguard_add_widget.xml
@@ -23,11 +23,18 @@
     android:id="@+id/keyguard_add_widget"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="center_horizontal">
-    <ImageView
-        android:id="@+id/keyguard_add_widget_view"
-        android:clickable="true"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:src="@drawable/add_widget" />
+    >
+    <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            >
+        <ImageView
+            android:id="@+id/keyguard_add_widget_view"
+            android:clickable="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:padding="24dp"
+            android:src="@drawable/add_widget" />
+    </FrameLayout>
 </com.android.internal.policy.impl.keyguard.KeyguardWidgetFrame>
\ No newline at end of file
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 8671ee8..8a7632c 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -323,5 +323,5 @@
 
     <!-- Size of the region along the edge of the screen that will accept
          swipes to scroll the widget area. -->
-    <dimen name="kg_edge_swipe_region_size">16dp</dimen>
+    <dimen name="kg_edge_swipe_region_size">24dp</dimen>
 </resources>
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 66205ef..b846729 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -249,6 +249,14 @@
                 mViewMediatorCallback.onUserActivityTimeoutChanged();
             }
         }
+
+        @Override
+        public void onPageSwitch(int newPageIndex) {
+            if (!isCameraOrAdd(newPageIndex)) {
+                if (DEBUG) Log.d(TAG, "Setting sticky widget index: " + newPageIndex);
+                mLockPatternUtils.setStickyWidgetIndex(newPageIndex);
+            }
+        }
     };
 
     @Override
@@ -712,7 +720,7 @@
 
     @Override
     public void onScreenTurnedOn() {
-        if (DEBUG) Log.d(TAG, "screen on");
+        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
         showPrimarySecurityScreen(false);
         getSecurityView(mCurrentSecuritySelection).onResume();
 
@@ -728,7 +736,7 @@
 
     @Override
     public void onScreenTurnedOff() {
-        if (DEBUG) Log.d(TAG, "screen off");
+        if (DEBUG) Log.d(TAG, "screen off, instance " + Integer.toHexString(hashCode()));
         showPrimarySecurityScreen(true);
         getSecurityView(mCurrentSecuritySelection).onPause();
     }
@@ -849,6 +857,7 @@
                 SlidingChallengeLayout slider = locateSlider();
                 if (slider != null) {
                     slider.showHandle(true);
+                    slider.showChallenge(true);
                 }
                 View v = mAppWidgetContainer.getChildAt(mAppWidgetContainer.getCurrentPage());
                 if (v instanceof CameraWidgetFrame) {
@@ -1060,41 +1069,66 @@
     }
 
     private void showAppropriateWidgetPage() {
-
-        // The following sets the priority for showing widgets. Transport should be shown if
-        // music is playing, followed by the multi-user widget if enabled, followed by the
-        // status widget.
-        final int pageToShow;
-        if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) {
+        boolean music = mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE;
+        if (music) {
             mTransportState = TRANSPORT_VISIBLE;
-            pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl);
-        } else {
-            UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            final View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
-            final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
-            if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) {
-                pageToShow = multiUserPosition;
-            } else {
-                final View statusView = findViewById(R.id.keyguard_status_view);
-                int statusViewIndex = mAppWidgetContainer.indexOfChild(statusView);
-                if (statusViewIndex == -1) {
-                    // TEMP code for default page
-                    if (mAppWidgetContainer.getChildCount() > 2) {
-                        pageToShow = mAppWidgetContainer.getChildCount() - 2;
-                    } else {
-                        pageToShow = 0;
-                    }
-                } else {
-                    pageToShow = mAppWidgetContainer.indexOfChild(statusView);
-                }
-            }
-            if (mTransportState == TRANSPORT_VISIBLE) {
-                mTransportState = TRANSPORT_INVISIBLE;
-            }
+        } else if (mTransportState == TRANSPORT_VISIBLE) {
+            mTransportState = TRANSPORT_INVISIBLE;
         }
+        int pageToShow = getAppropriateWidgetPage();
         mAppWidgetContainer.setCurrentPage(pageToShow);
     }
 
+    private boolean isCameraOrAdd(int pageIndex) {
+        View v = mAppWidgetContainer.getChildAt(pageIndex);
+        return v.getId() == R.id.keyguard_add_widget || v instanceof CameraWidgetFrame;
+    }
+
+    private int getAppropriateWidgetPage() {
+        // assumes at least one widget (besides camera + add)
+
+        boolean music = mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE;
+        // if music playing, show transport
+        if (music) {
+            if (DEBUG) Log.d(TAG, "Music playing, show transport");
+            return mAppWidgetContainer.indexOfChild(mTransportControl);
+        }
+
+        // if multi-user applicable, show it
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
+        int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
+        if (multiUserPosition != -1 && userManager.getUsers(true).size() > 1) {
+            if (DEBUG) Log.d(TAG, "Multi-user applicable, show it");
+            return multiUserPosition;
+        }
+
+        // if we have a sticky widget, show it
+        int stickyWidgetIndex = mLockPatternUtils.getStickyWidgetIndex();
+        if (stickyWidgetIndex > -1
+                && stickyWidgetIndex < mAppWidgetContainer.getChildCount()
+                && !isCameraOrAdd(stickyWidgetIndex)) {
+            if (DEBUG) Log.d(TAG, "Sticky widget found, show it");
+            return stickyWidgetIndex;
+        }
+
+        // if we have a status view, show it
+        View statusView = findViewById(R.id.keyguard_status_view);
+        int statusViewIndex = mAppWidgetContainer.indexOfChild(statusView);
+        if (statusViewIndex > -1) {
+            if (DEBUG) Log.d(TAG, "Status widget found, show it");
+            return mAppWidgetContainer.indexOfChild(statusView);
+        }
+
+        // else the right-most (except for camera)
+        int rightMost = mAppWidgetContainer.getChildCount() - 1;
+        if (mAppWidgetContainer.getChildAt(rightMost) instanceof CameraWidgetFrame) {
+            rightMost--;
+        }
+        if (DEBUG) Log.d(TAG, "Show right-most");
+        return rightMost;
+    }
+
     private void enableUserSelectorIfNecessary() {
         // if there are multiple users, we need to add the multi-user switcher widget to the
         // keyguard.
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index 19d3b19..612b8e0 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -110,6 +110,7 @@
             if (mCallbacks != null) {
                 mCallbacks.onUserActivityTimeoutChanged();
                 mCallbacks.userActivity();
+                mCallbacks.onPageSwitch(newPageIndex);
             }
             KeyguardWidgetFrame oldWidgetPage = getWidgetPageAt(oldPageIndex);
             if (oldWidgetPage != null) {
@@ -149,6 +150,7 @@
     public interface Callbacks {
         public void userActivity();
         public void onUserActivityTimeoutChanged();
+        public void onPageSwitch(int newPageIndex);
     }
 
     public void addWidget(View widget) {
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
index 26e0fec..f2e125b 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/SlidingChallengeLayout.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
@@ -41,6 +42,7 @@
  */
 public class SlidingChallengeLayout extends ViewGroup implements ChallengeLayout {
     private static final String TAG = "SlidingChallengeLayout";
+    private static final boolean DEBUG = false;
 
     // Drawn to show the drag handle in closed state; crossfades to the challenge view
     // when challenge is fully visible
@@ -80,9 +82,13 @@
     private VelocityTracker mVelocityTracker;
     private int mMinVelocity;
     private int mMaxVelocity;
-    private float mLastTouchY;
-    private int mDragHandleSize;
+    private float mGestureStartY; // where did you touch the screen to start this gesture?
+    private int mGestureStartChallengeBottom; // where was the challenge at that time?
+    private int mDragHandleSize; // handle hitrect extension into the challenge view
+    private int mDragHandleHeadroom; // extend the handle's hitrect this far above the line
     private int mDragHandleEdgeSlop;
+    private int mChallengeBottomBound; // Number of pixels from the top of the challenge view
+                                       // that should remain on-screen
     float mHandleAlpha;
     float mFrameAlpha;
     private ObjectAnimator mHandleAnimation;
@@ -224,8 +230,15 @@
     public void setDragDrawables(Drawable handle, Drawable icon) {
         final float density = getResources().getDisplayMetrics().density;
         final int defaultSize = (int) (DRAG_HANDLE_DEFAULT_SIZE * density + 0.5f);
-        mDragHandleSize = Math.max(handle != null ? handle.getIntrinsicHeight() : defaultSize,
-                icon != null ? icon.getIntrinsicHeight() : defaultSize);
+        final int handleHeight = handle != null ? handle.getIntrinsicHeight() : 0;
+        final int iconHeight = icon != null ? icon.getIntrinsicHeight() : 0;
+        mDragHandleSize = Math.max(handleHeight > 0 ? handleHeight : defaultSize,
+                iconHeight > 0 ? iconHeight : defaultSize);
+
+        // top half of the lock icon, plus another 25% to be sure
+        mDragHandleHeadroom = (int) (iconHeight * 0.75f);
+        mChallengeBottomBound = (mDragHandleSize + mDragHandleHeadroom + handleHeight) / 2;
+
         mHandleDrawable = handle;
         mDragIconDrawable = icon;
     }
@@ -250,8 +263,13 @@
     void animateHandle(boolean visible) {
         if (mHandleAnimation != null) {
             mHandleAnimation.cancel();
+            mHandleAnimation = null;
         }
-        mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, visible ? 1.f : 0.f);
+        final float targetAlpha = visible ? 1.f : 0.f;
+        if (targetAlpha == mHandleAlpha) {
+            return;
+        }
+        mHandleAnimation = ObjectAnimator.ofFloat(this, HANDLE_ALPHA, targetAlpha);
         mHandleAnimation.setInterpolator(sHandleFadeInterpolator);
         mHandleAnimation.setDuration(HANDLE_ANIMATE_DURATION);
         mHandleAnimation.start();
@@ -262,9 +280,14 @@
 
         if (mFrameAnimation != null) {
             mFrameAnimation.cancel();
+            mFrameAnimation = null;
         }
-        mFrameAnimation = ObjectAnimator.ofFloat(this, FRAME_ALPHA,
-                visible ? (full ? 1.f : 0.5f) : 0.f);
+        final float targetAlpha = visible ? (full ? 1.f : 0.5f) : 0.f;
+        if (targetAlpha == mFrameAlpha) {
+            return;
+        }
+
+        mFrameAnimation = ObjectAnimator.ofFloat(this, FRAME_ALPHA, targetAlpha);
         mFrameAnimation.setInterpolator(sHandleFadeInterpolator);
         mFrameAnimation.setDuration(HANDLE_ANIMATE_DURATION);
         mFrameAnimation.start();
@@ -329,7 +352,7 @@
             mScrollState = state;
 
             animateHandle(state == SCROLL_STATE_IDLE && !mChallengeShowing);
-            animateFrame(state == SCROLL_STATE_DRAGGING, false);
+            animateFrame(state != SCROLL_STATE_IDLE, false);
             if (mScrollListener != null) {
                 mScrollListener.onScrollStateChanged(state);
             }
@@ -362,8 +385,8 @@
             // Nothing to do.
             return;
         }
-        int sy = mChallengeView.getBottom();
-        int dy = y - sy;
+        final int sy = mChallengeView.getBottom();
+        final int dy = y - sy;
         if (dy == 0) {
             completeChallengeScroll();
             return;
@@ -392,12 +415,7 @@
     }
 
     private void setChallengeShowing(boolean showChallenge) {
-        if (mChallengeShowing != showChallenge) {
-            mChallengeShowing = showChallenge;
-            if (mChallengeView != null) {
-                mChallengeView.setVisibility(showChallenge ? VISIBLE : INVISIBLE);
-            }
-        }
+        mChallengeShowing = showChallenge;
     }
 
     /**
@@ -462,7 +480,7 @@
         final int action = ev.getActionMasked();
         switch (action) {
             case MotionEvent.ACTION_DOWN:
-                mLastTouchY = ev.getY();
+                mGestureStartY = ev.getY();
                 break;
 
             case MotionEvent.ACTION_CANCEL:
@@ -477,11 +495,12 @@
                     final float y = ev.getY(i);
 
                     if (!mIsBouncing &&
-                            (isInDragHandle(x, y) || crossedDragHandle(x, y, mLastTouchY) ||
+                            (isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
                             (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING)) &&
                             mActivePointerId == INVALID_POINTER) {
                         mActivePointerId = ev.getPointerId(i);
-                        mLastTouchY = y;
+                        mGestureStartY = ev.getY();
+                        mGestureStartChallengeBottom = getChallengeBottom();
                         mDragging = true;
                     } else if (isInChallengeView(x, y)) {
                         mBlockDrag = true;
@@ -540,10 +559,12 @@
                         final float x = ev.getX(i);
                         final float y = ev.getY(i);
 
-                        if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mLastTouchY) ||
+                        if ((isInDragHandle(x, y) || crossedDragHandle(x, y, mGestureStartY) ||
                                 (isInChallengeView(x, y) && mScrollState == SCROLL_STATE_SETTLING))
                                 && mActivePointerId == INVALID_POINTER) {
+                            mGestureStartY = y;
                             mActivePointerId = ev.getPointerId(i);
+                            mGestureStartChallengeBottom = getChallengeBottom();
                             mDragging = true;
                             break;
                         }
@@ -563,19 +584,25 @@
                         showChallenge(0);
                         return true;
                     }
-                    final float y = Math.max(ev.getY(index), getChallengeOpenedTop());
-                    final float delta = y - mLastTouchY;
-                    final int idelta = (int) delta;
-                    // Don't lose the rounded component
-                    mLastTouchY = y + delta - idelta;
+                    final float y = ev.getY(index);
+                    final float pos = Math.min(y - mGestureStartY,
+                            getLayoutBottom() - mChallengeBottomBound);
 
-                    moveChallengeBy(idelta);
+                    moveChallengeTo(mGestureStartChallengeBottom + (int) pos);
                 }
                 break;
         }
         return true;
     }
 
+    /**
+     * We only want to add additional vertical space to the drag handle when the panel is fully
+     * closed.
+     */
+    private int getDragHandleHeadroom() {
+        return isChallengeShowing() ? 0 : mDragHandleHeadroom;
+    }
+
     private boolean isInChallengeView(float x, float y) {
         if (mChallengeView == null) return false;
 
@@ -587,14 +614,16 @@
         if (mChallengeView == null) return false;
 
         return x >= mDragHandleEdgeSlop &&
-                y >= mChallengeView.getTop() &&
+                y >= mChallengeView.getTop() - getDragHandleHeadroom() &&
                 x < getWidth() - mDragHandleEdgeSlop &&
                 y < mChallengeView.getTop() + mDragHandleSize;
     }
 
-    private boolean crossedDragHandle(float x, float y, float lastY) {
+    private boolean crossedDragHandle(float x, float y, float initialY) {
         final int challengeTop = mChallengeView.getTop();
-        return x >= 0 && x < getWidth() && lastY < challengeTop &&
+        return  x >= 0 &&
+                x < getWidth() &&
+                initialY < (challengeTop - getDragHandleHeadroom()) &&
                 y > challengeTop + mDragHandleSize;
     }
 
@@ -629,7 +658,6 @@
                 }
                 // We're going to play silly games with the frame's background drawable later.
                 mFrameDrawable = mChallengeView.getBackground();
-                mFrameDrawable.setAlpha(0);
             } else if (lp.childType == LayoutParams.CHILD_TYPE_SCRIM) {
                 setScrimView(child);
             }
@@ -667,10 +695,9 @@
                 final int layoutBottom = height - paddingBottom - lp.bottomMargin;
                 // We use the top of the challenge view to position the handle, so
                 // we never want less than the handle size showing at the bottom.
-                final int bottom = layoutBottom + (int) ((childHeight - mDragHandleSize)
+                final int bottom = layoutBottom + (int) ((childHeight - mChallengeBottomBound)
                         * (1 - mChallengeOffset));
-                float offset = 1.f - (bottom - layoutBottom) / childHeight;
-                child.setAlpha(offset);
+                child.setAlpha(mChallengeOffset / 2 + 0.5f);
                 child.layout(left, bottom - childHeight, left + childWidth, bottom);
             } else {
                 // Non-challenge views lay out from the upper left, layered.
@@ -690,6 +717,9 @@
                     sendInitialListenerUpdates();
                 }
             });
+            if (mFrameDrawable != null) {
+                mFrameDrawable.setAlpha(0);
+            }
             mHasLayout = true;
         }
     }
@@ -717,6 +747,19 @@
     @Override
     public void draw(Canvas c) {
         super.draw(c);
+
+        final Paint debugPaint;
+        if (DEBUG) {
+            debugPaint = new Paint();
+            debugPaint.setColor(0x40FF00CC);
+            // show the isInDragHandle() rect
+            c.drawRect(mDragHandleEdgeSlop,
+                    mChallengeView.getTop() - getDragHandleHeadroom(),
+                    getWidth() - mDragHandleEdgeSlop,
+                    mChallengeView.getTop() + mDragHandleSize,
+                    debugPaint);
+        }
+
         if (mChallengeView != null && mHandleAlpha > 0 && mHandleDrawable != null) {
             final int top = mChallengeView.getTop();
             final int handleHeight = mHandleDrawable.getIntrinsicHeight();
@@ -726,6 +769,14 @@
             mHandleDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
             mHandleDrawable.draw(c);
 
+            if (DEBUG) {
+                // now show the actual drag handle
+                debugPaint.setStyle(Paint.Style.STROKE);
+                debugPaint.setStrokeWidth(1);
+                debugPaint.setColor(0xFF80FF00);
+                c.drawRect(challengeLeft, top, challengeRight, top + handleHeight, debugPaint);
+            }
+
             if (mDragIconDrawable != null) {
                 final int iconWidth = mDragIconDrawable.getIntrinsicWidth();
                 final int iconHeight = mDragIconDrawable.getIntrinsicHeight();
@@ -735,6 +786,12 @@
                         iconTop + iconHeight);
                 mDragIconDrawable.setAlpha((int) (mHandleAlpha * 0xFF));
                 mDragIconDrawable.draw(c);
+
+                if (DEBUG) {
+                    debugPaint.setColor(0xFF00FF00);
+                    c.drawRect(iconLeft, iconTop, iconLeft + iconWidth,
+                        iconTop + iconHeight, debugPaint);
+                }
             }
         }
     }
@@ -753,14 +810,14 @@
             return false;
         }
 
-        final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
-        final int layoutBottom = getHeight() - getPaddingBottom() - bottomMargin;
+        final int layoutBottom = getLayoutBottom();
         final int challengeHeight = mChallengeView.getHeight();
 
         bottom = Math.max(layoutBottom,
-                Math.min(bottom, layoutBottom + challengeHeight - mDragHandleSize));
+                Math.min(bottom, layoutBottom + challengeHeight - mChallengeBottomBound));
 
-        float offset = 1.f - (float) (bottom - layoutBottom) / (challengeHeight - mDragHandleSize);
+        float offset = 1.f - (float) (bottom - layoutBottom) /
+                (challengeHeight - mChallengeBottomBound);
         mChallengeOffset = offset;
         if (offset > 0 && !mChallengeShowing) {
             setChallengeShowing(true);
@@ -769,7 +826,7 @@
         mChallengeView.layout(mChallengeView.getLeft(),
                 bottom - mChallengeView.getHeight(), mChallengeView.getRight(), bottom);
 
-        mChallengeView.setAlpha(offset);
+        mChallengeView.setAlpha(offset / 2 + 0.5f);
         if (mScrollListener != null) {
             mScrollListener.onScrollPositionChanged(offset, mChallengeView.getTop());
         }
@@ -777,24 +834,32 @@
         return true;
     }
 
+    /**
+     * The bottom edge of this SlidingChallengeLayout's coordinate system; will coincide with
+     * the bottom edge of mChallengeView when the challenge is fully opened.
+     */
+    private int getLayoutBottom() {
+        final int bottomMargin = (mChallengeView == null)
+                ? 0
+                : ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
+        final int layoutBottom = getHeight() - getPaddingBottom() - bottomMargin;
+        return layoutBottom;
+    }
+
+    /**
+     * The bottom edge of mChallengeView; essentially, where the sliding challenge 'is'.
+     */
     private int getChallengeBottom() {
         if (mChallengeView == null) return 0;
 
         return mChallengeView.getBottom();
     }
 
+    /**
+     * The top edge of the challenge if it were fully opened.
+     */
     private int getChallengeOpenedTop() {
-        final int paddedBottom = getHeight() - getPaddingBottom();
-        if (mChallengeView == null) return paddedBottom;
-
-        final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
-        final int layoutBottom = paddedBottom - bottomMargin;
-
-        return layoutBottom - mChallengeView.getHeight();
-    }
-
-    private void moveChallengeBy(int delta) {
-        moveChallengeTo(getChallengeBottom() + delta);
+        return getLayoutBottom() - ((mChallengeView == null) ? 0 : mChallengeView.getHeight());
     }
 
     /**
@@ -822,10 +887,9 @@
         }
 
         if (mHasLayout) {
-            final int bottomMargin = ((LayoutParams) mChallengeView.getLayoutParams()).bottomMargin;
-            final int layoutBottom = getHeight() - getPaddingBottom() - bottomMargin;
+            final int layoutBottom = getLayoutBottom();
             animateChallengeTo(show ? layoutBottom :
-                layoutBottom + mChallengeView.getHeight() - mDragHandleSize, velocity);
+                    layoutBottom + mChallengeView.getHeight() - mChallengeBottomBound, velocity);
         }
     }