Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - change device system locale programmatically

Tags:

android

locale

I want to change device locale (not just the application locale) from my app, like the user can do in Settings -> Language & Keyboard -> Language.

Can someone please explain how to do it? I've been searching for hours and can not find a way.

like image 994
avicohh Avatar asked Aug 18 '14 01:08

avicohh


3 Answers

Has someone else way. Like previous method, this request permissions:

    <uses-permission android:name="android.permission.CHANGE_CONFIGURATION"/>

It possible get if we have admin access on device, this article https://snow.dog/blog/kiosk-mode-android determines how to access the admin access (root not needed).

And if we allow write settings:

if (!Settings.System.canWrite(this)) {
    Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
    intent.setData(Uri.parse("package:" + this.getPackageName()));
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivityForResult(intent, 12345);
}

And for get permissions we should run:

pm grant sk.lurity.defenqo android.permission.CHANGE_CONFIGURATION

Next, when I have access to change configuration, I try to set locale this way, but this way not working. Don't know why:

try {
  final String locale=(String)(Settings.System.class.getField("SYSTEM_LOCALES").get(null));
  Settings.System.putString(getContentResolver(),locale,"en-US,ru-UA");
} catch (NoSuchFieldException | IllegalAccessException ex){
  ex.printStackTrace();
}

with: You cannot change private secure settings. I found this article, https://www.fatalerrors.org/a/you-cannot-keep-your-settings-in-the-secure-settings.html But in fact this option has in sources:

        /**
         * These are all public system settings
         *
         * @hide
         */
        @UnsupportedAppUsage
        public static final Set<String> PUBLIC_SETTINGS = new ArraySet<>();
        static {
....
            PUBLIC_SETTINGS.add(SYSTEM_LOCALES);

in /android/provider/Settings.java So i can assume, this way start working, in API25, but i have only API24. i also try to grant permission:

<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>

But it is not solve nothing, at all.

However, i found solution in sources near SYSTEM_LOCALES definition:

        /**
         * The serialized system locale value.
         *
         * Do not use this value directory.
         * To get system locale, use {@link LocaleList#getDefault} instead.
         * To update system locale, use {@link com.android.internal.app.LocalePicker#updateLocales}
         * instead.
         * @hide
         */
        public static final String SYSTEM_LOCALES = "system_locales";

In this case i try call it internal class over reflection:

try {
    Class localePicker=Class.forName("com.android.internal.app.LocalePicker");
    Method updateLocales=localePicker.getMethod("updateLocales", LocaleList.class);
    LocaleList localeList=new LocaleList(new Locale("sk","SK"));
    updateLocales.invoke(null, localeList);
}catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException ex){
            ex.printStackTrace();
}

And this working, almost same like @Nasrudeen answer. It should working start from Android 7.0. And yes, again it is hack. Not as terrible as many of the solutions that I found, but also a hack.

like image 176
Lubagov Avatar answered Sep 18 '22 13:09

Lubagov


Yes you can change device locale for any android version make sure that your app is system app.Heres the code to help you out.Please rate my answer if you like it.

//getting the languages that are shown same as in our device settings
     String[] systemLocaleIetfLanguageTags = getAssets().getLocales();
            Arrays.sort(systemLocaleIetfLanguageTags);
            this.locale_data = new ArrayList();
            for (String ietfLanguageTag : systemLocaleIetfLanguageTags)
            {
                if (ietfLanguageTag != null && ietfLanguageTag.length() == 5)
                {
                    this.locale_data.add(new Loc(ietfLanguageTag));
                }
            }
            adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, locale_data);
            // Assign adapter to ListView
            lv.setAdapter(adapter);
            lv.setOnItemClickListener(new OnItemClickListener()
            {
                @Override
                public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3)
                {
                    int itemPosition = position;
                    Loc loc = (Loc) ChangeLanguage.this.adapter.getItem(position);
                    Toast.makeText(ChangeLanguage.this, "language activated successfully !!", 0).show();
                    ChangeLanguage.change_setting(loc.getLocale());
                }
            });
        }
    //to change the locale
        public static void change_setting(Locale loc)
        {
            try
            {
                Class<?> activityManagerNative = Class.forName("android.app.ActivityManagerNative");
                Object am = activityManagerNative.getMethod("getDefault", new Class[0]).invoke(activityManagerNative, new Object[0]);
                Object config = am.getClass().getMethod("getConfiguration", new Class[0]).invoke(am, new Object[0]);
                config.getClass().getDeclaredField("locale").set(config, loc);
                config.getClass().getDeclaredField("userSetLocale").setBoolean(config, true);
                am.getClass().getMethod("updateConfiguration", new Class[] { Configuration.class }).invoke(am, new Object[] { config });
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
like image 43
tanmeet Avatar answered Sep 21 '22 13:09

tanmeet


This app does want you want https://play.google.com/store/apps/details?id=org.gnvo.langpicker&hl=en

Here is its source code https://code.google.com/p/languagepickerwidget/

Here is its main logic http://bin-liu.blogspot.in/2012/05/how-to-change-system-locale-calling.html

EDIT

After 4.2 new Jelly Bean, The protection level definition of CHANGE_CONFIGURATION has been changed,so the app will not work above 4.2, solution is http://droider.eu/2013/07/22/morelocale-2-not-working-on-4-2-2-without-su-or-pm/

like image 27
Nasrudeen Avatar answered Sep 19 '22 13:09

Nasrudeen