Adding RTL paging.

Change-Id: Ic27d499cb76c7c30da37ed93f5372dd8441118b7
diff --git a/src/com/android/launcher2/AppsCustomizePagedView.java b/src/com/android/launcher2/AppsCustomizePagedView.java
index bfc2db0..a9f7faf 100644
--- a/src/com/android/launcher2/AppsCustomizePagedView.java
+++ b/src/com/android/launcher2/AppsCustomizePagedView.java
@@ -1344,6 +1344,7 @@
     // In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
     @Override
     protected void screenScrolled(int screenCenter) {
+        final boolean isRtl = isLayoutRtl();
         super.screenScrolled(screenCenter);
 
         for (int i = 0; i < getChildCount(); i++) {
@@ -1351,19 +1352,28 @@
             if (v != null) {
                 float scrollProgress = getScrollProgress(screenCenter, v, i);
 
-                float interpolatedProgress =
-                        mZInterpolator.getInterpolation(Math.abs(Math.min(scrollProgress, 0)));
+                float interpolatedProgress;
+                float translationX;
+                float maxScrollProgress = Math.max(0, scrollProgress);
+                float minScrollProgress = Math.min(0, scrollProgress);
+
+                if (isRtl) {
+                    translationX = maxScrollProgress * v.getMeasuredWidth();
+                    interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(maxScrollProgress));
+                } else {
+                    translationX = minScrollProgress * v.getMeasuredWidth();
+                    interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(minScrollProgress));
+                }
                 float scale = (1 - interpolatedProgress) +
                         interpolatedProgress * TRANSITION_SCALE_FACTOR;
-                float translationX = Math.min(0, scrollProgress) * v.getMeasuredWidth();
 
                 float alpha;
-
-                if (scrollProgress < 0) {
-                    alpha = scrollProgress < 0 ? mAlphaInterpolator.getInterpolation(
-                        1 - Math.abs(scrollProgress)) : 1.0f;
+                if (isRtl && (scrollProgress > 0)) {
+                    alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(maxScrollProgress));
+                } else if (!isRtl && (scrollProgress < 0)) {
+                    alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(scrollProgress));
                 } else {
-                    // On large screens we need to fade the page as it nears its leftmost position
+                    //  On large screens we need to fade the page as it nears its leftmost position
                     alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress);
                 }
 
@@ -1372,17 +1382,21 @@
                 int pageHeight = v.getMeasuredHeight();
 
                 if (PERFORM_OVERSCROLL_ROTATION) {
-                    if (i == 0 && scrollProgress < 0) {
+                    float xPivot = isRtl ? 1f - TRANSITION_PIVOT : TRANSITION_PIVOT;
+                    boolean isOverscrollingFirstPage = isRtl ? scrollProgress > 0 : scrollProgress < 0;
+                    boolean isOverscrollingLastPage = isRtl ? scrollProgress < 0 : scrollProgress > 0;
+
+                    if (i == 0 && isOverscrollingFirstPage) {
                         // Overscroll to the left
-                        v.setPivotX(TRANSITION_PIVOT * pageWidth);
+                        v.setPivotX(xPivot * pageWidth);
                         v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
                         scale = 1.0f;
                         alpha = 1.0f;
                         // On the first page, we don't want the page to have any lateral motion
                         translationX = 0;
-                    } else if (i == getChildCount() - 1 && scrollProgress > 0) {
+                    } else if (i == getChildCount() - 1 && isOverscrollingLastPage) {
                         // Overscroll to the right
-                        v.setPivotX((1 - TRANSITION_PIVOT) * pageWidth);
+                        v.setPivotX((1 - xPivot) * pageWidth);
                         v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
                         scale = 1.0f;
                         alpha = 1.0f;
diff --git a/src/com/android/launcher2/PagedView.java b/src/com/android/launcher2/PagedView.java
index 6d5d151..494534c 100644
--- a/src/com/android/launcher2/PagedView.java
+++ b/src/com/android/launcher2/PagedView.java
@@ -257,6 +257,13 @@
     }
 
     /**
+     * Note: this is a reimplementation of View.isLayoutRtl() since that is currently hidden api.
+     */
+    public boolean isLayoutRtl() {
+        return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+    }
+
+    /**
      * Called by subclasses to mark that data is ready, and that we can begin loading and laying
      * out pages.
      */
@@ -401,17 +408,28 @@
 
     @Override
     public void scrollTo(int x, int y) {
+        final boolean isRtl = isLayoutRtl();
         mUnboundedScrollX = x;
 
-        if (x < 0) {
+        boolean isXBeforeFirstPage = isRtl ? (x > mMaxScrollX) : (x < 0);
+        boolean isXAfterLastPage = isRtl ? (x < 0) : (x > mMaxScrollX);
+        if (isXBeforeFirstPage) {
             super.scrollTo(0, y);
             if (mAllowOverScroll) {
-                overScroll(x);
+                if (isRtl) {
+                    overScroll(x - mMaxScrollX);
+                } else {
+                    overScroll(x);
+                }
             }
-        } else if (x > mMaxScrollX) {
+        } else if (isXAfterLastPage) {
             super.scrollTo(mMaxScrollX, y);
             if (mAllowOverScroll) {
-                overScroll(x - mMaxScrollX);
+                if (isRtl) {
+                    overScroll(x);
+                } else {
+                    overScroll(x - mMaxScrollX);
+                }
             }
         } else {
             mOverScrollX = x;
@@ -566,7 +584,8 @@
         updateScrollingIndicatorPosition();
 
         if (childCount > 0) {
-            mMaxScrollX = getChildOffset(childCount - 1) - getRelativeChildOffset(childCount - 1);
+            final int index = isLayoutRtl() ? 0 : childCount - 1;
+            mMaxScrollX = getChildOffset(index) - getRelativeChildOffset(index);
         } else {
             mMaxScrollX = 0;
         }
@@ -631,9 +650,13 @@
         if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
         final int verticalPadding = getPaddingTop() + getPaddingBottom();
         final int childCount = getChildCount();
-        int childLeft = getRelativeChildOffset(0);
+        final boolean isRtl = isLayoutRtl();
 
-        for (int i = 0; i < childCount; i++) {
+        final int startIndex = isRtl ? childCount - 1 : 0;
+        final int endIndex = isRtl ? -1 : childCount;
+        final int delta = isRtl ? -1 : 1;
+        int childLeft = getRelativeChildOffset(startIndex);
+        for (int i = startIndex; i != endIndex; i += delta) {
             final View child = getPageAt(i);
             if (child.getVisibility() != View.GONE) {
                 final int childWidth = getScaledMeasuredWidth(child);
@@ -710,6 +733,7 @@
     }
 
     protected int getChildOffset(int index) {
+        final boolean isRtl = isLayoutRtl();
         int[] childOffsets = Float.compare(mLayoutScale, 1f) == 0 ?
                 mChildOffsets : mChildOffsetsWithLayoutScale;
 
@@ -719,8 +743,11 @@
             if (getChildCount() == 0)
                 return 0;
 
-            int offset = getRelativeChildOffset(0);
-            for (int i = 0; i < index; ++i) {
+            final int startIndex = isRtl ? getChildCount() - 1 : 0;
+            final int endIndex = isRtl ? index : index;
+            final int delta = isRtl ? -1 : 1;
+            int offset = getRelativeChildOffset(startIndex);
+            for (int i = startIndex; i != endIndex; i += delta) {
                 offset += getScaledMeasuredWidth(getPageAt(i)) + mPageSpacing;
             }
             if (childOffsets != null) {
@@ -754,28 +781,31 @@
     }
 
     protected void getVisiblePages(int[] range) {
+        final boolean isRtl = isLayoutRtl();
         final int pageCount = getChildCount();
 
         if (pageCount > 0) {
             final int screenWidth = getMeasuredWidth();
-            int leftScreen = 0;
+            int leftScreen = isRtl ? pageCount - 1 : 0;
             int rightScreen = 0;
+            int endIndex = isRtl ? 0 : pageCount - 1;
+            int delta = isRtl ? -1 : 1;
             View currPage = getPageAt(leftScreen);
-            while (leftScreen < pageCount - 1 &&
+            while (leftScreen != endIndex &&
                     currPage.getX() + currPage.getWidth() -
                     currPage.getPaddingRight() < getScrollX()) {
-                leftScreen++;
+                leftScreen += delta;
                 currPage = getPageAt(leftScreen);
             }
             rightScreen = leftScreen;
-            currPage = getPageAt(rightScreen + 1);
-            while (rightScreen < pageCount - 1 &&
+            currPage = getPageAt(rightScreen + delta);
+            while (rightScreen != endIndex &&
                     currPage.getX() - currPage.getPaddingLeft() < getScrollX() + screenWidth) {
-                rightScreen++;
-                currPage = getPageAt(rightScreen + 1);
+                rightScreen += delta;
+                currPage = getPageAt(rightScreen + delta);
             }
-            range[0] = leftScreen;
-            range[1] = rightScreen;
+            range[0] = Math.min(leftScreen, rightScreen);
+            range[1] = Math.max(leftScreen, rightScreen);
         } else {
             range[0] = -1;
             range[1] = -1;
@@ -854,6 +884,7 @@
 
     @Override
     public boolean dispatchUnhandledMove(View focused, int direction) {
+        // XXX-RTL: This will be fixed in a future CL
         if (direction == View.FOCUS_LEFT) {
             if (getCurrentPage() > 0) {
                 snapToPage(getCurrentPage() - 1);
@@ -870,6 +901,7 @@
 
     @Override
     public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+        // XXX-RTL: This will be fixed in a future CL
         if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
             getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode);
         }
@@ -930,14 +962,23 @@
      * Return true if a tap at (x, y) should trigger a flip to the previous page.
      */
     protected boolean hitsPreviousPage(float x, float y) {
-        return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
+        if (isLayoutRtl()) {
+            return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+        } else {
+            return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
+        }
     }
 
     /**
      * Return true if a tap at (x, y) should trigger a flip to the next page.
      */
     protected boolean hitsNextPage(float x, float y) {
-        return  (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+        if (isLayoutRtl()) {
+            return (x < getRelativeChildOffset(mCurrentPage) - mPageSpacing);
+        } else {
+            return (x > (getMeasuredWidth() - getRelativeChildOffset(mCurrentPage) + mPageSpacing));
+        }
+
     }
 
     @Override
@@ -1269,12 +1310,15 @@
                 // We give flings precedence over large moves, which is why we short-circuit our
                 // test for a large move if a fling has been registered. That is, a large
                 // move to the left and fling to the right will register as a fling to the right.
-                if (((isSignificantMove && deltaX > 0 && !isFling) ||
-                        (isFling && velocityX > 0)) && mCurrentPage > 0) {
+                final boolean isRtl = isLayoutRtl();
+                boolean isDeltaXLeft = isRtl ? deltaX > 0 : deltaX < 0;
+                boolean isVelocityXLeft = isRtl ? velocityX > 0 : velocityX < 0;
+                if (((isSignificantMove && !isDeltaXLeft && !isFling) ||
+                        (isFling && !isVelocityXLeft)) && mCurrentPage > 0) {
                     finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
                     snapToPageWithVelocity(finalPage, velocityX);
-                } else if (((isSignificantMove && deltaX < 0 && !isFling) ||
-                        (isFling && velocityX < 0)) &&
+                } else if (((isSignificantMove && isDeltaXLeft && !isFling) ||
+                        (isFling && isVelocityXLeft)) &&
                         mCurrentPage < getChildCount() - 1) {
                     finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
                     snapToPageWithVelocity(finalPage, velocityX);
@@ -1342,7 +1386,9 @@
                         hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
                     }
                     if (hscroll != 0 || vscroll != 0) {
-                        if (hscroll > 0 || vscroll > 0) {
+                        boolean isForwardScroll = isLayoutRtl() ? (hscroll < 0 || vscroll < 0)
+                                                         : (hscroll > 0 || vscroll > 0);
+                        if (isForwardScroll) {
                             scrollRight();
                         } else {
                             scrollLeft();
@@ -1400,10 +1446,14 @@
     }
 
     protected int getChildIndexForRelativeOffset(int relativeOffset) {
+        final boolean isRtl = isLayoutRtl();
         final int childCount = getChildCount();
         int left;
         int right;
-        for (int i = 0; i < childCount; ++i) {
+        final int startIndex = isRtl ? childCount - 1 : 0;
+        final int endIndex = isRtl ? -1 : childCount;
+        final int delta = isRtl ? -1 : 1;
+        for (int i = startIndex; i != endIndex; i += delta) {
             left = getRelativeChildOffset(i);
             right = (left + getScaledMeasuredWidth(getPageAt(i)));
             if (left <= relativeOffset && relativeOffset <= right) {
@@ -1839,17 +1889,20 @@
     }
 
     private void updateScrollingIndicatorPosition() {
+        final boolean isRtl = isLayoutRtl();
         if (!isScrollingIndicatorEnabled()) return;
         if (mScrollIndicator == null) return;
         int numPages = getChildCount();
         int pageWidth = getMeasuredWidth();
-        int lastChildIndex = Math.max(0, getChildCount() - 1);
-        int maxScrollX = getChildOffset(lastChildIndex) - getRelativeChildOffset(lastChildIndex);
         int trackWidth = pageWidth - mScrollIndicatorPaddingLeft - mScrollIndicatorPaddingRight;
         int indicatorWidth = mScrollIndicator.getMeasuredWidth() -
                 mScrollIndicator.getPaddingLeft() - mScrollIndicator.getPaddingRight();
 
-        float offset = Math.max(0f, Math.min(1f, (float) getScrollX() / maxScrollX));
+        float scrollPos = isRtl ? mMaxScrollX - getScrollX() : getScrollX();
+        float offset = Math.max(0f, Math.min(1f, (float) scrollPos / mMaxScrollX));
+        if (isRtl) {
+            offset = 1f - offset;
+        }
         int indicatorSpace = trackWidth / numPages;
         int indicatorPos = (int) (offset * (trackWidth - indicatorSpace)) + mScrollIndicatorPaddingLeft;
         if (hasElasticScrollIndicator()) {
diff --git a/src/com/android/launcher2/Workspace.java b/src/com/android/launcher2/Workspace.java
index 9d8845b..529f425 100644
--- a/src/com/android/launcher2/Workspace.java
+++ b/src/com/android/launcher2/Workspace.java
@@ -1182,18 +1182,6 @@
         }
     }
 
-    float overScrollBackgroundAlphaInterpolator(float r) {
-        float threshold = 0.08f;
-
-        if (r > mOverScrollMaxBackgroundAlpha) {
-            mOverScrollMaxBackgroundAlpha = r;
-        } else if (r < mOverScrollMaxBackgroundAlpha) {
-            r = mOverScrollMaxBackgroundAlpha;
-        }
-
-        return Math.min(r / threshold, 1.0f);
-    }
-
     private void updatePageAlphaValues(int screenCenter) {
         boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
         if (mWorkspaceFadeInAdjacentScreens &&
@@ -1226,23 +1214,38 @@
 
     @Override
     protected void screenScrolled(int screenCenter) {
+        final boolean isRtl = isLayoutRtl();
         super.screenScrolled(screenCenter);
 
         updatePageAlphaValues(screenCenter);
         enableHwLayersOnVisiblePages();
 
         if (mOverScrollX < 0 || mOverScrollX > mMaxScrollX) {
-            int index = mOverScrollX < 0 ? 0 : getChildCount() - 1;
+            int index = 0;
+            float pivotX = 0f;
+            final float leftBiasedPivot = 0.25f;
+            final float rightBiasedPivot = 0.75f;
+            final int lowerIndex = 0;
+            final int upperIndex = getChildCount() - 1;
+            if (isRtl) {
+                index = mOverScrollX < 0 ? upperIndex : lowerIndex;
+                pivotX = (index == 0 ? leftBiasedPivot : rightBiasedPivot);
+            } else {
+                index = mOverScrollX < 0 ? lowerIndex : upperIndex;
+                pivotX = (index == 0 ? rightBiasedPivot : leftBiasedPivot);
+            }
+
             CellLayout cl = (CellLayout) getChildAt(index);
             float scrollProgress = getScrollProgress(screenCenter, cl, index);
-            cl.setOverScrollAmount(Math.abs(scrollProgress), index == 0);
-            float rotation = - WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
+            final boolean isLeftPage = (isRtl ? index > 0 : index == 0);
+            cl.setOverScrollAmount(Math.abs(scrollProgress), isLeftPage);
+            float rotation = -WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
             cl.setRotationY(rotation);
             setFadeForOverScroll(Math.abs(scrollProgress));
             if (!mOverscrollTransformsSet) {
                 mOverscrollTransformsSet = true;
                 cl.setCameraDistance(mDensity * mCameraDistance);
-                cl.setPivotX(cl.getMeasuredWidth() * (index == 0 ? 0.75f : 0.25f));
+                cl.setPivotX(cl.getMeasuredWidth() * pivotX);
                 cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
                 cl.setOverscrollTransformsDirty(true);
             }