SharedPreference: OnSharedPreferenceChangeListener not working - android

Here's my use case.
The PreferenceActivity provides a selectable list of themes. When a user selects a particular theme from the list, I want the effect to take place immediately.
As of now, the change of theme affects all new activities that are launched but it does not affect the currently visible PreferenceActivity and activities on back stack.
To overcome this, I decided to implement a Sharedpreference change listener which on change would clear all old activities from the backstack and restart the same class with the modified theme.
Here's what I tried.
SharedPreferences.OnSharedPreferenceChangeListener spChanged = new
SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
Intent newIntent = new Intent(Settings.this,Settings.class);
newIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent);
}
};
I tried simple log message like Log.e("change", "pref changed");
But it seems, this listener is simply not listening for changes.

Register it with SharedPreferences.registerOnSharedPreferenceChangeListener.
Also, keep in mind that the listener is kept in a WeakHashMap. This means that you cannot use an anonymous inner class as a listener, as it will become the target of garbage collection as soon as you leave the current scope. So make your listener an instance variable of your activity or make the activity itself implement the listener.

Related

Passing data from MainActivity to sub-Activity when sub-Activity is opened through an Adapter?

So my title may be hard to follow, but I'll try to clarify and expand on what my issue is below.
I currently have an app that starts its life as a MainActivity with multiple Fragments sitting in a ViewPager.
In the MainActivity, I have Android In-App Billing V3 (library) setup so that the user can pay to remove ads. This works just fine in the MainActivity but my issue arises when moving to another Activity.
The first Fragment the user is presented with upon launching the app, contains a RecyclerView with an ArrayList of items. To get to a sub-Activity from the MainActivity, the user presses a button on one of the items in the RecyclerView, which means that the Intent data used to change Activities is contained within the RecyclerViewAdapter.
My issue is that once my app knows that the user has paid to remove ads, I want the app to also remove ads in all sub-Activities as well.
I don't know how to pass this info (that the "Remove Ads" in-app has been purchased) from Activity -> sub-Activity, when sub-Activity is launched through the RVAdapter instead.
So my question is: How would I pass data from MainActivity -> RVAdapter -> Sub-Activity?
Or is there an even better, more efficient way of passing this data along without using Intents? Do let me know!
Did my description of the issue make sense? I hope so! Otherwise let me know how I might clarify it! If you need me to paste in any code, let me know as well.
Thanks for any of your help!
you can use EventBus (greenrobot) nice library for send event this linke
to send evnts
after add library put below method to your main activity:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event)
don't forget about Register and unregister subscriber, do it like this :
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
finally post your event from everywhere like your subactivity :
EventBus.getDefault().postSticky(new MessageEvent());
Notice:I add postSticky(); to cache data on memory ,Then the sticky event can be delivered to subscribers or queried explicitly.
better solution
but i think you can save value in Sharedpreferences after purcahse:
SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME,
MODE_PRIVATE).edit();
editor.putBoolean("pay", true);
editor.apply();
then check this valu every Activity on onCreat method
to show adds or don't
SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
pay = prefs.getBoolean("pay", false);
if (pay) {
show();
}else dontShow();

Android - Language change when clicking back

I have this settings section where I allow users to change the languages displayed within the app. When the user chooses a different language, the activity is reloaded so that the change of language can be applied. But the problem is, when the user clicks back right after changing the language, the language shown in the background activity is still the same.
So my question is, what should I do to apply the change of language when I get back to some activity on the background? I suppose I should do something to detect the change in onResume method, but I'm not sure what it is. If you have any suggestions, please let me know.
Thank you.
After several attempts, I have found the solution to my problem. On my onCreate method, I get the SharedPreferences that contains the value of current language, and get the current language:
SharedPrefrences languagepref = getSharedPreferences("language",MODE_PRIVATE);
String language = languagepref.getString("languageToLoad", Locale.getDefault().getDisplayLanguage());
Then, in my onResume method, I assign the value of the above mentioned variable language to a local variable, and update the value of language. Then I compare these two variables - if they are different, I will destroy the current activity and start another:
#Override
public void onResume(){
super.onResume();
String oldLanguage = language;
language = languagepref.getString("languageToLoad", Locale.getDefault().getDisplayLanguage());
if (!oldLanguage.equals(language)){
finish();
startActivity(getIntent());
}
}
And voila, that did the trick!
I would suggest using SharedPreferences. You can store a lang key with the associated value in there and update it when necessary. In your onResume() methods you can get the lang value and then populate views according the value stored.
SharedPreferences sharedPreferences;
sharedPreferences = this.getSharedPreferences("MyActivity", Activity.MODE_PRIVATE);
String lang = sharedPreferences.getString("lang", "en-GB");
SharedPreferences.Editor editor;
editor = sharedPreferences.edit();
editor.putString("lang", "en-US").commit();
That's the basics you need to get going.
Have you tried restarting the Activity after the change is done ?
You can Simply use
finish();
startActivity(getIntent());
to refresh an activity whenever it detects a preference change.
The built in back pressed function does not refresh the code,so do this after u you change the language.
#Override
public void onBackPressed()
{
//new MainActivity();
Intent intent=new Intent(this,MainActivity.class);
startActivity(intent);
}
and in Main Activity class do this
public MainActivity() {
//1- This code is responsible for updating the change of all Strings from a language to another
//it will be called every time this activity is instantiated (da,since it is a constructor) , or when this activity gets
// called by an intent.
//2- Every String inside this Activity(ever fragment inside it ) will also be under the effect of change of language
LocalUtils.updateConfig(this);
}
Please review the following answer and add this to it. To get a full answer written by Roberto.B and LarsH:
Changing Locale within the app itself

Implementing user choice of theme

I want to give the user the choice between a few different themes, and was wondering if this is an alright way of doing things. I did a little test with this method and it worked, but I think there may be better ways and think it may cause some problems later on, so wanted to ask.
I was thinking of creating a different layout for each theme, and in onCreate just having a switch for the setContentView() method. I'd load a saved SharedPreference value (integer) first and depending on what that value was display the corresponding layout. Obviously the user could change the SharedPreference value with a button or something.
As these layouts would be basically the same but with different colours, I'd want to use the same IDs for my TextViews and other Views in each layout file. My main question is would this cause problems?
Sorry for the wall of text with no code. I'd just like to get a general idea of good practice for this situation. Thanks in advance.
I actually have this feature in my application and additionally, I allow users to change theme at runtime. As reading a value from preferences takes some time, I'm getting a theme id via globally accessible function which holds cached value.
As already pointed out - create some Android themes, using this guide. You will have at least two <style> items in your styles.xml file. For example:
<style name="Theme.App.Light" parent="#style/Theme.Light">...</style>
<style name="Theme.App.Dark" parent="#style/Theme">...</style>
Now, you have to apply one of these styles to your activities. I'm doing this in activitie's onCreate method, before any other call:
setTheme(MyApplication.getThemeId());
getThemeId is a method which returns cached theme ID:
public static int getThemeId()
{
return themeId;
}
This field is being updated by another method:
public static void reloadTheme()
{
themeSetting = PreferenceManager.getDefaultSharedPreferences(context).getString("defaultTheme", "0");
if(themeSetting.equals("0"))
themeId = R.style.Theme_Light;
else
themeId = R.style.Theme_Dark;
}
Which is being called whenever preferences are changed (and, on startup of course). These two methods reside in MyApplication class, which extends Application. The preference change listener is described at the end of this post and resides in main activity class.
The last and pretty important thing - theme is applied, when an activity starts. Assuming, you can change a theme only in preference screen and that there's only one way of getting there, i.e. from only one (main) activity, this activity won't be restarted when you will exit preference screen - the old theme still will be used. Here's the fix for that (restarts your main activity):
#Override
protected void onResume() {
super.onResume();
if(schduledRestart)
{
schduledRestart = false;
Intent i = getBaseContext().getPackageManager().getLaunchIntentForPackage( getBaseContext().getPackageName() );
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
}
}
scheduledRestart is a boolean variable, initially set to false. It's set to true when theme is changed by this listener, which also updates cached theme ID mentioned before:
private class ThemeListener implements OnSharedPreferenceChangeListener{
#Override
public void onSharedPreferenceChanged(SharedPreferences spref, String key) {
if(key.equals("defaultTheme") && !spref.getString(key, "0").equals(MyApplication.getThemeSetting()))
{
MyApplication.reloadTheme();
schduledRestart = true;
}
}
sp = PreferenceManager.getDefaultSharedPreferences(this);
listener = new ThemeListener();
sp.registerOnSharedPreferenceChangeListener(listener);
Remember to hold a reference to the listener object, otherwise it will be garbage colleted (and will cease to work).
If you are using Material Components themes and followed Light and Dark theme guidelines then you can do it from AppCompatDelegate. These themes can be changed/applied at run time without restarting your application.
private fun handleThemeChange(theme: String) {
when (newTheme) {
getString(R.string.light) -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
getString(R.string.dark) -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
getString(R.string.system) -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
}
You can also change dynamically theme using:
ContextThemeWrapper w = new ContextThemeWrapper(this, <newTHEMEId>);
getTheme().setTo(w.getTheme());
Before onCreate for each activity.
It does work if you do it this way, and I don't think it would cause any problem, but it seems like a lot of hassle (you have to multiply all your layouts by all the themes you want to add. If later you want to modify a resource in a layout, you'll have to modify it in all the themes. You're definitely bound to forget one)
Why not using the Styles and Themes feature of Android?
They can be applied to the whole activity easily:
<activity android:theme="#style/my_theme">
So that when you detect a change in the SharedPreferences value you use (button on a preference activity, or something) you can just switch the style. Or better, you can set the style to read your preference value at runtime (when creating the activity) and apply the correct style/theme accordingly.

Android - Determine if a Preference has changed, after user accesses PreferenceActivity

I'm making a fairly basic game. In my preferences, there's an option to change the difficulty setting. I'd like to be able to somehow, in the main activity that called it, sense if they've changed the difficulty. (And then restart the game)
I'm having difficulty because of how the preference activity is handled asynchronously. If I add logic to check the value before and after sending the intent to my PreferenceActivity (from a menu selection), it really doesn't work...
Can someone point me in a proper direction as to either how to serialize a chunk of code normally handled asynchronously, or have an idea of how to sense preferences changed? Is there a listener class hidden somewhere?
For posterity's sake, here's the code handling the intent and how I'm failing. (snippet, from inside menu onOptionsItemSelected, inside a switch)
case R.id.menuOptions:
String currentDifficulty = preferences.getString("difficulty","problem!");
Intent i = new Intent(this, prefs.class);
startActivity(i);
if (currentDifficulty.equals(preferences.getString("difficulty","problem!")))
return true;
else doNewGame();
return true;
Android allows you to register a preference changed listener. Here's a quick example:
public class myClass implements OnSharedPreferenceChangeListener
{
private SharedPreferences settings;
settings = PreferenceManager.getDefaultSharedPreferences(this);
settings.registerOnSharedPreferenceChangeListener(this);
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
// Process it here
}
}
OnPreferenceChangeListener You can use it to very easily listen for preference changes.
The OnPreferenceChangeListener is a good idea, but you could also start your preference activity using startActivityForResult(). Your activity is then notified in onActivityResult() after the preference activity returns (you should override it to handle preference changes).

ListPreference with a max number of selectable options

I want to have an element in my preference menu that does the following:
Show a list of options.
Many are selectable
Maximum amount of options to be chosen 2.
Possibilities I thought of:
Doing a separated PreferenceScreen and showing options as checkBoxes, but I don't know where to place the logic of max 2 options.
Extending DialogPreference and doing it by hand.
What's the best way?
Extending DialogPreference would get you the closest in terms of look-and-feel; the Preference classes are fairly unflexible and un-extendable in my experience.
I can't remember too much about PreferenceScreen, but I imagine it's similar.
In an app I worked on, we ended up using separate activities, launched via Intent from a Preference item onClick. This allowed us to easily develop preference screens that require validation logic a bit more complex than the usual.
You can put the logic of maximum two options in a OnSharedPreferenceChangeListener.
So you just listen to all the preferences as they change and update them if an invalid combination is selected.
So your code would be something like the following:
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,String key) {
//Code to calcuate how many are selected
int code = numberSelected();
if (count > 2) {
sharedPreferences.edit().putBoolean(key,false).commit();
Toast.makeText(this,"Can't select more than two!",Toast.LENGTH_LONG).show();
}
}
If you create your own PreferenceActivity that implements OnSharedPreferenceChangeListener you can enable the listener to be listening only when required doing something like this:
#Override
protected void onResume() {
super.onResume();
//Register the listener
getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
}
#Override
protected void onPause() {
super.onPause();
// Unregister the listener
getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
}

Categories

Resources