From 88db0ee2afbae38b53a0527506f0890914a7f115 Mon Sep 17 00:00:00 2001 From: Deepanshu Gupta Date: Wed, 19 Jun 2013 17:29:12 -0700 Subject: Fix layout rendering for RTL locales This changeset adds the framework resources for RTL locales and mirrors the layout if the application is RTL aware. Use ICU to check the character orientation of the locale - right to left or left to right. Set the layout direction on the top level layout accordingly. Also, load the RTL resources for Nav Bar when the locale is RTL. Change-Id: I1ed0d516ab64120a0abca413ba678036661508f8 --- tools/layoutlib/bridge/.classpath | 1 + tools/layoutlib/bridge/Android.mk | 1 + .../resources/bars/ldrtl-hdpi/ic_sysbar_back.png | Bin 0 -> 904 bytes .../resources/bars/ldrtl-hdpi/ic_sysbar_recent.png | Bin 0 -> 533 bytes .../resources/bars/ldrtl-mdpi/ic_sysbar_back.png | Bin 0 -> 617 bytes .../resources/bars/ldrtl-mdpi/ic_sysbar_recent.png | Bin 0 -> 423 bytes .../resources/bars/ldrtl-xhdpi/ic_sysbar_back.png | Bin 0 -> 1250 bytes .../bars/ldrtl-xhdpi/ic_sysbar_recent.png | Bin 0 -> 552 bytes .../src/com/android/layoutlib/bridge/Bridge.java | 20 +++++++++++++++- .../layoutlib/bridge/android/BridgeContext.java | 6 ++++- .../android/layoutlib/bridge/bars/CustomBar.java | 26 +++++++++++++++++---- .../layoutlib/bridge/bars/NavigationBar.java | 13 +++++++---- .../android/layoutlib/bridge/bars/StatusBar.java | 5 +++- .../layoutlib/bridge/impl/RenderAction.java | 3 ++- .../layoutlib/bridge/impl/RenderSessionImpl.java | 25 ++++++++++++++++---- 15 files changed, 81 insertions(+), 19 deletions(-) create mode 100644 tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png create mode 100644 tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png create mode 100644 tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png create mode 100644 tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png create mode 100644 tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png create mode 100644 tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png (limited to 'tools/layoutlib') 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 @@ + 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_RESOURCE_DIRS := resources 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 Binary files /dev/null and b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png 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 Binary files /dev/null and b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png 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 Binary files /dev/null and b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png 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 Binary files /dev/null and b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png 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 Binary files /dev/null and b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png 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 Binary files /dev/null and b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png differ 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.resources.ResourceType; 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 @@ import java.util.concurrent.locks.ReentrantLock; */ 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 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { 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 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { 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 @@ public final class BridgeContext extends Context { RenderResources renderResources, IProjectCallback projectCallback, Configuration config, - int targetSdkVersion) { + int targetSdkVersion, + boolean hasRtlSupport) { mProjectKey = projectKey; mMetrics = metrics; mProjectCallback = projectCallback; @@ -142,6 +143,9 @@ public final class BridgeContext extends Context { 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.android.BridgeXmlBlockParser; 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 @@ abstract class CustomBar extends LinearLayout { } } - 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 @@ import android.widget.TextView; 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 @@ public class NavigationBar extends CustomBar { // 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 @@ import android.widget.TextView; 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/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 @@ public abstract class RenderAction extends FrameworkReso // 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 @@ public class RenderSessionImpl extends RenderAction { 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 @@ public class RenderSessionImpl extends RenderAction { 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 @@ public class RenderSessionImpl extends RenderAction { 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 @@ public class RenderSessionImpl extends RenderAction { // 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 @@ public class RenderSessionImpl extends RenderAction { // 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)); -- cgit v1.1