Add support for Gravity BEFORE and AFTER

- update layouts
- add Callback2 for RTL aware Drawable
- add unit tests

Change-Id: Ic64d0291e262170aff7297c6580b0b422eaa8d89
diff --git a/api/current.txt b/api/current.txt
index 6854965..7a70842 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8321,6 +8321,7 @@
     method public android.graphics.Region getTransparentRegion();
     method public void inflate(android.content.res.Resources, org.xmlpull.v1.XmlPullParser, android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public void invalidateSelf();
+    method public boolean isLayoutRtlSelf();
     method public boolean isStateful();
     method public final boolean isVisible();
     method public void jumpToCurrentState();
@@ -8351,6 +8352,10 @@
     method public abstract void unscheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable);
   }
 
+  public static abstract interface Drawable.Callback2 implements android.graphics.drawable.Drawable.Callback {
+    method public abstract boolean isLayoutRtl(android.graphics.drawable.Drawable);
+  }
+
   public static abstract class Drawable.ConstantState {
     ctor public Drawable.ConstantState();
     method public abstract int getChangingConfigurations();
@@ -20107,14 +20112,17 @@
     method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect);
     method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect);
     method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect);
+    method public static int getAbsoluteGravity(int, boolean);
     method public static boolean isHorizontal(int);
     method public static boolean isVertical(int);
+    field public static final int AFTER = 8388613; // 0x800005
     field public static final int AXIS_CLIP = 8; // 0x8
     field public static final int AXIS_PULL_AFTER = 4; // 0x4
     field public static final int AXIS_PULL_BEFORE = 2; // 0x2
     field public static final int AXIS_SPECIFIED = 1; // 0x1
     field public static final int AXIS_X_SHIFT = 0; // 0x0
     field public static final int AXIS_Y_SHIFT = 4; // 0x4
+    field public static final int BEFORE = 8388611; // 0x800003
     field public static final int BOTTOM = 80; // 0x50
     field public static final int CENTER = 17; // 0x11
     field public static final int CENTER_HORIZONTAL = 1; // 0x1
@@ -20129,6 +20137,8 @@
     field public static final int HORIZONTAL_GRAVITY_MASK = 7; // 0x7
     field public static final int LEFT = 3; // 0x3
     field public static final int NO_GRAVITY = 0; // 0x0
+    field public static final int RELATIVE_HORIZONTAL_DIRECTION = 8388608; // 0x800000
+    field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
     field public static final int RIGHT = 5; // 0x5
     field public static final int TOP = 48; // 0x30
     field public static final int VERTICAL_GRAVITY_MASK = 112; // 0x70
@@ -21107,7 +21117,7 @@
     method public void recycle();
   }
 
-  public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
+  public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback2 android.view.KeyEvent.Callback {
     ctor public View(android.content.Context);
     ctor public View(android.content.Context, android.util.AttributeSet);
     ctor public View(android.content.Context, android.util.AttributeSet, int);
@@ -21293,6 +21303,7 @@
     method public boolean isInTouchMode();
     method public boolean isLayoutRequested();
     method public boolean isLayoutRtl();
+    method public boolean isLayoutRtl(android.graphics.drawable.Drawable);
     method public boolean isLongClickable();
     method public boolean isOpaque();
     method protected boolean isPaddingOffsetRequired();
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index cf79638..176c487 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -80,9 +80,12 @@
     /** Flag to clip the edges of the object to its container along the
      *  horizontal axis. */
     public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT;
-    
+
+    /** Raw bit controlling whether the horizontal direction is relative (before/after) or not. */
+    public static final int RELATIVE_HORIZONTAL_DIRECTION = 0x00800000;
+
     /**
-     * Binary mask to get the horizontal gravity of a gravity.
+     * Binary mask to get the absolute horizontal gravity of a gravity.
      */
     public static final int HORIZONTAL_GRAVITY_MASK = (AXIS_SPECIFIED |
             AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_X_SHIFT;
@@ -106,8 +109,19 @@
      */
     public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000;
     
+    /** Push object to x-axis position before its container, not changing its size. */
+    public static final int BEFORE = RELATIVE_HORIZONTAL_DIRECTION | LEFT;
+
+    /** Push object to x-axis position after its container, not changing its size. */
+    public static final int AFTER = RELATIVE_HORIZONTAL_DIRECTION | RIGHT;
+
     /**
-     * Apply a gravity constant to an object.
+     * Binary mask for the horizontal gravity and script specific direction bit.
+     */
+    public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = BEFORE | AFTER;
+
+    /**
+     * Apply a gravity constant to an object. This suppose that the layout direction is LTR.
      * 
      * @param gravity The desired placement of the object, as defined by the
      *                constants in this class.
@@ -119,12 +133,33 @@
      * @param outRect Receives the computed frame of the object in its
      *                container.
      */
-    public static void apply(int gravity, int w, int h, Rect container,
-                             Rect outRect) {
+    public static void apply(int gravity, int w, int h, Rect container, Rect outRect) {
         apply(gravity, w, h, container, 0, 0, outRect);
     }
 
     /**
+     * Apply a gravity constant to an object and take care if layout direction is RTL or not.
+     *
+     * @param gravity The desired placement of the object, as defined by the
+     *                constants in this class.
+     * @param w The horizontal size of the object.
+     * @param h The vertical size of the object.
+     * @param container The frame of the containing space, in which the object
+     *                  will be placed.  Should be large enough to contain the
+     *                  width and height of the object.
+     * @param outRect Receives the computed frame of the object in its
+     *                container.
+     * @param isRtl Whether the layout is right-to-left.
+     *
+     * @hide
+     */
+    public static void apply(int gravity, int w, int h, Rect container,
+            Rect outRect, boolean isRtl) {
+        int absGravity = getAbsoluteGravity(gravity, isRtl);
+        apply(absGravity, w, h, container, 0, 0, outRect);
+    }
+
+    /**
      * Apply a gravity constant to an object.
      * 
      * @param gravity The desired placement of the object, as defined by the
@@ -146,7 +181,7 @@
      *                container.
      */
     public static void apply(int gravity, int w, int h, Rect container,
-                             int xAdj, int yAdj, Rect outRect) {
+            int xAdj, int yAdj, Rect outRect) {
         switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) {
             case 0:
                 outRect.left = container.left
@@ -301,6 +336,54 @@
      * @return true if the supplied gravity has an horizontal pull
      */
     public static boolean isHorizontal(int gravity) {
-        return gravity > 0 && (gravity & HORIZONTAL_GRAVITY_MASK) != 0;
+        return gravity > 0 && (gravity & RELATIVE_HORIZONTAL_GRAVITY_MASK) != 0;
+    }
+
+    /**
+     * <p>Convert script specific gravity to absolute horizontal value.</p>
+     *
+     * if horizontal direction is LTR, then BEFORE will set LEFT and AFTER will set RIGHT.
+     * if horizontal direction is RTL, then BEFORE will set RIGHT and AFTER will set LEFT.
+     *
+     * If no horizontal direction is found, then just add LEFT to the existing gravity
+     *
+     * @param gravity The gravity to convert to absolute (horizontal) values.
+     * @param isRtl Whether the layout is right-to-left.
+     * @return gravity converted to absolute (horizontal) values.
+     */
+    public static int getAbsoluteGravity(int gravity, boolean isRtl) {
+        int result = gravity;
+        // Set default gravity, if no horizontal gravity is specified
+        if ((result & HORIZONTAL_GRAVITY_MASK) == 0) {
+            result |= Gravity.LEFT;
+        }
+        // If layout is script specific and gravity is horizontal relative (BEFORE or AFTER)
+        if ((result & RELATIVE_HORIZONTAL_DIRECTION) > 0) {
+            if ((result & Gravity.BEFORE) == Gravity.BEFORE) {
+                // Remove the BEFORE bit
+                result &= ~BEFORE;
+                if (isRtl) {
+                    // Set the RIGHT bit
+                    result |= RIGHT;
+                } else {
+                    // Set the LEFT bit
+                    result |= LEFT;
+                }
+            } else if ((result & Gravity.AFTER) == Gravity.AFTER) {
+                // Remove the AFTER bit
+                result &= ~AFTER;
+                if (isRtl) {
+                    // Set the LEFT bit
+                    result |= LEFT;
+                } else {
+                    // Set the RIGHT bit
+                    result |= RIGHT;
+                }
+            }
+            // Don't need the script specific bit any more, so remove it as we are converting to
+            // absolute values (LEFT or RIGHT)
+            result &= ~RELATIVE_HORIZONTAL_DIRECTION;
+        }
+        return result;
     }
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 98d07c4..90e780d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -631,7 +631,7 @@
  *
  * @see android.view.ViewGroup
  */
-public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
+public class View implements Drawable.Callback2, KeyEvent.Callback, AccessibilityEventSource {
     private static final boolean DBG = false;
 
     /**
@@ -10238,6 +10238,15 @@
         }
     }
 
+     /**
+     * Check if a given Drawable is in RTL layout direction.
+     *
+     * @param who the recipient of the action
+     */
+    public boolean isLayoutRtl(Drawable who) {
+        return (who == mBGDrawable) && isLayoutRtl();
+    }
+
     /**
      * If your view subclass is displaying its own Drawable objects, it should
      * override this function and return true for any Drawable it is
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 9395d5c..a1ddd08 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -77,8 +77,8 @@
             implements Parcelable {
         /**
          * X position for this window.  With the default gravity it is ignored.
-         * When using {@link Gravity#LEFT} or {@link Gravity#RIGHT} it provides
-         * an offset from the given edge.
+         * When using {@link Gravity#LEFT} or {@link Gravity#BEFORE} or {@link Gravity#RIGHT} or
+         * {@link Gravity#AFTER} it provides an offset from the given edge.
          */
         @ViewDebug.ExportedProperty
         public int x;
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 0659063..2a1398d 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -115,7 +115,7 @@
     }
 
     /**
-     * Describes how the foreground is positioned. Defaults to FILL.
+     * Describes how the foreground is positioned. Defaults to BEFORE and TOP.
      *
      * @param foregroundGravity See {@link android.view.Gravity}
      *
@@ -124,8 +124,8 @@
     @android.view.RemotableViewMethod
     public void setForegroundGravity(int foregroundGravity) {
         if (mForegroundGravity != foregroundGravity) {
-            if ((foregroundGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
-                foregroundGravity |= Gravity.LEFT;
+            if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
+                foregroundGravity |= Gravity.BEFORE;
             }
 
             if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
@@ -364,7 +364,7 @@
                     gravity = DEFAULT_CHILD_GRAVITY;
                 }
 
-                final int horizontalGravity = gravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+                final int horizontalGravity = Gravity.getAbsoluteGravity(gravity, isLayoutRtl());
                 final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
 
                 switch (horizontalGravity) {
@@ -436,7 +436,7 @@
                 }
 
                 Gravity.apply(mForegroundGravity, foreground.getIntrinsicWidth(),
-                        foreground.getIntrinsicHeight(), selfBounds, overlayBounds);
+                        foreground.getIntrinsicHeight(), selfBounds, overlayBounds, isLayoutRtl());
                 foreground.setBounds(overlayBounds);
             }
             
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 0383b5c..732cedc 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1408,19 +1408,20 @@
         int childLeft;
         final int childTop = flow ? y : y - h;
 
-        switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
-        case Gravity.LEFT:
-            childLeft = childrenLeft;
-            break;
-        case Gravity.CENTER_HORIZONTAL:
-            childLeft = childrenLeft + ((mColumnWidth - w) / 2);
-            break;
-        case Gravity.RIGHT:
-            childLeft = childrenLeft + mColumnWidth - w;
-            break;
-        default:
-            childLeft = childrenLeft;
-            break;
+        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity,isLayoutRtl());
+        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+            case Gravity.LEFT:
+                childLeft = childrenLeft;
+                break;
+            case Gravity.CENTER_HORIZONTAL:
+                childLeft = childrenLeft + ((mColumnWidth - w) / 2);
+                break;
+            case Gravity.RIGHT:
+                childLeft = childrenLeft + mColumnWidth - w;
+                break;
+            default:
+                childLeft = childrenLeft;
+                break;
         }
 
         if (needToMeasure) {
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index d8068f9..2a0a2f3 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -187,6 +187,11 @@
     }
 
     @Override
+    public boolean isLayoutRtl(Drawable dr) {
+        return ((dr == mDrawable) && isLayoutRtl()) || super.isLayoutRtl(dr);
+    }
+
+    @Override
     protected boolean onSetAlpha(int alpha) {
         if (getBackground() == null) {
             int scale = alpha + (alpha >> 7);
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index bac849e..a95c821 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -110,6 +110,8 @@
             @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
             @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
             @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
+            @ViewDebug.IntToString(from = Gravity.BEFORE,            to = "BEFORE"),
+            @ViewDebug.IntToString(from = Gravity.AFTER,             to = "AFTER"),
             @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
             @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
             @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
@@ -117,7 +119,7 @@
             @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
             @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
         })
-    private int mGravity = Gravity.LEFT | Gravity.TOP;
+    private int mGravity = Gravity.BEFORE | Gravity.TOP;
 
     @ViewDebug.ExportedProperty(category = "measurement")
     private int mTotalLength;
@@ -1344,7 +1346,7 @@
         final int count = getVirtualChildCount();
 
         final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
-        final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+        final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
 
         switch (majorGravity) {
            case Gravity.BOTTOM:
@@ -1378,7 +1380,7 @@
                 if (gravity < 0) {
                     gravity = minorGravity;
                 }
-
+                gravity = Gravity.getAbsoluteGravity(gravity, isLayoutRtl());
                 switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                     case Gravity.CENTER_HORIZONTAL:
                         childLeft = paddingLeft + ((childSpace - childWidth) / 2)
@@ -1433,7 +1435,7 @@
 
         final int count = getVirtualChildCount();
 
-        final int majorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+        final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
         final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
 
         final boolean baselineAligned = mBaselineAligned;
@@ -1441,7 +1443,7 @@
         final int[] maxAscent = mMaxAscent;
         final int[] maxDescent = mMaxDescent;
 
-        switch (majorGravity) {
+        switch (Gravity.getAbsoluteGravity(majorGravity, isLayoutRtl())) {
             case Gravity.RIGHT:
                 // mTotalLength contains the padding already
                 childLeft = mPaddingLeft + mRight - mLeft - mTotalLength;
@@ -1580,8 +1582,8 @@
     @android.view.RemotableViewMethod
     public void setGravity(int gravity) {
         if (mGravity != gravity) {
-            if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
-                gravity |= Gravity.LEFT;
+            if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
+                gravity |= Gravity.BEFORE;
             }
 
             if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
@@ -1595,9 +1597,9 @@
 
     @android.view.RemotableViewMethod
     public void setHorizontalGravity(int horizontalGravity) {
-        final int gravity = horizontalGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
-        if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != gravity) {
-            mGravity = (mGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) | gravity;
+        final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
+        if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
+            mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
             requestLayout();
         }
     }
@@ -1674,6 +1676,8 @@
             @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
             @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
             @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
+            @ViewDebug.IntToString(from = Gravity.BEFORE,            to = "BEFORE"),
+            @ViewDebug.IntToString(from = Gravity.AFTER,             to = "AFTER"),
             @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
             @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
             @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index 30374af..373a177 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -916,6 +916,12 @@
     }
 
     @Override
+    public boolean isLayoutRtl(Drawable who) {
+        return ((who == mProgressDrawable || who == mIndeterminateDrawable) && isLayoutRtl()) ||
+            super.isLayoutRtl(who);
+    }
+
+    @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         updateDrawableBounds(w, h);
     }
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index 9069283..acd8539 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -16,32 +16,32 @@
 
 package android.widget;
 
-import com.android.internal.R;
-
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.SparseArray;
-import android.util.Poolable;
 import android.util.Pool;
-import android.util.Pools;
+import android.util.Poolable;
 import android.util.PoolableManager;
-import static android.util.Log.d;
+import android.util.Pools;
+import android.util.SparseArray;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.RemoteViews.RemoteView;
+import com.android.internal.R;
 
+import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.SortedSet;
 import java.util.TreeSet;
-import java.util.LinkedList;
-import java.util.HashSet;
-import java.util.ArrayList;
+
+import static android.util.Log.d;
 
 /**
  * A Layout where the positions of the children can be described in relation to each other or to the
@@ -221,8 +221,8 @@
     @android.view.RemotableViewMethod
     public void setGravity(int gravity) {
         if (mGravity != gravity) {
-            if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
-                gravity |= Gravity.LEFT;
+            if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
+                gravity |= Gravity.BEFORE;
             }
 
             if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
@@ -236,9 +236,9 @@
 
     @android.view.RemotableViewMethod
     public void setHorizontalGravity(int horizontalGravity) {
-        final int gravity = horizontalGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
-        if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != gravity) {
-            mGravity = (mGravity & ~Gravity.HORIZONTAL_GRAVITY_MASK) | gravity;
+        final int gravity = horizontalGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
+        if ((mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) != gravity) {
+            mGravity = (mGravity & ~Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) | gravity;
             requestLayout();
         }
     }
@@ -339,7 +339,7 @@
         mHasBaselineAlignedChild = false;
 
         View ignore = null;
-        int gravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
+        int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
         final boolean horizontalGravity = gravity != Gravity.LEFT && gravity != 0;
         gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
         final boolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
@@ -494,7 +494,8 @@
                     height - mPaddingBottom);
 
             final Rect contentBounds = mContentBounds;
-            Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds);
+            Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
+                    isLayoutRtl());
 
             final int horizontalOffset = contentBounds.left - left;
             final int verticalOffset = contentBounds.top - top;
diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java
index b612004..5f20c85 100644
--- a/core/java/android/widget/TableRow.java
+++ b/core/java/android/widget/TableRow.java
@@ -224,7 +224,8 @@
                 final int childWidth = child.getMeasuredWidth();
                 lp.mOffset[LayoutParams.LOCATION_NEXT] = columnWidth - childWidth;
 
-                switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+                final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, isLayoutRtl());
+                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                     case Gravity.LEFT:
                         // don't offset on X axis
                         break;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3875765..4216dda 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2093,8 +2093,8 @@
      * @attr ref android.R.styleable#TextView_gravity
      */
     public void setGravity(int gravity) {
-        if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
-            gravity |= Gravity.LEFT;
+        if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
+            gravity |= Gravity.BEFORE;
         }
         if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
             gravity |= Gravity.TOP;
@@ -2102,8 +2102,8 @@
 
         boolean newLayout = false;
 
-        if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) !=
-            (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK)) {
+        if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) !=
+            (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK)) {
             newLayout = true;
         }
 
@@ -4142,6 +4142,17 @@
     }
 
     @Override
+    public boolean isLayoutRtl(Drawable who) {
+        if (who == null) return false;
+        final TextView.Drawables drawables = mDrawables;
+        if (who == drawables.mDrawableLeft || who == drawables.mDrawableRight ||
+            who == drawables.mDrawableTop || who == drawables.mDrawableBottom) {
+            return isLayoutRtl();
+        }
+        return super.isLayoutRtl(who);
+    }
+
+    @Override
     protected boolean onSetAlpha(int alpha) {
         // Alpha is supported if and only if the drawing can be done in one pass.
         // TODO text with spans with a background color currently do not respect this alpha.
@@ -4380,9 +4391,10 @@
             canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText);
         }
 
+        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, isLayoutRtl());
         if (mEllipsize == TextUtils.TruncateAt.MARQUEE) {
             if (!mSingleLine && getLineCount() == 1 && canMarquee() &&
-                    (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
+                    (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) {
                 canvas.translate(mLayout.getLineRight(0) - (mRight - mLeft -
                         getCompoundPaddingLeft() - getCompoundPaddingRight()), 0.0f);
             }
@@ -5528,7 +5540,8 @@
         }
 
         Layout.Alignment alignment;
-        switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, isLayoutRtl());
+        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
             case Gravity.CENTER_HORIZONTAL:
                 alignment = Layout.Alignment.ALIGN_CENTER;
                 break;
@@ -7563,7 +7576,8 @@
                     return 0.0f;
                 }
             } else if (getLineCount() == 1) {
-                switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+                final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, isLayoutRtl());
+                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                     case Gravity.LEFT:
                         return 0.0f;
                     case Gravity.RIGHT:
@@ -7586,7 +7600,8 @@
                 final Marquee marquee = mMarquee;
                 return (marquee.mMaxFadeScroll - marquee.mScroll) / getHorizontalFadingEdgeLength();
             } else if (getLineCount() == 1) {
-                switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+                final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, isLayoutRtl());
+                switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                     case Gravity.LEFT:
                         final int textWidth = (mRight - mLeft) - getCompoundPaddingLeft() -
                                 getCompoundPaddingRight();
diff --git a/core/java/com/android/internal/view/menu/IconMenuItemView.java b/core/java/com/android/internal/view/menu/IconMenuItemView.java
index afa8a01..c337a5d 100644
--- a/core/java/com/android/internal/view/menu/IconMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/IconMenuItemView.java
@@ -282,7 +282,7 @@
         getLineBounds(0, tmpRect);
         mPositionIconAvailable.set(0, 0, getWidth(), tmpRect.top);
         Gravity.apply(Gravity.CENTER_VERTICAL | Gravity.LEFT, mIcon.getIntrinsicWidth(), mIcon
-                .getIntrinsicHeight(), mPositionIconAvailable, mPositionIconOutput);
+                .getIntrinsicHeight(), mPositionIconAvailable, mPositionIconOutput, isLayoutRtl());
         mIcon.setBounds(mPositionIconOutput);
     }
 
diff --git a/core/java/com/android/internal/widget/TextProgressBar.java b/core/java/com/android/internal/widget/TextProgressBar.java
index aee7b76..e113dd8 100644
--- a/core/java/com/android/internal/widget/TextProgressBar.java
+++ b/core/java/com/android/internal/widget/TextProgressBar.java
@@ -86,7 +86,8 @@
             
             // Check if Chronometer should move with with ProgressBar 
             mChronometerFollow = (params.width == ViewGroup.LayoutParams.WRAP_CONTENT);
-            mChronometerGravity = (mChronometer.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK);
+            mChronometerGravity = (mChronometer.getGravity() &
+                    Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK);
             
         } else if (childId == PROGRESSBAR_ID && child instanceof ProgressBar) {
             mProgressBar = (ProgressBar) child;
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index ebb70e3..98deac0 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1128,6 +1128,10 @@
              The clip will be based on the horizontal gravity: a left gravity will clip the right
              edge, a right gravity will clip the left edge, and neither will clip both edges. -->
         <flag name="clip_horizontal" value="0x08" />
+        <!-- Push object to the beginning of its container, not changing its size. -->
+        <flag name="before" value="0x00800003" />
+        <!-- Push object to the end of its container, not changing its size. -->
+        <flag name="after" value="0x00800005" />
     </attr>
 
     <!-- Controls whether links such as urls and email addresses are
@@ -1184,6 +1188,10 @@
              The clip will be based on the horizontal gravity: a left gravity will clip the right
              edge, a right gravity will clip the left edge, and neither will clip both edges. -->
         <flag name="clip_horizontal" value="0x08" />
+        <!-- Push object to the beginning of its container, not changing its size. -->
+        <flag name="before" value="0x00800003" />
+        <!-- Push object to the end of its container, not changing its size. -->
+        <flag name="after" value="0x00800005" />
     </attr>
 
     <!-- Standard orientation constant. -->
diff --git a/core/tests/coretests/src/android/view/GravityTest.java b/core/tests/coretests/src/android/view/GravityTest.java
new file mode 100644
index 0000000..010127f
--- /dev/null
+++ b/core/tests/coretests/src/android/view/GravityTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+public class GravityTest extends AndroidTestCase {
+
+    @SmallTest
+    public void testGetAbsoluteGravity() throws Exception {
+        assertOneGravity(Gravity.LEFT, Gravity.LEFT, false);
+        assertOneGravity(Gravity.LEFT, Gravity.LEFT, true);
+
+        assertOneGravity(Gravity.RIGHT, Gravity.RIGHT, false);
+        assertOneGravity(Gravity.RIGHT, Gravity.RIGHT, true);
+
+        assertOneGravity(Gravity.TOP|Gravity.LEFT, Gravity.TOP, false);
+        assertOneGravity(Gravity.TOP|Gravity.LEFT, Gravity.TOP, true);
+
+        assertOneGravity(Gravity.BOTTOM|Gravity.LEFT, Gravity.BOTTOM, false);
+        assertOneGravity(Gravity.BOTTOM|Gravity.LEFT, Gravity.BOTTOM, true);
+
+        assertOneGravity(Gravity.CENTER_VERTICAL|Gravity.LEFT, Gravity.CENTER_VERTICAL, false);
+        assertOneGravity(Gravity.CENTER_VERTICAL|Gravity.LEFT, Gravity.CENTER_VERTICAL, true);
+
+        assertOneGravity(Gravity.CENTER_HORIZONTAL, Gravity.CENTER_HORIZONTAL, false);
+        assertOneGravity(Gravity.CENTER_HORIZONTAL, Gravity.CENTER_HORIZONTAL, true);
+
+        assertOneGravity(Gravity.CENTER, Gravity.CENTER, false);
+        assertOneGravity(Gravity.CENTER, Gravity.CENTER, true);
+
+        assertOneGravity(Gravity.FILL_VERTICAL|Gravity.LEFT, Gravity.FILL_VERTICAL, false);
+        assertOneGravity(Gravity.FILL_VERTICAL|Gravity.LEFT, Gravity.FILL_VERTICAL, true);
+
+        assertOneGravity(Gravity.FILL_HORIZONTAL, Gravity.FILL_HORIZONTAL, false);
+        assertOneGravity(Gravity.FILL_HORIZONTAL, Gravity.FILL_HORIZONTAL, true);
+
+        assertOneGravity(Gravity.FILL, Gravity.FILL, false);
+        assertOneGravity(Gravity.FILL, Gravity.FILL, true);
+
+        assertOneGravity(Gravity.CLIP_HORIZONTAL|Gravity.LEFT, Gravity.CLIP_HORIZONTAL, false);
+        assertOneGravity(Gravity.CLIP_HORIZONTAL|Gravity.LEFT, Gravity.CLIP_HORIZONTAL, true);
+
+        assertOneGravity(Gravity.CLIP_VERTICAL|Gravity.LEFT, Gravity.CLIP_VERTICAL, false);
+        assertOneGravity(Gravity.CLIP_VERTICAL|Gravity.LEFT, Gravity.CLIP_VERTICAL, true);
+
+        assertOneGravity(Gravity.LEFT, Gravity.BEFORE, false);
+        assertOneGravity(Gravity.RIGHT, Gravity.BEFORE, true);
+
+        assertOneGravity(Gravity.RIGHT, Gravity.AFTER, false);
+        assertOneGravity(Gravity.LEFT, Gravity.AFTER, true);
+    }
+
+    private void assertOneGravity(int expected, int initial, boolean isRtl) {
+        assertEquals(expected, Gravity.getAbsoluteGravity(initial, isRtl));
+    }
+}
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index c9c9fd7..311f024 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -387,7 +387,7 @@
             if (shader == null) {
                 if (mApplyGravity) {
                     Gravity.apply(state.mGravity, mBitmapWidth, mBitmapHeight,
-                            getBounds(), mDstRect);
+                            getBounds(), mDstRect, isLayoutRtlSelf());
                     mApplyGravity = false;
                 }
                 canvas.drawBitmap(bitmap, null, mDstRect, state.mPaint);
diff --git a/graphics/java/android/graphics/drawable/ClipDrawable.java b/graphics/java/android/graphics/drawable/ClipDrawable.java
index b333e01..83020aa 100644
--- a/graphics/java/android/graphics/drawable/ClipDrawable.java
+++ b/graphics/java/android/graphics/drawable/ClipDrawable.java
@@ -209,7 +209,7 @@
         if ((mClipState.mOrientation & VERTICAL) != 0) {
             h -= (h - ih) * (10000 - level) / 10000;
         }
-        Gravity.apply(mClipState.mGravity, w, h, bounds, r);
+        Gravity.apply(mClipState.mGravity, w, h, bounds, r, isLayoutRtlSelf());
 
         if (w > 0 && h > 0) {
             canvas.save();
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 159f371e..8994efc 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -288,6 +288,18 @@
     }
 
     /**
+     * Implement this interface if you want to create an drawable that is RTL aware
+     */
+    public static interface Callback2 extends Callback {
+        /**
+         * A Drawable can call this to know whether the <var>who</var> is in RTL layout direction.
+         *
+         * @param who The drawable being unscheduled.
+         */
+        public boolean isLayoutRtl(Drawable who);
+    }
+
+    /**
      * Bind a {@link Callback} object to this Drawable.  Required for clients
      * that want to support animated drawables.
      *
@@ -364,6 +376,18 @@
     }
 
     /**
+     * Use the current {@link android.graphics.drawable.Drawable.Callback2} implementation to know
+     * if this Drawable is having a layout in RTL direction.
+     */
+    public boolean isLayoutRtlSelf() {
+        final Callback callback = getCallback();
+        if (callback == null || !(callback instanceof Callback2)) {
+            return false;
+        }
+        return ((Callback2) callback).isLayoutRtl(this);
+    }
+
+    /**
      * Specify an alpha value for the drawable. 0 means fully transparent, and
      * 255 means fully opaque.
      */
diff --git a/graphics/java/android/graphics/drawable/ScaleDrawable.java b/graphics/java/android/graphics/drawable/ScaleDrawable.java
index a7ed0d0..cbe1f2d 100644
--- a/graphics/java/android/graphics/drawable/ScaleDrawable.java
+++ b/graphics/java/android/graphics/drawable/ScaleDrawable.java
@@ -221,7 +221,7 @@
             final int ih = min ? mScaleState.mDrawable.getIntrinsicHeight() : 0;
             h -= (int) ((h - ih) * (10000 - level) * mScaleState.mScaleHeight / 10000);
         }
-        Gravity.apply(mScaleState.mGravity, w, h, bounds, r);
+        Gravity.apply(mScaleState.mGravity, w, h, bounds, r, isLayoutRtlSelf());
 
         if (w > 0 && h > 0) {
             mScaleState.mDrawable.setBounds(r.left, r.top, r.right, r.bottom);