Fix layout rendering for RTL locales [DO NOT MERGE]
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 (cherry picked from commit eee0ea7a0b85ea6345eb7b8da5dbf17306c7339c)
This commit is contained in:
@@ -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/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/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/tools-common/tools-common-prebuilt.jar"/>
|
||||||
|
<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/icu4j/icu4j.jar"/>
|
||||||
<classpathentry kind="output" path="bin"/>
|
<classpathentry kind="output" path="bin"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ LOCAL_JAVA_RESOURCE_DIRS := resources
|
|||||||
|
|
||||||
LOCAL_JAVA_LIBRARIES := \
|
LOCAL_JAVA_LIBRARIES := \
|
||||||
kxml2-2.3.0 \
|
kxml2-2.3.0 \
|
||||||
|
icu4j \
|
||||||
layoutlib_api-prebuilt \
|
layoutlib_api-prebuilt \
|
||||||
tools-common-prebuilt
|
tools-common-prebuilt
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 904 B |
Binary file not shown.
|
After Width: | Height: | Size: 533 B |
Binary file not shown.
|
After Width: | Height: | Size: 617 B |
Binary file not shown.
|
After Width: | Height: | Size: 423 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 552 B |
@@ -35,6 +35,7 @@ import com.android.resources.ResourceType;
|
|||||||
import com.android.tools.layoutlib.create.MethodAdapter;
|
import com.android.tools.layoutlib.create.MethodAdapter;
|
||||||
import com.android.tools.layoutlib.create.OverrideMethod;
|
import com.android.tools.layoutlib.create.OverrideMethod;
|
||||||
import com.android.util.Pair;
|
import com.android.util.Pair;
|
||||||
|
import com.ibm.icu.util.ULocale;
|
||||||
|
|
||||||
import android.content.res.BridgeAssetManager;
|
import android.content.res.BridgeAssetManager;
|
||||||
import android.graphics.Bitmap;
|
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 {
|
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 {
|
public static class StaticMethodNotImplementedException extends RuntimeException {
|
||||||
private static final long serialVersionUID = 1L;
|
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.ANIMATED_VIEW_MANIPULATION,
|
||||||
Capability.ADAPTER_BINDING,
|
Capability.ADAPTER_BINDING,
|
||||||
Capability.EXTENDED_VIEWINFO,
|
Capability.EXTENDED_VIEWINFO,
|
||||||
Capability.FIXED_SCALABLE_NINE_PATCH);
|
Capability.FIXED_SCALABLE_NINE_PATCH,
|
||||||
|
Capability.RTL);
|
||||||
|
|
||||||
|
|
||||||
BridgeAssetManager.initSystem();
|
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");
|
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
|
* Returns the lock for the bridge
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -132,7 +132,8 @@ public final class BridgeContext extends Context {
|
|||||||
RenderResources renderResources,
|
RenderResources renderResources,
|
||||||
IProjectCallback projectCallback,
|
IProjectCallback projectCallback,
|
||||||
Configuration config,
|
Configuration config,
|
||||||
int targetSdkVersion) {
|
int targetSdkVersion,
|
||||||
|
boolean hasRtlSupport) {
|
||||||
mProjectKey = projectKey;
|
mProjectKey = projectKey;
|
||||||
mMetrics = metrics;
|
mMetrics = metrics;
|
||||||
mProjectCallback = projectCallback;
|
mProjectCallback = projectCallback;
|
||||||
@@ -142,6 +143,9 @@ public final class BridgeContext extends Context {
|
|||||||
|
|
||||||
mApplicationInfo = new ApplicationInfo();
|
mApplicationInfo = new ApplicationInfo();
|
||||||
mApplicationInfo.targetSdkVersion = targetSdkVersion;
|
mApplicationInfo.targetSdkVersion = targetSdkVersion;
|
||||||
|
if (hasRtlSupport) {
|
||||||
|
mApplicationInfo.flags = mApplicationInfo.flags | ApplicationInfo.FLAG_SUPPORTS_RTL;
|
||||||
|
}
|
||||||
|
|
||||||
mWindowManager = new WindowManagerImpl(mMetrics);
|
mWindowManager = new WindowManagerImpl(mMetrics);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
|
|||||||
import com.android.layoutlib.bridge.impl.ParserFactory;
|
import com.android.layoutlib.bridge.impl.ParserFactory;
|
||||||
import com.android.layoutlib.bridge.impl.ResourceHelper;
|
import com.android.layoutlib.bridge.impl.ResourceHelper;
|
||||||
import com.android.resources.Density;
|
import com.android.resources.Density;
|
||||||
|
import com.android.resources.LayoutDirection;
|
||||||
import com.android.resources.ResourceType;
|
import com.android.resources.ResourceType;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
@@ -86,38 +87,53 @@ abstract class CustomBar extends LinearLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getIcon(String iconName, Density[] densityInOut, String[] pathOut,
|
private InputStream getIcon(String iconName, Density[] densityInOut, LayoutDirection direction,
|
||||||
boolean tryOtherDensities) {
|
String[] pathOut, boolean tryOtherDensities) {
|
||||||
// current density
|
// current density
|
||||||
Density density = densityInOut[0];
|
Density density = densityInOut[0];
|
||||||
|
|
||||||
// bitmap url relative to this class
|
// 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]);
|
InputStream stream = getClass().getResourceAsStream(pathOut[0]);
|
||||||
if (stream == null && tryOtherDensities) {
|
if (stream == null && tryOtherDensities) {
|
||||||
for (Density d : Density.values()) {
|
for (Density d : Density.values()) {
|
||||||
if (d != density) {
|
if (d != density) {
|
||||||
densityInOut[0] = d;
|
densityInOut[0] = d;
|
||||||
stream = getIcon(iconName, densityInOut, pathOut, false /*tryOtherDensities*/);
|
stream = getIcon(iconName, densityInOut, direction, pathOut,
|
||||||
|
false /*tryOtherDensities*/);
|
||||||
if (stream != null) {
|
if (stream != null) {
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// couldn't find resource with direction qualifier. try without.
|
||||||
|
if (direction != null) {
|
||||||
|
return getIcon(iconName, densityInOut, null, pathOut, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void loadIcon(int index, String iconName, Density density) {
|
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);
|
View child = getChildAt(index);
|
||||||
if (child instanceof ImageView) {
|
if (child instanceof ImageView) {
|
||||||
ImageView imageView = (ImageView) child;
|
ImageView imageView = (ImageView) child;
|
||||||
|
|
||||||
String[] pathOut = new String[1];
|
String[] pathOut = new String[1];
|
||||||
Density[] densityInOut = new Density[] { density };
|
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*/);
|
true /*tryOtherDensities*/);
|
||||||
density = densityInOut[0];
|
density = densityInOut[0];
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
package com.android.layoutlib.bridge.bars;
|
package com.android.layoutlib.bridge.bars;
|
||||||
|
|
||||||
import com.android.resources.Density;
|
import com.android.resources.Density;
|
||||||
|
import com.android.layoutlib.bridge.Bridge;
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
@@ -26,7 +27,8 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
public class NavigationBar extends CustomBar {
|
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");
|
super(context, density, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml");
|
||||||
|
|
||||||
setBackgroundColor(0xFF000000);
|
setBackgroundColor(0xFF000000);
|
||||||
@@ -37,14 +39,15 @@ public class NavigationBar extends CustomBar {
|
|||||||
// 0 is a spacer.
|
// 0 is a spacer.
|
||||||
int back = 1;
|
int back = 1;
|
||||||
int recent = 3;
|
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;
|
back = 3;
|
||||||
recent = 1;
|
recent = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadIcon(back, "ic_sysbar_back.png", density);
|
loadIcon(back, "ic_sysbar_back.png", density, isRtl);
|
||||||
loadIcon(2, "ic_sysbar_home.png", density);
|
loadIcon(2, "ic_sysbar_home.png", density, isRtl);
|
||||||
loadIcon(recent, "ic_sysbar_recent.png", density);
|
loadIcon(recent, "ic_sysbar_recent.png", density, isRtl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
public class StatusBar extends CustomBar {
|
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");
|
super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml");
|
||||||
|
|
||||||
// FIXME: use FILL_H?
|
// FIXME: use FILL_H?
|
||||||
|
|||||||
@@ -121,7 +121,8 @@ public abstract class RenderAction<T extends RenderParams> extends FrameworkReso
|
|||||||
|
|
||||||
// build the context
|
// build the context
|
||||||
mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
|
mContext = new BridgeContext(mParams.getProjectKey(), metrics, resources,
|
||||||
mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion());
|
mParams.getProjectCallback(), getConfiguration(), mParams.getTargetSdkVersion(),
|
||||||
|
mParams.isRtlSupported());
|
||||||
|
|
||||||
setUp();
|
setUp();
|
||||||
|
|
||||||
|
|||||||
@@ -225,13 +225,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
|||||||
SessionParams params = getParams();
|
SessionParams params = getParams();
|
||||||
HardwareConfig hardwareConfig = params.getHardwareConfig();
|
HardwareConfig hardwareConfig = params.getHardwareConfig();
|
||||||
BridgeContext context = getContext();
|
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.
|
// the view group that receives the window background.
|
||||||
ViewGroup backgroundView = null;
|
ViewGroup backgroundView = null;
|
||||||
|
|
||||||
if (mWindowIsFloating || params.isForceNoDecor()) {
|
if (mWindowIsFloating || params.isForceNoDecor()) {
|
||||||
backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
|
backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
|
||||||
|
mViewRoot.setLayoutDirection(direction);
|
||||||
} else {
|
} else {
|
||||||
if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) {
|
if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) {
|
||||||
/*
|
/*
|
||||||
@@ -253,12 +255,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
|||||||
the bottom
|
the bottom
|
||||||
*/
|
*/
|
||||||
LinearLayout topLayout = new LinearLayout(context);
|
LinearLayout topLayout = new LinearLayout(context);
|
||||||
|
topLayout.setLayoutDirection(direction);
|
||||||
mViewRoot = topLayout;
|
mViewRoot = topLayout;
|
||||||
topLayout.setOrientation(LinearLayout.HORIZONTAL);
|
topLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
NavigationBar navigationBar = new NavigationBar(context,
|
NavigationBar navigationBar = new NavigationBar(context,
|
||||||
hardwareConfig.getDensity(), LinearLayout.VERTICAL);
|
hardwareConfig.getDensity(), LinearLayout.VERTICAL, isRtl,
|
||||||
|
params.isRtlSupported());
|
||||||
navigationBar.setLayoutParams(
|
navigationBar.setLayoutParams(
|
||||||
new LinearLayout.LayoutParams(
|
new LinearLayout.LayoutParams(
|
||||||
mNavigationBarSize,
|
mNavigationBarSize,
|
||||||
@@ -290,6 +294,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
|||||||
|
|
||||||
LinearLayout topLayout = new LinearLayout(context);
|
LinearLayout topLayout = new LinearLayout(context);
|
||||||
topLayout.setOrientation(LinearLayout.VERTICAL);
|
topLayout.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
topLayout.setLayoutDirection(direction);
|
||||||
// if we don't already have a view root this is it
|
// if we don't already have a view root this is it
|
||||||
if (mViewRoot == null) {
|
if (mViewRoot == null) {
|
||||||
mViewRoot = topLayout;
|
mViewRoot = topLayout;
|
||||||
@@ -301,13 +306,22 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
|||||||
|
|
||||||
// this is the case of soft buttons + vertical bar.
|
// this is the case of soft buttons + vertical bar.
|
||||||
// this top layout is the first layout in the horizontal layout. see above)
|
// 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) {
|
if (mStatusBarSize > 0) {
|
||||||
// system bar
|
// system bar
|
||||||
try {
|
try {
|
||||||
StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity());
|
StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity(),
|
||||||
|
direction, params.isRtlSupported());
|
||||||
systemBar.setLayoutParams(
|
systemBar.setLayoutParams(
|
||||||
new LinearLayout.LayoutParams(
|
new LinearLayout.LayoutParams(
|
||||||
LayoutParams.MATCH_PARENT, mStatusBarSize));
|
LayoutParams.MATCH_PARENT, mStatusBarSize));
|
||||||
@@ -366,7 +380,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
|||||||
// system bar
|
// system bar
|
||||||
try {
|
try {
|
||||||
NavigationBar navigationBar = new NavigationBar(context,
|
NavigationBar navigationBar = new NavigationBar(context,
|
||||||
hardwareConfig.getDensity(), LinearLayout.HORIZONTAL);
|
hardwareConfig.getDensity(), LinearLayout.HORIZONTAL, isRtl,
|
||||||
|
params.isRtlSupported());
|
||||||
navigationBar.setLayoutParams(
|
navigationBar.setLayoutParams(
|
||||||
new LinearLayout.LayoutParams(
|
new LinearLayout.LayoutParams(
|
||||||
LayoutParams.MATCH_PARENT, mNavigationBarSize));
|
LayoutParams.MATCH_PARENT, mNavigationBarSize));
|
||||||
|
|||||||
Reference in New Issue
Block a user