Reputation: 10030
I'm trying to add RTL language support in my app (specifically Arabic right now). I'll be supporting English as well. What I've done:
android:supportsRtl="true"
to my application tag in AndroidManifest.xml
At first I made these changes manually, then I used Android Studio's "Refactor -> Add RTL Support Where Possible..." menu item.
When I preview my layout files, I can see the RTL preview is properly mirroring the UI; however, even when I use the "Force RTL Layout Direction", my app doesn't display the RTL layout. The system UI is flipped, so that option does work in general.
Is there anything else I need to do to show the RTL layout? I'm hoping I have missed something obvious. I am testing this on an API 21 emulator.
I inherited some of this code. Something might be overriding a setting and forcing this into LTR mode. I made a test application to test out RTL mode and it worked fine. What sort of code could cause the "Force RTL Layout Direction" setting to be ignored (or be overridden)?
I've checked that the locale is being set properly, and it is. I also checked the configuration, and ldrtl
is set. I verified in the signed apk file that android:supportsRtl
made it in and none of the layout files had android:layoutDirection="ltr"
. I've even tried manually putting android:layoutDirection="rtl"
to try to force a layout to mirror, but that didn't work either.`
I added another activity to the project, made it the launcher activity, and made sure it isn't connected to any existing code. It is a subclass of Activity
. The issue still exists. So theoretically this is a configuration issue. Like I said I checked the AndroidManifest.xml file and all the layout files that are generated, and the RTL support and layout changes all made it in. What could be going wrong with configuration?
Upvotes: 27
Views: 17673
Reputation: 9442
Try this...
Create a class to maintain global application state.
public class YourGlobalClass extends Application {
@Override
public void onCreate() {
updateLanguage(this, null);
super.onCreate();
}
public static void updateLanguage(Context ctx, String lang) {
Configuration cfg = new Configuration();
LocalSharedManager manager = new LocalSharedManager(ctx);
String language = manager.GetValueFromSharedPrefs("force_locale");
if (TextUtils.isEmpty(language) && lang == null) {
cfg.locale = Locale.getDefault();
String tmp_locale = "";
tmp_locale = Locale.getDefault().toString().substring(0, 2);
manager.SaveValueToSharedPrefs("force_locale", tmp_locale);
} else if (lang != null) {
cfg.locale = new Locale(lang);
manager.SaveValueToSharedPrefs("force_locale", lang);
} else if (!TextUtils.isEmpty(language)) {
cfg.locale = new Locale(language);
}
ctx.getResources().updateConfiguration(cfg, null);
}
}
Specify global class to your AndroidManifest.xml's tag, which will cause that class to be instantiated with your saved locale, when the process for your application/package is created. Like, android:name="com.your.package.YourGlobalClass" android:supportsRtl="true"
Create following two methods in your MainActivity.java (wherever you want).
public class MainActivity extends ActionBarActivity{
.......
// Implement OnclickListener for english_locale button
findViewById(R.id.english_locale).setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
changeEnglish();
}
});
// Implement OnclickListener for arabic_locale button
findViewById(R.id.arabic_locale).setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
changeArabic();
}
});
/**
* Method that Update UI for Arabic locale.
*/
public void changeArabic() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
String app_locale = "ar";
Locale locale = new Locale(app_locale);
Locale.setDefault(locale);
//Configuration to query the current layout direction.
Configuration config = new Configuration();
config.locale = locale;
getResources().updateConfiguration(config,
getResources().getDisplayMetrics());
Bidi bidi = new Bidi(app_locale,
Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT);
bidi.isRightToLeft();
YourGlobalClass.updateLanguage(getActivity(), "ar");
//Refreshing current fragment
Intent i = getActivity().getIntent();
startActivity(i);
getActivity().finish();
return null;
}
}.execute();
}
/**
* Method that Update UI for Default(English) locale.
*/
public void changeEnglish() {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
String app_locale = "en";
Locale locale = new Locale(app_locale);
Locale.setDefault(locale);
//Configuration to query the current layout direction.
Configuration config = new Configuration();
config.locale = locale;
getResources().updateConfiguration(config,
getResources().getDisplayMetrics());
Bidi bidi = new Bidi(app_locale,
Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT);
bidi.isLeftToRight();
YourGlobalClass.updateLanguage(getActivity(), "en");
//Refreshing current fragment
Intent i = getActivity().getIntent();
startActivity(i);
getActivity().finish();
return null;
}
}.execute();
}
......
//MainActivity end
}
Upvotes: 2
Reputation: 10030
Most obscure bug ever. As I mentioned in the question, most of this code was inherited. It ended up being a bitwise operator issue that was screwing up flags for the application. &=
was being used instead of &
to check if a flag was set.
As noted in the comments, the code was taken from an example in an Android Developers blog post, which explains why so many others have run into this same issue. I filed a bug report, and the blog post has since been silently updated. Here is the original code, which I removed &=
from. Do not use the following code as is. You need to change &=
to &
:
isDebuggable = (appContext.getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE) != 0;
Upvotes: 13