Android: how do I call findPreference() from my main activity? - android

I'm handling the preferences screen for my Android app. I want to disable (grey it out) an item if the preceding one has a specific value.
I have implemented two classes: MainActivity and PreferencesActivity.
In MainActivity I do:
public class MainActivity extends Activity implements OnSharedPreferenceChangeListener {
...
public void onCreate (Bundle savedInstanceState) {
...
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
sharedPrefs.registerOnSharedPreferenceChangeListener(this);
...
}
Then, when my preferences are requested by the user:
startActivity(new Intent(this, PreferencesActivity.class));
Then, to handle preferences in MainActivity, I do:
#Override
public void onSharedPreferenceChanged (SharedPreferences prefs, String key) {
if ("sport".equals(key)) {
sport = prefs.getString("sport", "");
ListPreference lp = findPreference("match_duration");
if (sport.equals(getString(R.string."sport_soccer"))) {
lp.setEnabled(false);
}
}
...
}
The problem is I can't call findPreference in MainActivity ("The method findPreference(String) is undefined for the type MainActivity"...).
Is my approach wrong? Shoud I implement onSharedPreferenceChanged() method in PreferencesActivity? If so, how do I use MainActivity properties in PreferencesActivity?

findPreference() should be called from a class implementing PreferenceActivity interface (PreferencesActivity in my context). MainActivity properties can be accessed via SharedPreferences class.

Related

Can't resolve findPreference() from MainActivity?

I'm getting a compiler error cannot resolve method findPreference when I try to intialize an OnSharedPreferencesChanged listener in my MainActivity. According to the answer here:
findPreference() should be called from a class implementing PreferenceActivity interface
but I don't understand what the code to do this would be. How can I get rid of the compiler error and successfully set listeners for preference changes?
MainActivity.java
public class MainActivity extends FragmentActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
private SharedPreferences.OnSharedPreferenceChangeListener listener;
#Override
protected void onCreate(Bundle savedInstanceState) {
...
//Test preference menu
listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (key.equals("pref_wood")) {
Preference woodPref = findPreference(key); //COMPILER ERROR HERE
MainActivity.getGLSurfaceView().setTexture("");
// Set summary to be the user-description for the selected value
woodPref.setSummary(sharedPreferences.getString(key, ""));
}
}
}
}
}
findPreference is a method which is part of both PreferenceFragment and PreferenceActivity - these are the Fragments/Activities that actually show your Preference screen (the activity is deprecated and you should be using the PreferenceFragment).
You're trying to use it in your MainActivity. This doesn't work because the Preference objects don't actually exist on this screen (they exist in another activity that usually have a PreferenceFragment as part of it). If you need to get access to a preference value of a preference in an activity that is not your preference screen, use SharedPreferences, something like:
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);
int defaultValue = getResources().getBoolean(R.bool.saved_high_score_default);
boolean wood = sharedPref.getBoolean(pref_wood, defaultValue);
You can check out the documentation for further examples.
If your MainActivity is supposed to be a screen that shows settings, then you should probably rename it and include a preference fragment inside of it.
I believe you're also going to run into trouble with setSummary because the Preference is not part of this activity, it's part of the activity where you actually modify the preferences. setSummary is used to update the actual UI of the Preference so that if you, for example, select one of three values when using a list preference, it shows the value you just selected on the screen.

Variable with Activity and Fragments

I have my activity with one boolean variable public.
I set the variable value to true in other class inside MainActivity, but when my application enters the onPause() function, the variable gets the value false, why?
public class MainActivity extends ActionBarActivity {
public boolean detectedState;
public boolean isDetectedState() {
return detectedState;
}
public void setDetectedState(boolean DetectedState) {
this.detectedState = DetectedState;
}
// i have one fragment in MainActivity...
public static class contentFragment extends Fragment{
// Get and set value of Variable
MainActivity activity = new MainActivity();
System.out.println(activity.isDetectedState());
//out is false
activity.setDetectedState(enable);
System.out.println(activity.isDetectedState());
//out is true
// if now i click in home Button for example, the application is with state onPause.. and my out println is false, why?
}
#Override
protected void onPause(){
super.onPause();
System.out.println(isDetectedState());
//here out is false...
}
}
You should understand that the value of detectedState is set to true for the instance of activity instantiated inside the Fragment class. Variable's value doesn't get affected if you change its value inside an inner class.
You should never create Activity like this
MainActivity activity = new MainActivity();
You can use interfaces to pass data between Activity and Fragment.
You can achieve it by making that variable static but it would be better if you store in the application class.check this

Android Preferences onBackButton

i'm saving the users login information to SharedPreferences, so he only has to configure his login data once.
This is my onBackPressed method in my Preferences.class (extends PreferenceActivity):
#Override
public void onBackPressed() {
//Login again
Intent intent = new Intent(Preferences.this, LoginActivity.class);
startActivity(intent);
}
What I need is a if-condition which checks, if the preferences changed or not.:
If the user opens the Preferences Activity (edit: from any View(!)), and does not change anything and clicks the backbutton -> just go back to last state.
If the preferences changed: call LoginActivity.
Couldnt find a solution yet and the LoginActivity gets called whenever i hit the backbutton.
Thanks in advance,
Marley
To determine if there was a change in SharedPreferences you have to assign a OnSharedPreferenceChangeListener to your SharedPreferences object like this:
prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(this);
in this case I'm doing it in my Application class that's implementing:
public class YambaAppObj extends Application implements OnSharedPreferenceChangeListener
Then you will have to override:
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
//this method will be called when preferences are changed.
//do here what you want to record the change
Log.d(TAG , "onSharedPreferenceChanged for:" + key);
}
and then you could check this record in your onBackPressed() method of your Preferences Activity and act accordingly.
You can use OnSharedPreferenceChangeListener or (depending on what you really meany by changed) you can try utilising onSharedPreferenceChanged() like:
protected Boolean mPrefsChanged = false;
#Override
public void onSharedPreferenceChanged( SharedPreferences sharedPreferences,
String key ) {
mPrefsChanged = true;
}
of course it's far from perfect, but at least you got more options

onSharedPreferenceChanged not fired if change occurs in separate activity?

I've implemented onSharedPreferenceChanged in my main activity.
If I change the preferences in the main activity, my event fires.
If I change the preferences through my preferences screen (PreferenceActivity) my event does NOT fire when preferences are changed (because it's a separate activity and separate reference to sharedPreferences?)
Does anybody have a recommendation of how I should go about overcoming this situation?
Thanks!
EDIT1: I tried adding the event handler right in my preference activity but it never fires. The following method gets called during onCreate of my preference activity. When I change values, it never prints the message (msg() is a wrapper for Log.d).
private void registerChangeListener () {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
sp.registerOnSharedPreferenceChangeListener(new OnSharedPreferenceChangeListener () {
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
msg (" ***** Shared Preference Update ***** ");
Intent i = new Intent();
i.putExtra("KEY", key);
i.setAction("com.gtosoft.dash.settingschanged");
sendBroadcast(i);
// TODO: fire off the event
}
});
}
The OnSharedPreferenceChangeListener gets garbage collected in your case if you use an anonymous class.
To solve that problem use the following code in PreferenceActivity to register and unregister a change listener:
public class MyActivity extends PreferenceActivity implements
OnSharedPreferenceChangeListener {
#Override
protected void onResume() {
super.onResume();
// Set up a listener whenever a key changes
getPreferenceScreen().getSharedPreferences()
.registerOnSharedPreferenceChangeListener(this);
}
#Override
protected void onPause() {
super.onPause();
// Unregister the listener whenever a key changes
getPreferenceScreen().getSharedPreferences()
.unregisterOnSharedPreferenceChangeListener(this);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,String key)
{
// do stuff
}
Furthermore be aware that the listener only gets called if the actual value changes. Setting the same value again will not fire the listener.
see also SharedPreferences.onSharedPreferenceChangeListener not being called consistently
This happen because garbage collector. its works only one time. then the reference is collected as garbage. so create instance field for listener.
private OnSharedPreferenceChangeListener listner;
listner = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
//implementation goes here
}
};
prefs.registerOnSharedPreferenceChangeListener(listner);
I arrived here, like many others, because my listener won't be fired when I changed my boolean from true to false, or viceversa.
After much reading, and refactoring, switching contexts/inner classes/privates/static/ and the like, I realized my (stupid) error:
The onSharedPreferenceChanged is only called if something changes. Only. Ever.
During my tests, I was so dumb to click on the same button all the time, thus assigning the same boolean value to the preference all the time, so it did not ever change.
Hope this helps somebody!!
One other way of avoiding the problem is to make your activity the listener class. Since there is only one override method with a distinctive name you can do this:
public class MainActivity extends AppCompatActivity implements SharedPreferences.OnSharedPreferenceChangeListener
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sharedPreferences.registerOnSharedPreferenceChangeListener(this);
...
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key)
{
...
}
}
Note the original question spoke of a MainActivity listening to setting changes in a PreferenceActivity. The asker then added an "EDIT1" and changed the question to listening in the PreferenceActivity itself. That is easier than the former and seems to be what all the answers assume. But what if you still want the former scenario?
Well, it will work too, but do not use OnResume() and OnPause() to register and unregister the listener. Doing so will cause the listener to be ineffectual because the user leaves the MainActivity when they use the PreferenceActivity (which makes sense when you think about it). So it will work, but then your MainActivity will still be listening in the background even when the user is not using it. Kind of a waste of resources isn't it? So there is another solution that seems to work, simply add a method to OnResume() to re-read all preferences. That way when a user finishes editing preferences in a PreferenceActivity, the MainActivity will pick them up when the user returns to it and you don't need a listener at all.
Someone please let me know if they see a problem with this approach.
Why don't you just add a onSharedPreferenceChanged in the rest of the activities where the preferences could change?
The garbage collector erases that... you should consider using an Application context instead...or just add the code when app launchs... and then add the the listener with application context...
Consider keeping PreferencesChangeListener inside Android App class instance. Although it's NOT a clean solution storing reference inside App should stop GC from garbage collecting your listener and you should still be able to receive DB change updates. Remember that preference manager does not store a strong reference to the listener! (WeakHashMap)
/**
* Main application class
*/
class MyApp : Application(), KoinComponent {
var preferenceManager: SharedPreferences? = null
var prefChangeListener: MySharedPrefChangeListener? = null
override fun onCreate() {
super.onCreate()
preferenceManager = PreferenceManager.getDefaultSharedPreferences(this)
prefChangeListener = MySharedPrefChangeListener()
preferenceManager?.registerOnSharedPreferenceChangeListener(prefChangeListener)
}
}
and
class MySharedPrefChangeListener : SharedPreferences.OnSharedPreferenceChangeListener {
/**
* Called when a shared preference is changed, added, or removed.
*/
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (sharedPreferences == null)
return
if (sharedPreferences.contains(key)) {
// action to perform
}
}
}
While reading Word readable data shared by first app,we should
Replace
getSharedPreferences("PREF_NAME", Context.MODE_PRIVATE);
with
getSharedPreferences("PREF_NAME", Context.MODE_MULTI_PROCESS);
in second app to get updated value in second app.

accessing a class from another activity

Have following setup:
MainActivity class - extends activity
MyLayout class - extends View
Prefs class - extends PreferenceActivity and implements OnSharedPreferenceChangeListener
MainActivity creates a MyLayout class and sets it as its contentview. Once the user presses on the menu, Prefs class starts where the user can change some settings.
What I want is that, once the user changes a setting, the overloaded OnsharedPreferenceChanged method in the Prefs class will be called and from there I would like to invoke public methods on the MyLayout class that was created in the MainActivity.
How can I do this?
Don't overload onSharedPreferenceChanged method in preferenceactivity. Get an instance of the shared preference in your MainActivity, and then register an onsharedpreferencechangedlistener on that inside of your mainactivity
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
pref.registerOnSharedPreferenceChangeListener(prefListener);
And then you can create a new preference listener
OnSharedPreferenceChangeListener prefListener = new OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
// Do stuff
}
};
You should also unregister the listener in onPause() unless you need it to persist, otherwise unregister it on onStop()
Try making MainActivity implement OnSharedPreferenceChangeListener and register it on the onCreate() method as Falmarri said.

Categories

Resources