Merge "Do not change NetworkInfo.DetailedState." into jb-mr2-dev
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
index 3c124d9..2e4274d 100644
--- a/tools/layoutlib/bridge/.classpath
+++ b/tools/layoutlib/bridge/.classpath
@@ -7,5 +7,6 @@
 	<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
 	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/ninepatch/ninepatch-prebuilt.jar"/>
 	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/tools-common/tools-common-prebuilt.jar"/>
+	<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/icu4j/icu4j.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk
index 687a91f..e3d48fc 100644
--- a/tools/layoutlib/bridge/Android.mk
+++ b/tools/layoutlib/bridge/Android.mk
@@ -22,6 +22,7 @@
 
 LOCAL_JAVA_LIBRARIES := \
 	kxml2-2.3.0 \
+	icu4j \
 	layoutlib_api-prebuilt \
 	tools-common-prebuilt
 
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..782ebfe
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..677b471
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..a1b8062
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..fcdbefe
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..633d864
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..4665e2a
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
new file mode 100644
index 0000000..62d0a0d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2013 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.graphics;
+
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphVector;
+import java.util.LinkedList;
+import java.util.List;
+
+import com.ibm.icu.lang.UScript;
+import com.ibm.icu.lang.UScriptRun;
+
+import android.graphics.Paint_Delegate.FontInfo;
+
+/**
+ * Render the text by breaking it into various scripts and using the right font for each script.
+ * Can be used to measure the text without actually drawing it.
+ */
+@SuppressWarnings("deprecation")
+public class BidiRenderer {
+
+    /* package */ static class ScriptRun {
+        int start;
+        int limit;
+        boolean isRtl;
+        int scriptCode;
+        FontInfo font;
+
+        public ScriptRun(int start, int limit, boolean isRtl) {
+            this.start = start;
+            this.limit = limit;
+            this.isRtl = isRtl;
+            this.scriptCode = UScript.INVALID_CODE;
+        }
+    }
+
+    /* package */ Graphics2D graphics;
+    /* package */ Paint_Delegate paint;
+    /* package */ char[] text;
+
+    /**
+     * @param graphics May be null.
+     * @param paint The Paint to use to get the fonts. Should not be null.
+     * @param text Unidirectional text. Should not be null.
+     */
+    /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
+        assert (paint != null);
+        this.graphics = graphics;
+        this.paint = paint;
+        this.text = text;
+    }
+
+    /**
+     * Render unidirectional text.
+     *
+     * This method can also be used to measure the width of the text without actually drawing it.
+     *
+     * @param start index of the first character
+     * @param limit index of the first character that should not be rendered.
+     * @param isRtl is the text right-to-left
+     * @param advances If not null, then advances for each character to be rendered are returned
+     *            here.
+     * @param advancesIndex index into advances from where the advances need to be filled.
+     * @param draw If true and {@link graphics} is not null, draw the rendered text on the graphics
+     *            at the given co-ordinates
+     * @param x The x-coordinate of the left edge of where the text should be drawn on the given
+     *            graphics.
+     * @param y The y-coordinate at which to draw the text on the given graphics.
+     * @return The x-coordinate of the right edge of the drawn text. In other words,
+     *            x + the width of the text.
+     */
+    /* package */ float renderText(int start, int limit, boolean isRtl, float advances[],
+            int advancesIndex, boolean draw, float x, float y) {
+        // We break the text into scripts and then select font based on it and then render each of
+        // the script runs.
+        for (ScriptRun run : getScriptRuns(text, start, limit, isRtl, paint.getFonts())) {
+            int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
+            flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
+            x = renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw,
+                    x, y);
+            advancesIndex += run.limit - run.start;
+        }
+        return x;
+    }
+
+    /**
+     * Render a script run. Use the preferred font to render as much as possible. This also
+     * implements a fallback mechanism to render characters that cannot be drawn using the
+     * preferred font.
+     *
+     * @return x + width of the text drawn.
+     */
+    private float renderScript(int start, int limit, FontInfo preferredFont, int flag,
+            float advances[], int advancesIndex, boolean draw, float x, float y) {
+        List<FontInfo> fonts = paint.getFonts();
+        if (fonts == null || preferredFont == null) {
+            return x;
+        }
+
+        while (start < limit) {
+            boolean foundFont = false;
+            int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(text, start, limit);
+            if (canDisplayUpTo == -1) {
+                return render(start, limit, preferredFont, flag, advances, advancesIndex, draw,
+                        x, y);
+            } else if (canDisplayUpTo > start) { // can draw something
+                x = render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex,
+                        draw, x, y);
+                advancesIndex += canDisplayUpTo - start;
+                start = canDisplayUpTo;
+            }
+
+            int charCount = Character.isHighSurrogate(text[start]) ? 2 : 1;
+            for (FontInfo font : fonts) {
+                canDisplayUpTo = font.mFont.canDisplayUpTo(text, start, start + charCount);
+                if (canDisplayUpTo == -1) {
+                    x = render(start, start+charCount, font, flag, advances, advancesIndex, draw,
+                            x, y);
+                    start += charCount;
+                    advancesIndex += charCount;
+                    foundFont = true;
+                    break;
+                }
+            }
+            if (!foundFont) {
+                // No font can display this char. Use the preferred font. The char will most
+                // probably appear as a box or a blank space. We could, probably, use some
+                // heuristics and break the character into the base character and diacritics and
+                // then draw it, but it's probably not worth the effort.
+                x = render(start, start + charCount, preferredFont, flag, advances, advancesIndex,
+                        draw, x, y);
+                start += charCount;
+                advancesIndex += charCount;
+            }
+        }
+        return x;
+    }
+
+    /**
+     * Render the text with the given font.
+     */
+    private float render(int start, int limit, FontInfo font, int flag, float advances[],
+            int advancesIndex, boolean draw, float x, float y) {
+
+        float totalAdvance = 0;
+        // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with
+        // the anti-aliasing set.
+        FontRenderContext f = font.mMetrics.getFontRenderContext();
+        FontRenderContext frc = new FontRenderContext(f.getTransform(), paint.isAntiAliased(),
+                f.usesFractionalMetrics());
+        GlyphVector gv = font.mFont.layoutGlyphVector(frc, text, start, limit, flag);
+        int ng = gv.getNumGlyphs();
+        int[] ci = gv.getGlyphCharIndices(0, ng, null);
+        for (int i = 0; i < ng; i++) {
+            float adv = gv.getGlyphMetrics(i).getAdvanceX();
+            if (advances != null) {
+                int adv_idx = advancesIndex + ci[i];
+                advances[adv_idx] += adv;
+            }
+            totalAdvance += adv;
+        }
+        if (draw && graphics != null) {
+            graphics.drawGlyphVector(gv, x, y);
+        }
+        return x + totalAdvance;
+    }
+
+    // --- Static helper methods ---
+
+    /* package */  static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
+            boolean isRtl, List<FontInfo> fonts) {
+        LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
+
+        int count = limit - start;
+        UScriptRun uScriptRun = new UScriptRun(text, start, count);
+        while (uScriptRun.next()) {
+            int scriptStart = uScriptRun.getScriptStart();
+            int scriptLimit = uScriptRun.getScriptLimit();
+            ScriptRun run = new ScriptRun(scriptStart, scriptLimit, isRtl);
+            run.scriptCode = uScriptRun.getScriptCode();
+            setScriptFont(text, run, fonts);
+            scriptRuns.add(run);
+        }
+
+        return scriptRuns;
+    }
+
+    // TODO: Replace this method with one which returns the font based on the scriptCode.
+    private static void setScriptFont(char[] text, ScriptRun run,
+            List<FontInfo> fonts) {
+        for (FontInfo fontInfo : fonts) {
+            if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) {
+                run.font = fontInfo;
+                return;
+            }
+        }
+        run.font = fonts.get(0);
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 4171bb5..da18864 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -23,7 +23,6 @@
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.graphics.Bitmap.Config;
-import android.graphics.Paint_Delegate.FontInfo;
 import android.text.TextUtils;
 
 import java.awt.Color;
@@ -35,7 +34,6 @@
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Arc2D;
 import java.awt.image.BufferedImage;
-import java.util.List;
 
 
 /**
@@ -978,7 +976,8 @@
     @LayoutlibDelegate
     /*package*/ static void native_drawText(int nativeCanvas,
             final char[] text, final int index, final int count,
-            final float startX, final float startY, int flags, int paint) {
+            final float startX, final float startY, final int flags, int paint) {
+
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
             @Override
@@ -988,10 +987,10 @@
                 // Paint.TextAlign indicates how the text is positioned relative to X.
                 // LEFT is the default and there's nothing to do.
                 float x = startX;
-                float y = startY;
+                int limit = index + count;
+                boolean isRtl = flags == Canvas.DIRECTION_RTL;
                 if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
-                    // TODO: check the value of bidiFlags.
-                    float m = paintDelegate.measureText(text, index, count, 0);
+                    float m = paintDelegate.measureText(text, index, count, isRtl);
                     if (paintDelegate.getTextAlign() == Paint.Align.CENTER.nativeInt) {
                         x -= m / 2;
                     } else if (paintDelegate.getTextAlign() == Paint.Align.RIGHT.nativeInt) {
@@ -999,87 +998,15 @@
                     }
                 }
 
-                List<FontInfo> fonts = paintDelegate.getFonts();
-
-                if (fonts.size() > 0) {
-                    FontInfo mainFont = fonts.get(0);
-                    int i = index;
-                    int lastIndex = index + count;
-                    while (i < lastIndex) {
-                        // always start with the main font.
-                        int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
-                        if (upTo == -1) {
-                            // draw all the rest and exit.
-                            graphics.setFont(mainFont.mFont);
-                            graphics.drawChars(text, i, lastIndex - i, (int)x, (int)y);
-                            return;
-                        } else if (upTo > 0) {
-                            // draw what's possible
-                            graphics.setFont(mainFont.mFont);
-                            graphics.drawChars(text, i, upTo - i, (int)x, (int)y);
-
-                            // compute the width that was drawn to increase x
-                            x += mainFont.mMetrics.charsWidth(text, i, upTo - i);
-
-                            // move index to the first non displayed char.
-                            i = upTo;
-
-                            // don't call continue at this point. Since it is certain the main font
-                            // cannot display the font a index upTo (now ==i), we move on to the
-                            // fallback fonts directly.
-                        }
-
-                        // no char supported, attempt to read the next char(s) with the
-                        // fallback font. In this case we only test the first character
-                        // and then go back to test with the main font.
-                        // Special test for 2-char characters.
-                        boolean foundFont = false;
-                        for (int f = 1 ; f < fonts.size() ; f++) {
-                            FontInfo fontInfo = fonts.get(f);
-
-                            // need to check that the font can display the character. We test
-                            // differently if the char is a high surrogate.
-                            int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                            upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
-                            if (upTo == -1) {
-                                // draw that char
-                                graphics.setFont(fontInfo.mFont);
-                                graphics.drawChars(text, i, charCount, (int)x, (int)y);
-
-                                // update x
-                                x += fontInfo.mMetrics.charsWidth(text, i, charCount);
-
-                                // update the index in the text, and move on
-                                i += charCount;
-                                foundFont = true;
-                                break;
-
-                            }
-                        }
-
-                        // in case no font can display the char, display it with the main font.
-                        // (it'll put a square probably)
-                        if (foundFont == false) {
-                            int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-
-                            graphics.setFont(mainFont.mFont);
-                            graphics.drawChars(text, i, charCount, (int)x, (int)y);
-
-                            // measure it to advance x
-                            x += mainFont.mMetrics.charsWidth(text, i, charCount);
-
-                            // and move to the next chars.
-                            i += charCount;
-                        }
-                    }
-                }
+                new BidiRenderer(graphics, paintDelegate, text).renderText(
+                        index, limit, isRtl, null, 0, true, x, startY);
             }
         });
     }
 
     @LayoutlibDelegate
     /*package*/ static void native_drawText(int nativeCanvas, String text,
-            int start, int end, float x, float y, int flags, int paint) {
+            int start, int end, float x, float y, final int flags, int paint) {
         int count = end - start;
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index c9c9800..41953ed 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -32,7 +32,6 @@
 import java.awt.Toolkit;
 import java.awt.font.FontRenderContext;
 import java.awt.geom.AffineTransform;
-import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -576,7 +575,7 @@
             return 0;
         }
 
-        return delegate.measureText(text, index, count, bidiFlags);
+        return delegate.measureText(text, index, count, isRtl(bidiFlags));
     }
 
     @LayoutlibDelegate
@@ -615,7 +614,7 @@
             }
 
             // measure from start to end
-            float res = delegate.measureText(text, start, end - start + 1, bidiFlags);
+            float res = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags));
 
             if (measuredWidth != null) {
                 measuredWidth[measureIndex] = res;
@@ -980,51 +979,27 @@
     /*package*/ static float native_getTextRunAdvances(int native_object,
             char[] text, int index, int count, int contextIndex, int contextCount,
             int flags, float[] advances, int advancesIndex) {
+
+        if (advances != null)
+            for (int i = advancesIndex; i< advancesIndex+count; i++)
+                advances[i]=0;
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
+        if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
             return 0.f;
         }
+        boolean isRtl = isRtl(flags);
 
-        if (delegate.mFonts.size() > 0) {
-            // FIXME: handle multi-char characters (see measureText)
-            float totalAdvance = 0;
-            for (int i = 0; i < count; i++) {
-                char c = text[i + index];
-                boolean found = false;
-                for (FontInfo info : delegate.mFonts) {
-                    if (info.mFont.canDisplay(c)) {
-                        float adv = info.mMetrics.charWidth(c);
-                        totalAdvance += adv;
-                        if (advances != null) {
-                            advances[i] = adv;
-                        }
-
-                        found = true;
-                        break;
-                    }
-                }
-
-                if (found == false) {
-                    // no advance for this char.
-                    if (advances != null) {
-                        advances[i] = 0.f;
-                    }
-                }
-            }
-
-            return totalAdvance;
-        }
-
-        return 0;
-
+        int limit = index + count;
+        return new BidiRenderer(null, delegate, text).renderText(
+                index, limit, isRtl, advances, advancesIndex, false, 0, 0);
     }
 
     @LayoutlibDelegate
     /*package*/ static float native_getTextRunAdvances(int native_object,
             String text, int start, int end, int contextStart, int contextEnd,
             int flags, float[] advances, int advancesIndex) {
-        // FIXME: support contextStart, contextEnd and direction flag
+        // FIXME: support contextStart and contextEnd
         int count = end - start;
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
@@ -1080,19 +1055,12 @@
 
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null) {
+        if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
             return;
         }
-
-        // FIXME should test if the main font can display all those characters.
-        // See MeasureText
-        if (delegate.mFonts.size() > 0) {
-            FontInfo mainInfo = delegate.mFonts.get(0);
-
-            Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count,
-                    delegate.mFontContext);
-            bounds.set(0, 0, (int) rect.getWidth(), (int) rect.getHeight());
-        }
+        int w = (int) delegate.measureText(text, index, count, isRtl(bidiFlags));
+        int h= delegate.getFonts().get(0).mMetrics.getHeight();
+        bounds.set(0, 0, w, h);
     }
 
     @LayoutlibDelegate
@@ -1176,6 +1144,7 @@
                     info.mFont = info.mFont.deriveFont(new AffineTransform(
                             mTextScaleX, mTextSkewX, 0, 1, 0, 0));
                 }
+                // The metrics here don't have anti-aliasing set.
                 info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
 
                 infoList.add(info);
@@ -1185,64 +1154,9 @@
         }
     }
 
-    /*package*/ float measureText(char[] text, int index, int count, int bidiFlags) {
-        // TODO: find out what bidiFlags actually does.
-
-        // WARNING: the logic in this method is similar to Canvas_Delegate.native_drawText
-        // Any change to this method should be reflected there as well
-
-        if (mFonts.size() > 0) {
-            FontInfo mainFont = mFonts.get(0);
-            int i = index;
-            int lastIndex = index + count;
-            float total = 0f;
-            while (i < lastIndex) {
-                // always start with the main font.
-                int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex);
-                if (upTo == -1) {
-                    // shortcut to exit
-                    return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i);
-                } else if (upTo > 0) {
-                    total += mainFont.mMetrics.charsWidth(text, i, upTo - i);
-                    i = upTo;
-                    // don't call continue at this point. Since it is certain the main font
-                    // cannot display the font a index upTo (now ==i), we move on to the
-                    // fallback fonts directly.
-                }
-
-                // no char supported, attempt to read the next char(s) with the
-                // fallback font. In this case we only test the first character
-                // and then go back to test with the main font.
-                // Special test for 2-char characters.
-                boolean foundFont = false;
-                for (int f = 1 ; f < mFonts.size() ; f++) {
-                    FontInfo fontInfo = mFonts.get(f);
-
-                    // need to check that the font can display the character. We test
-                    // differently if the char is a high surrogate.
-                    int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                    upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount);
-                    if (upTo == -1) {
-                        total += fontInfo.mMetrics.charsWidth(text, i, charCount);
-                        i += charCount;
-                        foundFont = true;
-                        break;
-
-                    }
-                }
-
-                // in case no font can display the char, measure it with the main font.
-                if (foundFont == false) {
-                    int size = Character.isHighSurrogate(text[i]) ? 2 : 1;
-                    total += mainFont.mMetrics.charsWidth(text, i, size);
-                    i += size;
-                }
-            }
-
-            return total;
-        }
-
-        return 0;
+    /*package*/ float measureText(char[] text, int index, int count, boolean isRtl) {
+        return new BidiRenderer(null, this, text).renderText(
+                index, index + count, isRtl, null, 0, false, 0, 0);
     }
 
     private float getFontMetrics(FontMetrics metrics) {
@@ -1281,4 +1195,14 @@
         }
     }
 
+    private static boolean isRtl(int flag) {
+        switch(flag) {
+        case Paint.BIDI_RTL:
+        case Paint.BIDI_FORCE_RTL:
+        case Paint.BIDI_DEFAULT_RTL:
+            return true;
+        default:
+            return false;
+        }
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
index 52b8f34..973fa0e 100644
--- a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
@@ -16,7 +16,10 @@
 
 package android.text;
 
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import com.ibm.icu.text.Bidi;
 
 
 /**
@@ -29,9 +32,29 @@
 public class AndroidBidi_Delegate {
 
     @LayoutlibDelegate
-    /*package*/ static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo) {
-        // return the equivalent of Layout.DIR_LEFT_TO_RIGHT
-        // TODO: actually figure the direction.
-        return 0;
+    /*package*/ static int runBidi(int dir, char[] chars, byte[] charInfo, int count,
+            boolean haveInfo) {
+
+        switch (dir) {
+        case 0: // Layout.DIR_REQUEST_LTR
+        case 1: // Layout.DIR_REQUEST_RTL
+            break;  // No change.
+        case -1:
+            dir = Bidi.LEVEL_DEFAULT_LTR;
+            break;
+        case -2:
+            dir = Bidi.LEVEL_DEFAULT_RTL;
+            break;
+        default:
+            // Invalid code. Log error, assume LEVEL_DEFAULT_LTR and continue.
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Invalid direction flag", null);
+            dir = Bidi.LEVEL_DEFAULT_LTR;
+        }
+        Bidi bidi = new Bidi(chars, 0, null, 0, count, dir);
+        if (charInfo != null) {
+            for (int i = 0; i < count; ++i)
+            charInfo[i] = bidi.getLevelAt(i);
+        }
+        return bidi.getParaLevel();
     }
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 42257c5..ab4be71 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -35,6 +35,7 @@
 import com.android.tools.layoutlib.create.MethodAdapter;
 import com.android.tools.layoutlib.create.OverrideMethod;
 import com.android.util.Pair;
+import com.ibm.icu.util.ULocale;
 
 import android.content.res.BridgeAssetManager;
 import android.graphics.Bitmap;
@@ -64,6 +65,8 @@
  */
 public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
 
+    private static final String ICU_LOCALE_DIRECTION_RTL = "right-to-left";
+
     public static class StaticMethodNotImplementedException extends RuntimeException {
         private static final long serialVersionUID = 1L;
 
@@ -211,7 +214,8 @@
                 Capability.ANIMATED_VIEW_MANIPULATION,
                 Capability.ADAPTER_BINDING,
                 Capability.EXTENDED_VIEWINFO,
-                Capability.FIXED_SCALABLE_NINE_PATCH);
+                Capability.FIXED_SCALABLE_NINE_PATCH,
+                Capability.RTL);
 
 
         BridgeAssetManager.initSystem();
@@ -411,6 +415,20 @@
         throw new IllegalArgumentException("viewObject is not a View");
     }
 
+    @Override
+    public boolean isRtl(String locale) {
+        return isLocaleRtl(locale);
+    }
+
+    public static boolean isLocaleRtl(String locale) {
+        if (locale == null) {
+            locale = "";
+        }
+        ULocale uLocale = new ULocale(locale);
+        return uLocale.getCharacterOrientation().equals(ICU_LOCALE_DIRECTION_RTL) ?
+                true : false;
+    }
+
     /**
      * Returns the lock for the bridge
      */
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 21bef1c..99aa228 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -132,7 +132,8 @@
             RenderResources renderResources,
             IProjectCallback projectCallback,
             Configuration config,
-            int targetSdkVersion) {
+            int targetSdkVersion,
+            boolean hasRtlSupport) {
         mProjectKey = projectKey;
         mMetrics = metrics;
         mProjectCallback = projectCallback;
@@ -142,6 +143,9 @@
 
         mApplicationInfo = new ApplicationInfo();
         mApplicationInfo.targetSdkVersion = targetSdkVersion;
+        if (hasRtlSupport) {
+            mApplicationInfo.flags = mApplicationInfo.flags | ApplicationInfo.FLAG_SUPPORTS_RTL;
+        }
 
         mWindowManager = new WindowManagerImpl(mMetrics);
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index ea9d8d9..17b0eb6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -25,6 +25,7 @@
 import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 import com.android.resources.Density;
+import com.android.resources.LayoutDirection;
 import com.android.resources.ResourceType;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -86,38 +87,53 @@
         }
     }
 
-    private InputStream getIcon(String iconName, Density[] densityInOut, String[] pathOut,
-            boolean tryOtherDensities) {
+    private InputStream getIcon(String iconName, Density[] densityInOut, LayoutDirection direction, 
+            String[] pathOut, boolean tryOtherDensities) {
         // current density
         Density density = densityInOut[0];
 
         // bitmap url relative to this class
-        pathOut[0] = "/bars/" + density.getResourceValue() + "/" + iconName;
+        if (direction != null) {
+            pathOut[0] = "/bars/" + direction.getResourceValue() + "-" + density.getResourceValue()
+                    + "/" + iconName;
+        } else {
+            pathOut[0] = "/bars/" + density.getResourceValue() + "/" + iconName;
+        }
 
         InputStream stream = getClass().getResourceAsStream(pathOut[0]);
         if (stream == null && tryOtherDensities) {
             for (Density d : Density.values()) {
                 if (d != density) {
                     densityInOut[0] = d;
-                    stream = getIcon(iconName, densityInOut, pathOut, false /*tryOtherDensities*/);
+                    stream = getIcon(iconName, densityInOut, direction, pathOut,
+                            false /*tryOtherDensities*/);
                     if (stream != null) {
                         return stream;
                     }
                 }
             }
+            // couldn't find resource with direction qualifier. try without.
+            if (direction != null) {
+                return getIcon(iconName, densityInOut, null, pathOut, true);
+            }
         }
 
         return stream;
     }
 
     protected void loadIcon(int index, String iconName, Density density) {
+        loadIcon(index, iconName, density, false);
+    }
+
+    protected void loadIcon(int index, String iconName, Density density, boolean isRtl) {
         View child = getChildAt(index);
         if (child instanceof ImageView) {
             ImageView imageView = (ImageView) child;
 
             String[] pathOut = new String[1];
             Density[] densityInOut = new Density[] { density };
-            InputStream stream = getIcon(iconName, densityInOut, pathOut,
+            LayoutDirection dir = isRtl ? LayoutDirection.RTL : LayoutDirection.LTR;
+            InputStream stream = getIcon(iconName, densityInOut, dir, pathOut,
                     true /*tryOtherDensities*/);
             density = densityInOut[0];
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index cc90d6b..84e676e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -17,6 +17,7 @@
 package com.android.layoutlib.bridge.bars;
 
 import com.android.resources.Density;
+import com.android.layoutlib.bridge.Bridge;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -26,7 +27,8 @@
 
 public class NavigationBar extends CustomBar {
 
-    public NavigationBar(Context context, Density density, int orientation) throws XmlPullParserException {
+    public NavigationBar(Context context, Density density, int orientation, boolean isRtl,
+            boolean rtlEnabled) throws XmlPullParserException {
         super(context, density, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml");
 
         setBackgroundColor(0xFF000000);
@@ -37,14 +39,15 @@
         // 0 is a spacer.
         int back = 1;
         int recent = 3;
-        if (orientation == LinearLayout.VERTICAL) {
+        if (orientation == LinearLayout.VERTICAL || (isRtl && !rtlEnabled)) {
+            // If RTL is enabled, then layoutlib mirrors the layout for us.
             back = 3;
             recent = 1;
         }
 
-        loadIcon(back,   "ic_sysbar_back.png", density);
-        loadIcon(2,      "ic_sysbar_home.png", density);
-        loadIcon(recent, "ic_sysbar_recent.png", density);
+        loadIcon(back,   "ic_sysbar_back.png",   density, isRtl);
+        loadIcon(2,      "ic_sysbar_home.png",   density, isRtl);
+        loadIcon(recent, "ic_sysbar_recent.png", density, isRtl);
     }
 
     @Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 5c08412..baa956d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -30,7 +30,10 @@
 
 public class StatusBar extends CustomBar {
 
-    public StatusBar(Context context, Density density) throws XmlPullParserException {
+    public StatusBar(Context context, Density density, int direction, boolean RtlEnabled)
+            throws XmlPullParserException {
+        // FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar.
+
         super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml");
 
         // FIXME: use FILL_H?
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
index 081ce67..108b651 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
@@ -52,6 +52,8 @@
     private static final String NODE_NAME = "name";
     private static final String NODE_FILE = "file";
 
+    private static final String ATTRIBUTE_VARIANT = "variant";
+    private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant";
     private static final String FONT_SUFFIX_NONE = ".ttf";
     private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf";
     private static final String FONT_SUFFIX_BOLD = "-Bold.ttf";
@@ -189,6 +191,7 @@
         private FontInfo mFontInfo = null;
         private final StringBuilder mBuilder = new StringBuilder();
         private List<FontInfo> mFontList = new ArrayList<FontInfo>();
+        private boolean isCompactFont = true;
 
         private FontHandler(String osFontsLocation) {
             super();
@@ -209,8 +212,21 @@
                 mFontList = new ArrayList<FontInfo>();
             } else if (NODE_FAMILY.equals(localName)) {
                 if (mFontList != null) {
+                    mFontInfo = null;
+                }
+            } else if (NODE_NAME.equals(localName)) {
+                if (mFontList != null && mFontInfo == null) {
                     mFontInfo = new FontInfo();
                 }
+            } else if (NODE_FILE.equals(localName)) {
+                if (mFontList != null && mFontInfo == null) {
+                    mFontInfo = new FontInfo();
+                }
+                if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) {
+                    isCompactFont = false;
+                } else {
+                    isCompactFont = true;
+                }
             }
 
             mBuilder.setLength(0);
@@ -223,7 +239,9 @@
          */
         @Override
         public void characters(char[] ch, int start, int length) throws SAXException {
-            mBuilder.append(ch, start, length);
+            if (isCompactFont) {
+              mBuilder.append(ch, start, length);
+            }
         }
 
         /* (non-Javadoc)
@@ -259,7 +277,7 @@
                 }
             } else if (NODE_FILE.equals(localName)) {
                 // handle a new file for an existing Font Info
-                if (mFontInfo != null) {
+                if (isCompactFont && mFontInfo != null) {
                     String fileName = trimXmlWhitespaces(mBuilder.toString());
                     Font font = getFont(fileName);
                     if (font != null) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index b909bec..87047b3 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -121,7 +121,8 @@
 
         // build the context
         mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
-                mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion());
+                mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion(),
+                mParams.isRtlSupported());
 
         setUp();
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index c14af4a..9ddbbf1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -225,13 +225,15 @@
             SessionParams params = getParams();
             HardwareConfig hardwareConfig = params.getHardwareConfig();
             BridgeContext context = getContext();
-
+            boolean isRtl = Bridge.isLocaleRtl(params.getLocale());
+            int direction = isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
 
             // the view group that receives the window background.
             ViewGroup backgroundView = null;
 
             if (mWindowIsFloating || params.isForceNoDecor()) {
                 backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
+                mViewRoot.setLayoutDirection(direction);
             } else {
                 if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) {
                     /*
@@ -253,12 +255,14 @@
                        the bottom
                      */
                     LinearLayout topLayout = new LinearLayout(context);
+                    topLayout.setLayoutDirection(direction);
                     mViewRoot = topLayout;
                     topLayout.setOrientation(LinearLayout.HORIZONTAL);
 
                     try {
                         NavigationBar navigationBar = new NavigationBar(context,
-                                hardwareConfig.getDensity(), LinearLayout.VERTICAL);
+                                hardwareConfig.getDensity(), LinearLayout.VERTICAL, isRtl,
+                                params.isRtlSupported());
                         navigationBar.setLayoutParams(
                                 new LinearLayout.LayoutParams(
                                         mNavigationBarSize,
@@ -290,6 +294,7 @@
 
                 LinearLayout topLayout = new LinearLayout(context);
                 topLayout.setOrientation(LinearLayout.VERTICAL);
+                topLayout.setLayoutDirection(direction);
                 // if we don't already have a view root this is it
                 if (mViewRoot == null) {
                     mViewRoot = topLayout;
@@ -301,13 +306,22 @@
 
                     // this is the case of soft buttons + vertical bar.
                     // this top layout is the first layout in the horizontal layout. see above)
-                    mViewRoot.addView(topLayout, 0);
+                    if (isRtl && params.isRtlSupported()) {
+                        // If RTL is enabled, layoutlib will mirror the layouts. So, add the
+                        // topLayout to the right of Navigation Bar and layoutlib will draw it
+                        // to the left.
+                        mViewRoot.addView(topLayout);
+                    } else {
+                        // Add the top layout to the left of the Navigation Bar.
+                        mViewRoot.addView(topLayout, 0);
+                    }
                 }
 
                 if (mStatusBarSize > 0) {
                     // system bar
                     try {
-                        StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity());
+                        StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity(),
+                                direction, params.isRtlSupported());
                         systemBar.setLayoutParams(
                                 new LinearLayout.LayoutParams(
                                         LayoutParams.MATCH_PARENT, mStatusBarSize));
@@ -366,7 +380,8 @@
                     // system bar
                     try {
                         NavigationBar navigationBar = new NavigationBar(context,
-                                hardwareConfig.getDensity(), LinearLayout.HORIZONTAL);
+                                hardwareConfig.getDensity(), LinearLayout.HORIZONTAL, isRtl,
+                                params.isRtlSupported());
                         navigationBar.setLayoutParams(
                                 new LinearLayout.LayoutParams(
                                         LayoutParams.MATCH_PARENT, mNavigationBarSize));