From c44d880c508ded633b61930e1964df70876ec42f Mon Sep 17 00:00:00 2001 From: Fabrice Di Meglio Date: Tue, 19 Feb 2013 17:42:13 -0800 Subject: Fix bug #8217354 Suspicious behavior in RTL support for a RelativeLayout - make rules resolution follow the same pattern as padding and other RTL properties Change-Id: Ia0f460f188063f950bc311b51765fdec2f96f542 --- core/java/android/widget/RelativeLayout.java | 146 +++++++++++++++++++++++---- 1 file changed, 128 insertions(+), 18 deletions(-) (limited to 'core/java/android/widget/RelativeLayout.java') diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index deec41cf7eff..166e6f98fbf1 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -42,6 +42,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.RemoteViews.RemoteView; +import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; import static android.util.Log.d; /** @@ -1223,6 +1224,7 @@ public class RelativeLayout extends ViewGroup { private int mEnd = DEFAULT_RELATIVE; private boolean mRulesChanged = false; + private boolean mIsRtlCompatibilityMode = false; /** * When true, uses the parent as the anchor if the anchor doesn't exist or if @@ -1237,6 +1239,10 @@ public class RelativeLayout extends ViewGroup { TypedArray a = c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.RelativeLayout_Layout); + final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion; + mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 || + !c.getApplicationInfo().hasRtlSupport()); + final int[] rules = mRules; //noinspection MismatchedReadAndWriteOfArray final int[] initialRules = mInitialRules; @@ -1405,28 +1411,132 @@ public class RelativeLayout extends ViewGroup { mInitialRules[ALIGN_PARENT_START] != 0 || mInitialRules[ALIGN_PARENT_END] != 0); } + // The way we are resolving rules depends on the layout direction and if we are pre JB MR1 + // or not. + // + // If we are pre JB MR1 (said as "RTL compatibility mode"), "left"/"right" rules are having + // predominance over any "start/end" rules that could have been defined. A special case: + // if no "left"/"right" rule has been defined and "start"/"end" rules are defined then we + // resolve those "start"/"end" rules to "left"/"right" respectively. + // + // If we are JB MR1+, then "start"/"end" rules are having predominance over "left"/"right" + // rules. If no "start"/"end" rule is defined then we use "left"/"right" rules. + // + // In all cases, the result of the resolution should clear the "start"/"end" rules to leave + // only the "left"/"right" rules at the end. private void resolveRules(int layoutDirection) { final boolean isLayoutRtl = (layoutDirection == View.LAYOUT_DIRECTION_RTL); + // Reset to initial state System.arraycopy(mInitialRules, LEFT_OF, mRules, LEFT_OF, VERB_COUNT); - // Apply rules depending on direction - if (mRules[ALIGN_START] != 0) { - mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START]; - } - if (mRules[ALIGN_END] != 0) { - mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END]; - } - if (mRules[START_OF] != 0) { - mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF]; - } - if (mRules[END_OF] != 0) { - mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF]; - } - if (mRules[ALIGN_PARENT_START] != 0) { - mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START]; - } - if (mRules[ALIGN_PARENT_END] != 0) { - mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END]; + + // Apply rules depending on direction and if we are in RTL compatibility mode + if (mIsRtlCompatibilityMode) { + if (mRules[ALIGN_START] != 0) { + if (mRules[ALIGN_LEFT] == 0) { + // "left" rule is not defined but "start" rule is: use the "start" rule as + // the "left" rule + mRules[ALIGN_LEFT] = mRules[ALIGN_START]; + } + mRules[ALIGN_START] = 0; + } + + if (mRules[ALIGN_END] != 0) { + if (mRules[ALIGN_RIGHT] == 0) { + // "right" rule is not defined but "end" rule is: use the "end" rule as the + // "right" rule + mRules[ALIGN_RIGHT] = mRules[ALIGN_END]; + } + mRules[ALIGN_END] = 0; + } + + if (mRules[START_OF] != 0) { + if (mRules[LEFT_OF] == 0) { + // "left" rule is not defined but "start" rule is: use the "start" rule as + // the "left" rule + mRules[LEFT_OF] = mRules[START_OF]; + } + mRules[START_OF] = 0; + } + + if (mRules[END_OF] != 0) { + if (mRules[RIGHT_OF] == 0) { + // "right" rule is not defined but "end" rule is: use the "end" rule as the + // "right" rule + mRules[RIGHT_OF] = mRules[END_OF]; + } + mRules[END_OF] = 0; + } + + if (mRules[ALIGN_PARENT_START] != 0) { + if (mRules[ALIGN_PARENT_LEFT] == 0) { + // "left" rule is not defined but "start" rule is: use the "start" rule as + // the "left" rule + mRules[ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START]; + } + mRules[ALIGN_PARENT_START] = 0; + } + + if (mRules[ALIGN_PARENT_RIGHT] == 0) { + if (mRules[ALIGN_PARENT_RIGHT] == 0) { + // "right" rule is not defined but "end" rule is: use the "end" rule as the + // "right" rule + mRules[ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END]; + } + mRules[ALIGN_PARENT_END] = 0; + } + } else { + // JB MR1+ case + if ((mRules[ALIGN_START] != 0 || mRules[ALIGN_END] != 0) && + (mRules[ALIGN_LEFT] != 0 || mRules[ALIGN_RIGHT] != 0)) { + // "start"/"end" rules take precedence over "left"/"right" rules + mRules[ALIGN_LEFT] = 0; + mRules[ALIGN_RIGHT] = 0; + } + if (mRules[ALIGN_START] != 0) { + // "start" rule resolved to "left" or "right" depending on the direction + mRules[isLayoutRtl ? ALIGN_RIGHT : ALIGN_LEFT] = mRules[ALIGN_START]; + mRules[ALIGN_START] = 0; + } + if (mRules[ALIGN_END] != 0) { + // "end" rule resolved to "left" or "right" depending on the direction + mRules[isLayoutRtl ? ALIGN_LEFT : ALIGN_RIGHT] = mRules[ALIGN_END]; + mRules[ALIGN_END] = 0; + } + + if ((mRules[START_OF] != 0 || mRules[END_OF] != 0) && + (mRules[LEFT_OF] != 0 || mRules[RIGHT_OF] != 0)) { + // "start"/"end" rules take precedence over "left"/"right" rules + mRules[LEFT_OF] = 0; + mRules[RIGHT_OF] = 0; + } + if (mRules[START_OF] != 0) { + // "start" rule resolved to "left" or "right" depending on the direction + mRules[isLayoutRtl ? RIGHT_OF : LEFT_OF] = mRules[START_OF]; + mRules[START_OF] = 0; + } + if (mRules[END_OF] != 0) { + // "end" rule resolved to "left" or "right" depending on the direction + mRules[isLayoutRtl ? LEFT_OF : RIGHT_OF] = mRules[END_OF]; + mRules[END_OF] = 0; + } + + if ((mRules[ALIGN_PARENT_START] != 0 || mRules[ALIGN_PARENT_END] != 0) && + (mRules[ALIGN_PARENT_LEFT] != 0 || mRules[ALIGN_PARENT_RIGHT] != 0)) { + // "start"/"end" rules take precedence over "left"/"right" rules + mRules[ALIGN_PARENT_LEFT] = 0; + mRules[ALIGN_PARENT_RIGHT] = 0; + } + if (mRules[ALIGN_PARENT_START] != 0) { + // "start" rule resolved to "left" or "right" depending on the direction + mRules[isLayoutRtl ? ALIGN_PARENT_RIGHT : ALIGN_PARENT_LEFT] = mRules[ALIGN_PARENT_START]; + mRules[ALIGN_PARENT_START] = 0; + } + if (mRules[ALIGN_PARENT_END] != 0) { + // "end" rule resolved to "left" or "right" depending on the direction + mRules[isLayoutRtl ? ALIGN_PARENT_LEFT : ALIGN_PARENT_RIGHT] = mRules[ALIGN_PARENT_END]; + mRules[ALIGN_PARENT_END] = 0; + } } mRulesChanged = false; } -- cgit v1.2.3