Bug 970707 - Modify GeckoSwipeRefreshLayout for UX. r=margaret
authorJosh Dover <gerfuls@gmail.com>
Tue, 15 Apr 2014 15:53:00 +0200
changeset 179198 c1366bb9277148f8f4e406753a8b425d94b3cfe4
parent 179197 8956eb0b642e23ef5721dd9d48d9d6758d96a138
child 179199 6f221ba93943b5d0b494e87586bdc7220d61da82
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersmargaret
bugs970707
milestone31.0a1
Bug 970707 - Modify GeckoSwipeRefreshLayout for UX. r=margaret
mobile/android/base/moz.build
mobile/android/base/widget/GeckoSwipeRefreshLayout.java
mobile/android/base/widget/SwipeRefreshLayout.java
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -401,20 +401,20 @@ gbjar.sources += [
     'widget/DateTimePicker.java',
     'widget/Divider.java',
     'widget/DoorHanger.java',
     'widget/EllipsisTextView.java',
     'widget/FaviconView.java',
     'widget/FlowLayout.java',
     'widget/GeckoActionProvider.java',
     'widget/GeckoPopupMenu.java',
+    'widget/GeckoSwipeRefreshLayout.java',
     'widget/GeckoViewFlipper.java',
     'widget/IconTabWidget.java',
     'widget/SquaredImageView.java',
-    'widget/SwipeRefreshLayout.java',
     'widget/TabRow.java',
     'widget/TabThumbnailWrapper.java',
     'widget/ThumbnailView.java',
     'widget/TwoWayView.java',
     'ZoomConstraints.java',
 ]
 gbjar.sources += [ thirdparty_source_dir + f for f in [
     'com/googlecode/eyesfree/braille/selfbraille/ISelfBrailleService.java',
rename from mobile/android/base/widget/SwipeRefreshLayout.java
rename to mobile/android/base/widget/GeckoSwipeRefreshLayout.java
--- a/mobile/android/base/widget/SwipeRefreshLayout.java
+++ b/mobile/android/base/widget/GeckoSwipeRefreshLayout.java
@@ -35,29 +35,34 @@ import android.view.animation.Animation;
 import android.view.animation.Animation.AnimationListener;
 import android.view.animation.AnimationUtils;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.animation.Transformation;
 import android.widget.AbsListView;
 
 /**
- * SwipeRefreshLayout is lifted from Android's support library (v4).
+ * GeckoSwipeRefreshLayout is mostly lifted from Android's support library (v4) with these
+ * modfications:
+ *  - Removes elastic "rubber banding" effect when overscrolling the child view.
+ *  - Changes the height of the progress bar to match the height of HomePager's page indicator.
+ *  - Uses a rectangle rather than a circle for the SwipeProgressBar indicator.
  *
  * This class also embeds package-access dependent classes SwipeProgressBar and
  * BakedBezierInterpolator.
  *
  * Original source: https://android.googlesource.com/platform/frameworks/support/+/
  * android-support-lib-19.1.0/v4/java/android/support/v4/widget/SwipeRefreshLayout.java
  */
-public class SwipeRefreshLayout extends ViewGroup {
+public class GeckoSwipeRefreshLayout extends ViewGroup {
     private static final long RETURN_TO_ORIGINAL_POSITION_TIMEOUT = 300;
     private static final float ACCELERATE_INTERPOLATION_FACTOR = 1.5f;
     private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
-    private static final float PROGRESS_BAR_HEIGHT = 4;
+    // Reduce the height (from 4 to 3) of the progress bar to match HomePager's page indicator.
+    private static final float PROGRESS_BAR_HEIGHT = 3;
     private static final float MAX_SWIPE_DISTANCE_FACTOR = .6f;
     private static final int REFRESH_TRIGGER_DISTANCE = 120;
 
     private SwipeProgressBar mProgressBar; //the thing that shows progress is going
     private View mTarget; //the content that gets pulled down
     private int mOriginalOffsetTop;
     private OnRefreshListener mListener;
     private MotionEvent mDownEvent;
@@ -149,29 +154,29 @@ public class SwipeRefreshLayout extends 
             }
             animateOffsetToStartPosition(mCurrentTargetOffsetTop + getPaddingTop(),
                     mReturnToStartPositionListener);
         }
 
     };
 
     /**
-     * Simple constructor to use when creating a SwipeRefreshLayout from code.
+     * Simple constructor to use when creating a GeckoSwipeRefreshLayout from code.
      * @param context
      */
-    public SwipeRefreshLayout(Context context) {
+    public GeckoSwipeRefreshLayout(Context context) {
         this(context, null);
     }
 
     /**
-     * Constructor that is called when inflating SwipeRefreshLayout from XML.
+     * Constructor that is called when inflating GeckoSwipeRefreshLayout from XML.
      * @param context
      * @param attrs
      */
-    public SwipeRefreshLayout(Context context, AttributeSet attrs) {
+    public GeckoSwipeRefreshLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
 
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
 
         mMediumAnimationDuration = getResources().getInteger(
                 android.R.integer.config_mediumAnimTime);
 
         setWillNotDraw(false);
@@ -275,17 +280,17 @@ public class SwipeRefreshLayout extends 
         return mRefreshing;
     }
 
     private void ensureTarget() {
         // Don't bother getting the parent height if the parent hasn't been laid out yet.
         if (mTarget == null) {
             if (getChildCount() > 1 && !isInEditMode()) {
                 throw new IllegalStateException(
-                        "SwipeRefreshLayout can host only one direct child");
+                        "GeckoSwipeRefreshLayout can host only one direct child");
             }
             mTarget = getChildAt(0);
             mOriginalOffsetTop = mTarget.getTop() + getPaddingTop();
         }
         if (mDistanceToTriggerSync == -1) {
             if (getParent() != null && ((View)getParent()).getHeight() > 0) {
                 final DisplayMetrics metrics = getResources().getDisplayMetrics();
                 mDistanceToTriggerSync = (int) Math.min(
@@ -316,17 +321,17 @@ public class SwipeRefreshLayout extends 
         final int childHeight = height - getPaddingTop() - getPaddingBottom();
         child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);
     }
 
     @Override
     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         if (getChildCount() > 1 && !isInEditMode()) {
-            throw new IllegalStateException("SwipeRefreshLayout can host only one direct child");
+            throw new IllegalStateException("GeckoSwipeRefreshLayout can host only one direct child");
         }
         if (getChildCount() > 0) {
             getChildAt(0).measure(
                     MeasureSpec.makeMeasureSpec(
                             getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                             MeasureSpec.EXACTLY),
                     MeasureSpec.makeMeasureSpec(
                             getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
@@ -396,17 +401,18 @@ public class SwipeRefreshLayout extends 
                             // Just track the user's movement
                             setTriggerPercentage(
                                     mAccelerateInterpolator.getInterpolation(
                                             yDiff / mDistanceToTriggerSync));
                             float offsetTop = yDiff;
                             if (mPrevY > eventY) {
                                 offsetTop = yDiff - mTouchSlop;
                             }
-                            updateContentOffsetTop((int) (offsetTop));
+                            // Removed this call to disable "rubber-band" overscroll effect.
+                            // updateContentOffsetTop((int) offsetTop);
                             if (mPrevY > eventY && (mTarget.getTop() < mTouchSlop)) {
                                 // If the user puts the view back at the top, we
                                 // don't need to. This shouldn't be considered
                                 // cancelling the gesture as the user can restart from the top.
                                 removeCallbacks(mCancel);
                             } else {
                                 updatePositionTimeout();
                             }
@@ -684,17 +690,20 @@ public class SwipeRefreshLayout extends 
                     drawTrigger(canvas, cx, cy);
                 }
             }
             canvas.restoreToCount(restoreCount);
         }
 
         private void drawTrigger(Canvas canvas, int cx, int cy) {
             mPaint.setColor(mColor1);
-            canvas.drawCircle(cx, cy, cx * mTriggerPercentage, mPaint);
+            // Use a rectangle to instead of a circle as per UX.
+            // canvas.drawCircle(cx, cy, cx * mTriggerPercentage, mPaint);
+            canvas.drawRect(cx - cx * mTriggerPercentage, 0, cx + cx * mTriggerPercentage,
+                mBounds.bottom, mPaint);
         }
 
         /**
          * Draws a circle centered in the view.
          *
          * @param canvas the canvas to draw on
          * @param cx the center x coordinate
          * @param cy the center y coordinate