Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resources and layout direction rendered incorrectly only on Android 8.0 and above

I have a multilingual app with primary language English and secondary language Arabic.

I am calling setLocale() in the onCreate() of every Activity in my app:

public static void setLocale(Locale locale){
    Locale.setDefault(locale);
    Context context = MyApplication.getInstance();
    final Resources resources = context.getResources();
    final Configuration config = resources.getConfiguration();
    config.setLocale(locale);
    context.getResources().updateConfiguration(config,
            resources.getDisplayMetrics());
}

where locale is one of the following:

![enter image description here

The above method is called before super.onCreate(savedInstanceState) gets called.

As described in the documentation,

  • I have added android:supportsRtl="true" in the manifest.
  • I have changed all xml properties with left and right attributes to start and end respectively.
  • I have put Arabic-language strings in res\values-ar\strings folder and drawable resources in res\drawable-ar folder (and similarly for other resources).

The above setup works properly. After changing the Locale to ar-AE, Arabic text & resources are correctly displayed in my Activities.

However, there is a problem with both resources and layout direction for all Android devices with version 8.0 and above.

On a device with version less than 8.0, an RTL screen correctly looks like this:

enter image description here

And on all devices with 8.0+, the same screen turns up looking like this:

enter image description here

which is wrong.

It turns out that both the direction and the resources are getting displayed incorrectly.

There are two problems here:

  • The correct Locale does not seem to be updated across the app configuration.
  • The direction of the text and drawables is opposite of what it should be.

With respect to direction, a curious method called setLayoutDirection() exists which I had not noticed before.

I would like to know what this problem is, why it happens in Oreo and what is the solution for it. Please help / comment on this.

EDIT:

According to the API Differences report, the updateConfiguration() method was indeed deprecated in Android 7.1 (API level 25).

Also, found all the relevant posts on this. In order of importance:

1. Android N change language programmatically.

2. Android context.getResources.updateConfiguration() deprecated.

3. How to change Android O / Oreo / api 26 app language.

4. Android RTL issue in API 24 and higher on locale change

5. Change language programmatically (Android N 7.0 - API 24).

6. Android N - Change Locale in runtime.

7. RTL layout bug in android Oreo.

like image 490
Y.S Avatar asked Sep 05 '18 14:09

Y.S


People also ask

What is Force RTL layout on Android?

android.util.LayoutDirection. A class for defining layout directions. A layout direction can be left-to-right (LTR) or right-to-left (RTL). It can also be inherited (from a parent) or deduced from the default language script of a locale.

What are the resources how different types of resources managed in Android explain?

Resources are the additional files and static content that your code uses, such as bitmaps, layout definitions, user interface strings, animation instructions, and more. You should always externalize app resources such as images and strings from your code, so that you can maintain them independently.

What is resource manager in Android?

Resource Manager is a tool window for importing, creating, managing, and using resources in your app.


1 Answers

The updateConfiguration() method was deprecated

Now we need to use createConfigurationContext()

I have managed this way

create a new class ContextWrapper

import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.LocaleList;

import java.util.Locale;

public class ContextWrapper extends android.content.ContextWrapper {

    public ContextWrapper(Context base) {
        super(base);
    }

    public static ContextWrapper wrap(Context context, Locale newLocale) {

        Resources res = context.getResources();
        Configuration configuration = res.getConfiguration();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration.setLocale(newLocale);

            LocaleList localeList = new LocaleList(newLocale);
            LocaleList.setDefault(localeList);
            configuration.setLocales(localeList);

            context = context.createConfigurationContext(configuration);

        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLocale(newLocale);
            context = context.createConfigurationContext(configuration);

        } else {
            configuration.locale = newLocale;
            res.updateConfiguration(configuration, res.getDisplayMetrics());
        }

        return new ContextWrapper(context);
    }}

create a new class of BaseActivity

import android.content.Context;

import android.support.v7.app.AppCompatActivity;

import java.util.Locale;

/**
 * Created by nilesh on 20/3/18.
 */

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void attachBaseContext(Context newBase) {

        Locale newLocale;

        String lang = new PrefManager(newBase).getLanguage();

        if (lang.equals("zh_CN")) {
            newLocale = new Locale("zh");
        } else {
            newLocale = new Locale(lang);
        }


        Context context = ContextWrapper.wrap(newBase, newLocale);
        super.attachBaseContext(context);
    }
}

Create a PrefManager class to store locale

import android.content.Context;
import android.content.SharedPreferences;

public class PrefManager {

    private SharedPreferences.Editor editor;
    private Context mContext;
    private SharedPreferences prefs;
    private final String LANGUAGE = "language";
    private final String PREF = "user_data";

    public PrefManager(Context mContext) {
        this.mContext = mContext;
    }

    public String getLanguage() {
        this.prefs = this.mContext.getSharedPreferences(PREF, 0);
        return this.prefs.getString(LANGUAGE, "en_US");
    }

    public void setLanguage(String language) {
        this.editor = this.mContext.getSharedPreferences(PREF, 0).edit();
        this.editor.putString(LANGUAGE, language);
        this.editor.apply();
    }

}

Now you need to extends your BaseActivity in your all activity like

public class OrdersActivity extends BaseActivity

Now when your need to change Locale just update the value in PrefManager and restart your activity

    PrefManager prefManager= new PrefManager(this);
    prefManager.setLanguage("zh_CN");
    //  restart your activity

NOTE

You can download source code from github repo

like image 109
AskNilesh Avatar answered Oct 02 '22 05:10

AskNilesh