Improve resolution of RTL related properties

- fix bug #6887370 ListPreference shows misaligned radio drawables (in CheckedTextView?)
- fix bug #6938146 "Show more cards..." text on bottom button is not centered

- also defer scrollbar initialization as we need resolved padding values for them

Change-Id: Ife651ffe6bbcc228ff6724f3d9b91079fac3a740
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 59aeffc..2f2c906 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2790,6 +2790,20 @@
     int mUserPaddingEnd;
 
     /**
+     * Whether a left padding has been defined during layout inflation.
+     *
+     * @hide
+     */
+    boolean mUserPaddingLeftDefined = false;
+
+    /**
+     * Whether a right padding has been defined during layout inflation.
+     *
+     * @hide
+     */
+    boolean mUserPaddingRightDefined = false;
+
+    /**
      * Default undefined padding
      */
     private static final int UNDEFINED_PADDING = Integer.MIN_VALUE;
@@ -3195,8 +3209,11 @@
         boolean transformSet = false;
 
         int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
-
         int overScrollMode = mOverScrollMode;
+        boolean initializeScrollbars = false;
+
+        final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
+
         final int N = a.getIndexCount();
         for (int i = 0; i < N; i++) {
             int attr = a.getIndex(i);
@@ -3206,15 +3223,19 @@
                     break;
                 case com.android.internal.R.styleable.View_padding:
                     padding = a.getDimensionPixelSize(attr, -1);
+                    mUserPaddingLeftDefined = true;
+                    mUserPaddingRightDefined = true;
                     break;
                  case com.android.internal.R.styleable.View_paddingLeft:
                     leftPadding = a.getDimensionPixelSize(attr, -1);
+                    mUserPaddingLeftDefined = true;
                     break;
                 case com.android.internal.R.styleable.View_paddingTop:
                     topPadding = a.getDimensionPixelSize(attr, -1);
                     break;
                 case com.android.internal.R.styleable.View_paddingRight:
                     rightPadding = a.getDimensionPixelSize(attr, -1);
+                    mUserPaddingRightDefined = true;
                     break;
                 case com.android.internal.R.styleable.View_paddingBottom:
                     bottomPadding = a.getDimensionPixelSize(attr, -1);
@@ -3359,12 +3380,12 @@
                     if (scrollbars != SCROLLBARS_NONE) {
                         viewFlagValues |= scrollbars;
                         viewFlagMasks |= SCROLLBARS_MASK;
-                        initializeScrollbars(a);
+                        initializeScrollbars = true;
                     }
                     break;
                 //noinspection deprecation
                 case R.styleable.View_fadingEdge:
-                    if (context.getApplicationInfo().targetSdkVersion >= ICE_CREAM_SANDWICH) {
+                    if (targetSdkVersion >= ICE_CREAM_SANDWICH) {
                         // Ignore the attribute starting with ICS
                         break;
                     }
@@ -3496,12 +3517,11 @@
             }
         }
 
-        a.recycle();
-
         setOverScrollMode(overScrollMode);
 
-        // Cache user padding as we cannot fully resolve padding here (we dont have yet the resolved
-        // layout direction). Those cached values will be used later during padding resolution.
+        // Cache start/end user padding as we cannot fully resolve padding here (we dont have yet
+        // the resolved layout direction). Those cached values will be used later during padding
+        // resolution.
         mUserPaddingStart = startPadding;
         mUserPaddingEnd = endPadding;
 
@@ -3529,6 +3549,12 @@
             setFlags(viewFlagValues, viewFlagMasks);
         }
 
+        if (initializeScrollbars) {
+            initializeScrollbars(a);
+        }
+
+        a.recycle();
+
         // Needs to be called after mViewFlags is set
         if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {
             recomputePadding();
@@ -5533,10 +5559,13 @@
             // Reset the current layout direction and the resolved one
             mPrivateFlags2 &= ~LAYOUT_DIRECTION_MASK;
             resetResolvedLayoutDirection();
-            // Set the new layout direction (filtered) and ask for a layout pass
+            // Reset padding resolution
+            mPrivateFlags2 &= ~PADDING_RESOLVED;
+            // Set the new layout direction (filtered)
             mPrivateFlags2 |=
                     ((layoutDirection << LAYOUT_DIRECTION_MASK_SHIFT) & LAYOUT_DIRECTION_MASK);
-            resolvePadding();
+            resolveRtlProperties();
+            // ... and ask for a layout pass
             requestLayout();
         }
     }
@@ -5552,6 +5581,11 @@
         @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL, to = "RESOLVED_DIRECTION_RTL")
     })
     public int getResolvedLayoutDirection() {
+        final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
+        if (targetSdkVersion < JELLY_BEAN_MR1) {
+            mPrivateFlags2 |= LAYOUT_DIRECTION_RESOLVED;
+            return LAYOUT_DIRECTION_LTR;
+        }
         // The layout direction will be resolved only if needed
         if ((mPrivateFlags2 & LAYOUT_DIRECTION_RESOLVED) != LAYOUT_DIRECTION_RESOLVED) {
             resolveLayoutDirection();
@@ -9657,6 +9691,7 @@
             throw new NullPointerException("Layout parameters cannot be null");
         }
         mLayoutParams = params;
+        resolveLayoutParams();
         if (mParent instanceof ViewGroup) {
             ((ViewGroup) mParent).onSetLayoutParams(this, params);
         }
@@ -9664,6 +9699,15 @@
     }
 
     /**
+     * Resolve the layout parameters depending on the resolved layout direction
+     */
+    private void resolveLayoutParams() {
+        if (mLayoutParams != null) {
+            mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection());
+        }
+    }
+
+    /**
      * Set the scrolled position of your view. This will cause a call to
      * {@link #onScrollChanged(int, int, int, int)} and the view will be
      * invalidated.
@@ -11197,12 +11241,7 @@
 
         jumpDrawablesToCurrentState();
 
-        // Order is important here: LayoutDirection MUST be resolved before Padding
-        // and TextDirection
-        resolveLayoutDirection();
-        resolvePadding();
-        resolveTextDirection();
-        resolveTextAlignment();
+        resolveRtlProperties();
 
         clearAccessibilityFocus();
         if (isFocused()) {
@@ -11215,6 +11254,16 @@
         }
     }
 
+    void resolveRtlProperties() {
+        // Order is important here: LayoutDirection MUST be resolved first...
+        resolveLayoutDirection();
+        // ... then we can resolve the others properties depending on the resolved LayoutDirection.
+        resolvePadding();
+        resolveLayoutParams();
+        resolveTextDirection();
+        resolveTextAlignment();
+    }
+
     /**
      * @see #onScreenStateChanged(int)
      */
@@ -11295,55 +11344,65 @@
     }
 
     /**
+     * Return if padding has been resolved
+     */
+    boolean isPaddingResolved() {
+        return (mPrivateFlags2 & PADDING_RESOLVED) != 0;
+    }
+
+    /**
      * Resolve padding depending on layout direction.
      */
     public void resolvePadding() {
-        // If the user specified the absolute padding (either with android:padding or
-        // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise
-        // use the default padding or the padding from the background drawable
-        // (stored at this point in mPadding*)
-        int resolvedLayoutDirection = getResolvedLayoutDirection();
-        switch (resolvedLayoutDirection) {
-            case LAYOUT_DIRECTION_RTL:
-                // Start user padding override Right user padding. Otherwise, if Right user
-                // padding is not defined, use the default Right padding. If Right user padding
-                // is defined, just use it.
-                if (mUserPaddingStart != UNDEFINED_PADDING) {
-                    mUserPaddingRight = mUserPaddingStart;
-                }
-                if (mUserPaddingRight == UNDEFINED_PADDING) {
-                    mUserPaddingRight = mPaddingRight;
-                }
-                if (mUserPaddingEnd != UNDEFINED_PADDING) {
-                    mUserPaddingLeft = mUserPaddingEnd;
-                }
-                if (mUserPaddingLeft == UNDEFINED_PADDING) {
-                    mUserPaddingLeft = mPaddingLeft;
-                }
-                break;
-            case LAYOUT_DIRECTION_LTR:
-            default:
-                // Start user padding override Left user padding. Otherwise, if Left user
-                // padding is not defined, use the default left padding. If Left user padding
-                // is defined, just use it.
-                if (mUserPaddingStart != UNDEFINED_PADDING) {
-                    mUserPaddingLeft = mUserPaddingStart;
-                }
-                if (mUserPaddingLeft == UNDEFINED_PADDING) {
-                    mUserPaddingLeft = mPaddingLeft;
-                }
-                if (mUserPaddingEnd != UNDEFINED_PADDING) {
-                    mUserPaddingRight = mUserPaddingEnd;
-                }
-                if (mUserPaddingRight == UNDEFINED_PADDING) {
-                    mUserPaddingRight = mPaddingRight;
-                }
+        final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
+        if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport()) {
+            // Pre Jelly Bean MR1 case (compatibility mode) OR no RTL support case:
+            // left / right padding are used if defined. If they are not defined and start / end
+            // padding are defined (e.g. in Frameworks resources), then we use start / end and
+            // resolve them as left / right (layout direction is not taken into account).
+            if (!mUserPaddingLeftDefined && mUserPaddingStart != UNDEFINED_PADDING) {
+                mUserPaddingLeft = mUserPaddingStart;
+            }
+            if (!mUserPaddingRightDefined && mUserPaddingEnd != UNDEFINED_PADDING) {
+                mUserPaddingRight = mUserPaddingEnd;
+            }
+
+            mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
+
+            internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight,
+                    mUserPaddingBottom);
+        } else {
+            // Post Jelly Bean MR1 case: we need to take the resolved layout direction into account.
+            // If start / end padding are defined, they will be resolved (hence overriding) to
+            // left / right or right / left depending on the resolved layout direction.
+            // If start / end padding are not defined, use the left / right ones.
+            int resolvedLayoutDirection = getResolvedLayoutDirection();
+            switch (resolvedLayoutDirection) {
+                case LAYOUT_DIRECTION_RTL:
+                    if (mUserPaddingStart != UNDEFINED_PADDING) {
+                        mUserPaddingRight = mUserPaddingStart;
+                    }
+                    if (mUserPaddingEnd != UNDEFINED_PADDING) {
+                        mUserPaddingLeft = mUserPaddingEnd;
+                    }
+                    break;
+                case LAYOUT_DIRECTION_LTR:
+                default:
+                    if (mUserPaddingStart != UNDEFINED_PADDING) {
+                        mUserPaddingLeft = mUserPaddingStart;
+                    }
+                    if (mUserPaddingEnd != UNDEFINED_PADDING) {
+                        mUserPaddingRight = mUserPaddingEnd;
+                    }
+            }
+
+            mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
+
+            internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight,
+                    mUserPaddingBottom);
+            onPaddingChanged(resolvedLayoutDirection);
         }
 
-        mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom;
-
-        internalSetPadding(mUserPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom);
-        onPaddingChanged(resolvedLayoutDirection);
         mPrivateFlags2 |= PADDING_RESOLVED;
     }
 
@@ -14016,6 +14075,8 @@
             }
             background.setLayoutDirection(getResolvedLayoutDirection());
             if (background.getPadding(padding)) {
+                // Reset padding resolution
+                mPrivateFlags2 &= ~PADDING_RESOLVED;
                 switch (background.getLayoutDirection()) {
                     case LAYOUT_DIRECTION_RTL:
                         internalSetPadding(padding.right, padding.top, padding.left, padding.bottom);
@@ -14112,13 +14173,19 @@
      * @param bottom the bottom padding in pixels
      */
     public void setPadding(int left, int top, int right, int bottom) {
+        // Reset padding resolution
+        mPrivateFlags2 &= ~PADDING_RESOLVED;
+
         mUserPaddingStart = UNDEFINED_PADDING;
         mUserPaddingEnd = UNDEFINED_PADDING;
 
         internalSetPadding(left, top, right, bottom);
     }
 
-    void internalSetPadding(int left, int top, int right, int bottom) {
+    /**
+     * @hide
+     */
+    protected void internalSetPadding(int left, int top, int right, int bottom) {
         mUserPaddingLeft = left;
         mUserPaddingRight = right;
         mUserPaddingBottom = bottom;
@@ -14193,6 +14260,9 @@
      * @param bottom the bottom padding in pixels
      */
     public void setPaddingRelative(int start, int top, int end, int bottom) {
+        // Reset padding resolution
+        mPrivateFlags2 &= ~PADDING_RESOLVED;
+
         mUserPaddingStart = start;
         mUserPaddingEnd = end;
 
@@ -14234,6 +14304,9 @@
      * @return the left padding in pixels
      */
     public int getPaddingLeft() {
+        if (!isPaddingResolved()) {
+            resolvePadding();
+        }
         return mPaddingLeft;
     }
 
@@ -14245,6 +14318,9 @@
      * @return the start padding in pixels
      */
     public int getPaddingStart() {
+        if (!isPaddingResolved()) {
+            resolvePadding();
+        }
         return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
                 mPaddingRight : mPaddingLeft;
     }
@@ -14257,6 +14333,9 @@
      * @return the right padding in pixels
      */
     public int getPaddingRight() {
+        if (!isPaddingResolved()) {
+            resolvePadding();
+        }
         return mPaddingRight;
     }
 
@@ -14268,6 +14347,9 @@
      * @return the end padding in pixels
      */
     public int getPaddingEnd() {
+        if (!isPaddingResolved()) {
+            resolvePadding();
+        }
         return (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL) ?
                 mPaddingLeft : mPaddingRight;
     }
@@ -14913,10 +14995,6 @@
         mPrivateFlags |= FORCE_LAYOUT;
         mPrivateFlags |= INVALIDATED;
 
-        if (mLayoutParams != null) {
-            mLayoutParams.onResolveLayoutDirection(getResolvedLayoutDirection());
-        }
-
         if (mParent != null && !mParent.isLayoutRequested()) {
             mParent.requestLayout();
         }
@@ -14960,7 +15038,7 @@
             // first clears the measured dimension flag
             mPrivateFlags &= ~MEASURED_DIMENSION_SET;
 
-            if ((mPrivateFlags2 & PADDING_RESOLVED) == 0) {
+            if (!isPaddingResolved()) {
                 resolvePadding();
             }