I'm trying to implement app language switch on the runtime, once the user made language changes in app preferences. I have this code in my PreferenceFragment:
public class Fragment_Preferences extends PreferenceFragment {
private SharedPreferences.OnSharedPreferenceChangeListener prefListener;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
prefListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
Log.i("Pref changed", "Settings key changed: " + key);
if(key.equals("language_preference"))
{
String system_language = Locale.getDefault().getLanguage().toUpperCase();
String preference_language = Common_Methods.get_preference_language(getActivity());
Toast.makeText(getActivity(), "Pref changed: "+preference_language, Toast.LENGTH_SHORT).show();
Common_Methods.set_app_interface_language(getActivity(), system_language, preference_language);
}
}
};
prefs.registerOnSharedPreferenceChangeListener(prefListener);
}
}
This is my set_app_interface_language method in Common_Methods class:
public static void set_app_interface_language(Context context, String system_language, String preference_language)
{
if(!preference_language.equals(system_language))
{
Locale locale = new Locale(preference_language.toLowerCase());
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
}
}
I get the Toast message when I change language in preferences. I know that this method works since I call it also from my Fragment_Main. But the language doesn't change on the runtime - I have to exit the app and reopen it, only then I see the changes.
So how can I make the app language change on the runtime, without restarting the app? Thanks!
OK, I think I solved this problem: I call for Common_Methods.set_app_interface_language not in SharedPreferences.OnSharedPreferenceChangeListener, but rather in onRestart method of my Fragment_Main.
I also changed set_app_interface_language to return new config. And once it's returned - I pass it to onConfigurationChanged method to recreate fragment. Now, I did ran into Performing pause of activity that is not resumed... error message once my device's screen turned off. After googling a little about it, I realized that it's non-fatal exception, but I still used Handler to postpone recreate() for 1 millisecond and let the Fragment restart properly. I also set another method in my Common_Methods to check if there were any changes made to the app language and recreate the fragment only if the method returns true; I call this method in onRestart That gave the app some performance boost, since now there's no need to recreate the fragment every time the app restarts.
Related
I have an app where the user can change its language.
Everything is working fine, with just this code on my MainActivity.onCreate():
String lang = PreferenceManager.getDefaultSharedPreferences(this).getString("languagePref", "default");
Configuration config = getResources().getConfiguration();
if( lang.equals("default") ) lang = Locale.getDefault().getLanguage();
config.locale = new Locale(lang);
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
When I restart the app or navigate through activities it's still in the right language.
The only problem is on the PreferenceActivity screen. When the orientation changes, the PreferenceActivity title (and only it) changes to the default device language.
The prefs are still checked correctly, if I go back (closing the PreferenceActivity) the app is still on the right language, but the PreferenceActivity stays wrong until I restart the app.
I tried forcing the code above on the PreferenceActivity.onCreate() and altough debugging seems OK, the PrefenceActivity Title stays wrong.
Here's my PrefenceActivity code:
public class PreferencesActivity extends PreferenceActivity {
#SuppressWarnings("deprecation")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
This behavior doesn't happen on any other Activity :/
Locking the screen orientation is not an option.
Any thoughts?
Thanks.
OK, this fixed it for me.
#Override
protected void onSaveInstanceState(Bundle outState) {
String lang = PreferenceManager.getDefaultSharedPreferences(this).getString("languagePref", "default");
Configuration config = getResources().getConfiguration();
if( lang.equals("default") ) lang = Locale.getDefault().getLanguage();
config.locale = new Locale(lang);
getResources().updateConfiguration(config, getResources().getDisplayMetrics());
super.onSaveInstanceState(outState);
}
I solved this problem using onConfigurationChanged().
In this method I'm saving preferred language again in Configuration object.
OnCreate method of activity is called when orientation is changes, so some property is again set by your code, look at your on oncreate method or lock your screen orientation.
You can lock your screen orientation to your activity by Using
android:screenOrientation="portrait"
or
android:screenOrientation="landscape"
I already know how to change the language of my application (updating the configuration). My code also check if the configuration is changed by the system and "fix it" in the ´onCreate´ method. I even have created a ListPreference to let the user decide the language with one that my app supports (and saves the decision).
Let's say I have 3 activities (A, B and SettingsActivity). Activity A can start activities B and SettingsActivity. Activity B can start SettingsActivity. If the user changes the language inside SettingsActivity, I can update its resources (in this case Strings) without any problem using this code:
//if (Build.VERSION_CODES.HONEYCOMB <= Build.VERSION.SDK_INT) {
// Disabled because it blinks and looks bad
// recreate();
// } else {
startActivity(getIntent());
finish();
overridePendingTransition(0, 0);
// }
However, I'm unable to change the already open activities because I have no reference to them from SettingsActivity.
My question: is there any clean way to update the resources or recreate the already open activities? If I don't find a better solution, my approach will be one of the above:
Start activities using startActivityForResultand return a code to trigger the code I already use to recreate the activity.
Check inside the onResume method if the current language has changed and do the same thing.
At the end what I did was this:
#Override
protected void onStart() {
super.onStart();
if (!locale.equals(getResources().getConfiguration().locale)) {
finish();
startActivity(getIntent());
overridePendingTransition(0, 0);
return;
}
}
Where locale is a variable assigned in my onCreate method:
private Locale locale;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((Application) getApplication()).refreshLanguage();
locale = getResources().getConfiguration().locale;
setContentView(R.layout.activity_main);
//moar code
}
Finally, for the sake of posting code, here is my refreshLanguage method(s):
boolean refreshLanguage() {
return refreshLanguage(PreferenceManager.getDefaultSharedPreferences(this));
}
boolean refreshLanguage(SharedPreferences sharedPreferences) {
if (sharedPreferences.contains("language")) {
int languageIndex = Integer.parseInt(sharedPreferences.getString("language", "0"));
if (!getResources().getConfiguration().locale.equals(languages[languageIndex])) {
Configuration config = new Configuration();
config.locale = languages[languageIndex];
getResources().updateConfiguration(config, null);
return true;
}
}
return false;
}
Notice that I use onStart rather than onResume because I'm not switching between transparent activities or using dialogs.
I have to change the language on runtime in Android (yes, I know that this is not a good behaviour, but this is a requirement...).
So I have a basic class, from which every activity extends. This class has the following function:
public static void changeLanguage(Context context) {
Resources res = context.getResources();
/*
* Change locale settings in the app.
*/
DisplayMetrics dm = res.getDisplayMetrics();
/*
* Store and load data in this preferences
*/
android.content.res.Configuration conf = res.getConfiguration();
String[] localArray = res.getStringArray(R.array.language_short_array);
if (localArray != null) {
SharedPreferences settings = context.getSharedPreferences(
MyService.APP_ID, MODE_PRIVATE);
conf.locale = new Locale(localArray[settings.getInt(
PREFERED_LANGUAGE_KEY, 0)]);
res.updateConfiguration(conf, dm);
}
}
I will call this method in onCreate:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
changeLanguage(this);
}
This is in the super-class. My Activities extends from it and call super.onCreate at first. After this call, they set their layout and initializes their settings...
I thought that my lines of code would make it. But I have the following problem: Sometimes, the activity changes the language and sometimes not!
If I set a debug breakpoint on it and after the programm pauses I press continue, everything works fine. So I think, in some cases, where my Application is "slow enough", the language will change correctly, whereas the language won't change if the application is too fast...
Is there any solution of my problem? How can I be sure, that my language will change correctly in any time?
Thanks a lot!
Edit: Here is an example for a class which extends from my super-class
public class MainMenuActivity extends BaseActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start);
}
}
changeLanguage(this); only needs to be called when the language has changed or when the App is loaded.. res.updateConfiguration(conf, dm); updates the global config and is specific to your app instance not your activity instance.
When you change the locale in an Activity you have to recreate that Activity to see your language change. This can be easily done by forcing an orientation change then forcing it back like this:
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
If you hit back after a language change you will see the old language because onCreate is not called. You will have to detect in onResume that the language changed and force a recreate of the Activity.
-= EDIT =-
Using Screen Orientation to reload the Activity has proven to be a bit buggy on some devices. I am now using this to reload the current Activity:
public static void resetScreen(Activity activity) {
if (activity != null) {
if (Build.VERSION.SDK_INT >= 11) {
activity.recreate();
} else {
Intent intent = activity.getIntent();
activity.overridePendingTransition(0, 0);
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
activity.finish();
activity.overridePendingTransition(0, 0);
activity.startActivity(intent);
}
}
}
I think you need to post an example of an Activity that extends your superclass. Calling changeLanguage in onCreate() seems suspicious to me, though. That's only going to run when the app is first initialized. To change the language once your app is loaded, you'd have to stop it and re-create it.
I have a PreferenceActivty in my Android app, which due to compatibility reasons I use via the getPreferenceScreen() method and some Preference objects which I create in code, mostly CheckBoxPreference and SwitchPreference.
Up to the previous version of my app there were 8 preferences in total and everything worked fine, but now I added 2 more preferences and I'm experiencing a REALLY weird issue.
The second preference on the screen is a SwitchPreference. When I open the activity, it is checked. If I scroll down the screen without actually changing anything, suddenly its value is automatically set to OFF. I tried adding an OnChangeListener to the Preference and implementing OnSharedPreferenceChangeListener, but the results are the same: once that particular Preference disappears from the screen, it is turned OFF. If it's set to OFF, it keeps its value and the change listener is not called.
Does anyone have any idea as to why could this be happening? I'm completely lost...
Thanks in advance!
The code for my preferences is basically this, repeated 5 times for 5 different settings, on the onCreate method:
controlWifiPreference = new CheckBoxPreference(this);
controlWifiPreference.setKey(Constants.PREF_1_KEY);
getPreferenceScreen().addPreference(controlWifiPreference);
wifiPreference = new SwitchPreference(this);
wifiPreference.setKey(Constants.PREF_2_KEY);
getPreferenceScreen().addPreference(wifiPreference);
Since the preferences are inside a TabActivity, on the onResume method I call setChecked() for every preference to set its value again, though I'm not sure that it's completely neccessary.
And, finally, I have an onSharedPreferenceChanged method that activates/deactivates preferences when others are clicked, because I couldn't get the setDependency method to work. It's something like this (again, repeated five times):
if (key.equals(controlWifiPreference.getKey())) {
wifiPreference.setEnabled(controlWifiPreference.isChecked());
}
Turns out it was an Android bug in the SwitchPreference class. Someone (who I'm VERY thankful to ;)) reported it to b.android.com and even posted a workaround. It's all here: https://code.google.com/p/android/issues/detail?id=26194
How you implemented preferences inside TabActivity?I checked your code in my own IDE inside a PreferenceActivity and its working like a charm.If you need to have some pseudo prefences inside your activity, you should not use prefernces and instead, you will need to use normal forms items and save their values to the preferences manually.here is the code i tested and its working ok:
public class PreferencesFromCode extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
private SwitchPreference switchPref;
private CheckBoxPreference checkboxPref;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setPreferenceScreen(createPreferenceHierarchy());
}
private PreferenceScreen createPreferenceHierarchy() {
// Root
#SuppressWarnings("deprecation")
PreferenceScreen root = getPreferenceManager().createPreferenceScreen(
this);
// Inline preferences
PreferenceCategory inlinePrefCat = new PreferenceCategory(this);
inlinePrefCat.setTitle(R.string.inline_preferences);
root.addPreference(inlinePrefCat);
// Checkbox preference
checkboxPref = new CheckBoxPreference(this);
checkboxPref.setKey("checkbox_preference");
checkboxPref.setTitle(R.string.title_checkbox_preference);
checkboxPref.setSummary(R.string.summary_checkbox_preference);
inlinePrefCat.addPreference(checkboxPref);
// Switch preference
switchPref = new SwitchPreference(this);
switchPref.setKey("switch_preference");
switchPref.setTitle(R.string.title_switch_preference);
switchPref.setSummary(R.string.summary_switch_preference);
inlinePrefCat.addPreference(switchPref);
/*
* The Preferences screenPref serves as a screen break (similar to page
* break in word processing). Like for other preference types, we assign
* a key here so that it is able to save and restore its instance state.
*/
// Screen preference
PreferenceScreen screenPref = getPreferenceManager()
.createPreferenceScreen(this);
screenPref.setKey("screen_preference");
screenPref.setTitle(R.string.title_screen_preference);
screenPref.setSummary(R.string.summary_screen_preference);
return root;
}
#Override
protected void onResume() {
super.onResume();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
/*
* getPreferenceScreen().getSharedPreferences()
* .registerOnSharedPreferenceChangeListener(this);
*/
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
Log.i("ghjg", "changed key is : " + key);
if (key.equals(checkboxPref.getKey())) {
switchPref.setEnabled(checkboxPref.isChecked());
}
}
}
However you may override onContentChanged() and see what happens.
I would like the application language to be set according to the user preferences but up till now it doesn't work how I would like it to.
I have set the default values: strings.xml and also values-es with a strings.xml inside in spanish. I have a menu option which brings the user to a Preference activity where he can amon gother things chose the language.
So here are some extracts of the code:
public class Preference extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
......
// Set up a listener whenever a key changes
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
...}
//(......)
//and here I have the listener so when the language pref changes value the locale gets changed.
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
if (key.equals("listPref2")) {
String idioma = sharedPreferences.getString("listPref2", "catala");
if ("castella".equals(idioma)) {
idioma = "es_ES";
Locale locale = new Locale(idioma);
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getApplicationContext().getResources().updateConfiguration(config,
getBaseContext().getResources().getDisplayMetrics());
}
}
}
So when I change the language it works but then when I come back later or restart the emulator the language gets back to default locale the en_US and the app language gets changed back to default again. What can I do to sort that out?
I know I can get this preference (which I can access to from all my activities) and then each time set up the locale but I find it a bit heavy isn't there a way to do it in a more elegant way?
What I would like to do is if the user sets up the language so when he comes back 2 days later he doesn't have to change the language again.
Any ideas?
OK it may help someone. I have added the folowing to the main activity manifest:
android:configChanges="locale"
Then when the user choses the preferences I have put a confirm button and then this button brings you to main activity that is why the lnagages gets reset.
I have a static class where I have this code to change the locale:
public static void updateLanguage(Context context, String idioma) {
if (!"".equals(idioma)) {
if ("castella".equals(idioma)) {
idioma = "es_ES";
} else if ("catala".equals(idioma)) {
idioma = "ca_ES";
}
Locale locale = new Locale(idioma);
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
context.getResources().updateConfiguration(config, null);
}
}
end at every activity I have like 20 of them I call this method before:
setContentView(R.layout.list_event);
With these methods when I rotate the screen the activities don't change the language
here is a link to a blog that helped me:
http://adrianvintu.com/blogengine/post/Force-Locale-on-Android.aspx
I would think that you need to be setting the locale in the MainActivity onCreate method. The same way you are setting it when the onSharedPreferenceChanged method.