Android - change device system locale programmatically
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.
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/
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();
}
}