Android: Is there a way to show DialogPreference from code? - android

I want to open some preferences (which are made by extending DialogPreference) on first app startup. Also, these preferences are used as usual preferences.
Is there a way of accomplishing this?
EDIT:
I have my custom preference, made like this:
public class CitySelectPreference extends DialogPreference {
// Some code here
}
And as the solution I want it to be shown from the code, without the need of user getting to preference screen.

Just do this :
CitySelectPreference preference = (CitySelectPreference) findPreference("city_pref_key")
//You have to set a key to yout PreferenceScreen
PreferenceScreen screen = (PreferenceScreen) findPreference("screen_pref_key");
//Retrieve the index of the preference in preferenceScreen
int index = preference.getOrder();
//Perform a click
screen.onItemClick(null, null, index, 0);

Related

DialogPreference is not saving the preference when I expect it to?

I have written a bare bones standard DialogPreference which is working fine, except that it is not saving the preference to default shared preferences when I expected it to.
1) open the app, and main activity shows value of foo from default shared preferences = 1
2) go to settings
3) click on foo setting which opens my DialogPreference and shows value = 1
4) enter value 3
5) close my DialogPreference using Ok button
***** default shared preferences foo should now be 3
6) click on foo setting which opens my DialogPreference and shows value = 1
***** so my DialogPreference didn't save the preference to default shared preferences?
7) cancel dialog
8) go back to main activity which shows value of foo from default shared preferences = 3
***** so my DialogPreference did save the preference to default shared preferences
9) go to settings
10) click on foo setting which opens my DialogPreference and shows value of 3
Why isn't the value of default shared preferences foo = 3 at step (6)?
It seems that the preference is only being saved to default shared preferences when the flow returns to the main activity from the settings list, which is counter intuitive to saving the preference in the onDialogClosed method of DialogPreference.
MyDialogPreference
public class MyDialogPreference extends DialogPreference
{
private static final String DEFAULT_VALUE = "0";
private String value = DEFAULT_VALUE;
private EditText editText;
public MyDialogPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
setDialogLayoutResource(R.layout.constrained_integer_preference);
}
#Override
public void onBindDialogView(View view)
{
super.onBindDialogView(view);
editText = (EditText) view.findViewById(R.id.edit);
editText.setText("" + value);
}
#Override
protected void onDialogClosed(boolean positiveResult)
{
if (positiveResult)
{
persistString(editText.getText().toString());
}
super.onDialogClosed(positiveResult);
}
#Override
protected Object onGetDefaultValue(TypedArray typedArray, int index)
{
return typedArray.getString(index);
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue)
{
if (restorePersistedValue)
{
value = getPersistedString(DEFAULT_VALUE);
}
else
{
value = (String) defaultValue;
if (shouldPersist())
{
persistString(value);
}
}
}
}
EDIT: So it appears that the preference I am handling with my DialogPreference has no key, which is causing all the problems. But I have specified the key in the preferences.xml file for this DialogPreference. I have tried everything to force the key to be recognised but nothing is working.
Can anyone tell me how I get a DialogPreference to receive the android:key from the preferences.xml file to work?
preferences.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
<org.mycompany.myproject.MyDialogPreference
android:defaultValue="11"
android:dialogLayout="#layout/my_preference"
android:inputType="number"
android:key="MY_KEY"
android:selectAllOnFocus="true"
android:singleLine="true"
android:summary="summary"
android:title="My Preference" />
</PreferenceScreen>
You'd have to implement the OnPreferenceChangeListener and/or call to notifyChanged().
Unless you'd provide the code of that DialogPreference, it's difficult to reproduce the issue.
At some point I always feel like I'm hacking Android, and this is definitely a hack.
Initially I thought the problem I was fighting was that the framework ignores my android:key, because getKey() returns an empty string, but that can't be true because it gets the persistent value when it starts the PreferenceScreen, and saves my changed values to shared preferences when I close the DialogPreference.
So it seems the problem I am fighting is that the framework reads the preferences persistent values in to internal members, and then uses the internal members until the flow returns out of the preferences framework, without refreshing them after a DialogPreference has closed.
But I have finally found a way of getting the PreferenceScreen to refresh the preferences persistent values it holds in it's internal members. Although it's not really a refresh, it's a hack.
So what I do is basically throw away the PreferenceScreen and create a new one. I do this by adding the following code to my SettingsFragment.onCreate method directly before addPreferencesFromResource(R.xml.preferences).
SharedPreferences.OnSharedPreferenceChangeListener prefListener = (prefs, key) ->
{
setPreferenceScreen(null);
addPreferencesFromResource(R.xml.preferences);
};
PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext()).registerOnSharedPreferenceChangeListener(prefListener);
This is probably bad. I have tested it, repeatedly, though not thouroghly, and have not witnessed any adverse effects so far.
So with this hack, I can now repeatedly open a DialogPreference from the PreferenceScreen, save a new value, then go back in to the DialogPreference with the previously updated value.
I don't believe my hack is the intended way of achieving this outcome, but after days of searching the source code and google for answers, I have not found anything else.
I am leaving this answer here for anyone else that faces the same problem and is brave enough to try my hack. I'll update the answer if (and probably when) I find any problems with the hack.
Better yet, if anyone else can provide a preferred solution, pointing out what I have done wrong that caused the problem, please do.
EDIT: After working for so long, that hack eventually broke, and consistently.
But while removing the hack, I approached the problem with a fresh mind, and using the fact that I determined the dialog is getting the preference key, I used this fix for the problem, which is working perfectly.
I added this line of code to the start of onBindDialogView
value = getSharedPreferences().getString(getKey(), "-1");
Which makes the calls to onGetDefaultValue and onSetInitialValue redundant, but they just don't work as intended, at least not for me.
EDIT:
omg, I hate this!
I did not notice that during an earlier refactor the line of code that updates the DialogPreference internal value in onDialogClosed was removed.
It's usually something simple, and with everything else I was checking, I missed that small change.
I only just noticed it during a code review on the repo, and now I feel silly. So no additional code was required in the end.

Dynamically create CheckBoxPreferences

I am currently building out a list of rows with checkboxes dynamically using content from a web service. However, this ListView will need to do pretty much what a PreferenceActivity would accomplish.
I don't know the number of rows as the content is dynamic so I can't create each CheckBoxPreference in XML. How do I go about building a PreferenceActivity that will display an unknown number rows with a CheckBoxPreference dynamically?
I think you're looking for something like this:
public class MyPreferenceActivity extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.my_preference_activity);
//fetch the item where you wish to insert the CheckBoxPreference, in this case a PreferenceCategory with key "targetCategory"
PreferenceCategory targetCategory = (PreferenceCategory)findPreference("targetCategory");
//create one check box for each setting you need
CheckBoxPreference checkBoxPreference = new CheckBoxPreference(this);
//make sure each key is unique
checkBoxPreference.setKey("keyName");
checkBoxPreference.setChecked(true);
targetCategory.addPreference(checkBoxPreference);
}
}
Well #Jodes, actually both of you are right, but the correct way of doing this would be using a ListPreference.
I would use a entire programmatic approach, from my experience it's easier to be consistent; either create an entire XML layout via code, or via XML, but mixing the 2 can be weird and you cannot alter everything set via XML...
onCreate(){
this.setPreferenceScreen(createPreferenceHierarchy());
}
public PreferenceScreen createPreferenceHierarchy(){
PreferenceScreen root = getPreferenceManager().createPreferenceScreen(this);
// category 1 created programmatically
PreferenceCategory cat1 = new PreferenceCategory(this);
cat1.setTitle("title");
root.addPreference(cat1);
ListPreference list1 = new ListPreference(this);
list1.setTitle(getResources().getString(R.string.some_string_title));
list1.setSummary(getResources().getString(R.string.some_string_text));
list1.setDialogTitle(getResources().getString(R.string.some_string_pick_title));
list1.setKey("your_key");
CharSequence[] entries = calendars.getCalenders(); //or anything else that returns the right data
list1.setEntries(entries);
int length = entries.length;
CharSequence[] values = new CharSequence[length];
for (int i=0; i<length; i++){
CharSequence val = ""+i+1+"";
values[i] = val;
}
list1.setEntryValues(values);
cat1.addPreference(list1);
return root;
}//end method
However, using this approach you will run into the platform's limitations of not having a multiple select ListPreference, and you'll probably want to implement something else.
I found this solution, which works great. You'll have to read the comments to find clues about how to debug the code though...
You need a ListView for that, a PreferenceActivity. As discussed in this link, PreferenceActivity should only be used for actually saving preferences.
Instead you could either create a simple dialog with single or multiple choice options:
http://developer.android.com/guide/topics/ui/dialogs.html
Or use a ListView as in the API examples Google provides, they give a simple example:
http://hi-android.info/docs/resources/samples/ApiDemos/src/com/example/android/apis/view/List10.html
Use PreferenceFragmentCompat from Preference Compat Library
compile 'com.android.support:preference-v7:23.4.0'
Check this article for the implementation details https://medium.com/#arasthel92/dynamically-creating-preferences-on-android-ecc56e4f0789#.71ssvjses

Programmatically populating preferences with checkboxes

In my setting page I have a preference which fetches a list of toggle-able settings that I wanted to display as individual checkbox preferences.
I know that preferences.xml supports generating lists of preferences (looking at wi-fi settings page) but ListPreference only allows you to select one from the list.
I've been searching for how to generate preferences programmatically but have only managed to find how to change attributes of preferences that are already in the XML.
Here is a short example (assuming you are extending PreferenceActivity):
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
PreferenceScreen screen = getPreferenceManager().createPreferenceScreen(this);
PreferenceCategory category = new PreferenceCategory(this);
category.setTitle("category name");
screen.addPreference(category);
CheckBoxPreference checkBoxPref = new CheckBoxPreference(this);
checkBoxPref.setTitle("title");
checkBoxPref.setSummary("summary");
checkBoxPref.setChecked(true);
category.addPreference(checkBoxPref);
setPreferenceScreen(screen);
}
Programatically add a preference, with other preferences in xml file:
Other solutions didn't work for me because I ALSO had an xml with preferences. I'm not sure all these calls are necessary/redundant, but this works.
onCreate() method, class extends PreferenceActivity:
setContentView(R.layout.preferences);
addPreferencesFromResource(R.xml.preferences);
PreferenceScreen pScreen = getPreferenceManager().createPreferenceScreen(this);
CheckBoxPreference cb = new CheckBoxPreference(this);
cb.setKey("cb");
cb.setTitle("BLAH");
cb.setOrder(99); //not working...
pScreen.addPreference(cb);
setPreferenceScreen(pScreen);
addPreferencesFromResource(R.xml.preferences);
Sidenote: Since I needed to generate a dynamic checkbox list, it was best suited inside an inner PreferenceScreen. So I created this PreferenceScreen inside the xml, then dynamically generated the checkboxes inside this. This way the ordering didn't matter since all the 'new' dynamica checkboxes were inside this screen.
Try the below code.
CheckBoxPreference checkBoxPref = findPreference("your_key");
if (checkBoxPref != null)
{
checkBoxPref.setChecked(false);
}

How to set scroll position for long PreferenceScreen

Android app has some very long preference screens, which always open at the top of the preference menu. I have some idea where the user wants to be in the preference menu. How can I force the preference screen to open scrolled to a specific preference item?
I know this is an old one, so this answer is just for reference.
To auto-select a given screen, all you have to do is setPreferenceScreen() (this is for a pre-Honeycomb non-Fragment PreferenceActivity).
Once you're on the correct PreferenceScreen, you can indeed use getListView().smoothScrollToPosition(position) (but this is a Froyo+ method), or you can use getListView.setSelection(position).
But how to get the position?
First, watch out for the trap: PreferenceActivity.getListAdapter() does not return the actual ListAdapter, but a local instance variable which is disconcertingly not in sync with PreferenceActivity.getListView().getAdapter() (and usually null).
Second, trying to use Preference.getOrder() returns the order of the Preference object within its parent, which is what you want to use for the position only if you're not using PreferenceCategories since what you need is its order within the PreferenceScreen.
If you are using PreferenceCategories, you need to iterate over the items in the adapter (for (int i = 0; i < adapter.getCount(); i++)until you find the right one, and use its position.
Another corner of the Android SDK that is in dire need of some attention…
You can just use scrollToPreference :
https://developer.android.com/reference/androidx/preference/PreferenceFragmentCompat#scrollToPreference(androidx.preference.Preference)
Example:
scrollToPreference(preferenceKey)
or:
scrollToPreference(preference)
Add this function to your PreferenceFragment
public void scrollToItem(String preferenceName) {
ListView listView = ButterKnife.findById(getView(),android.R.id.list);
Preference preference = findPreference(preferenceName);
if (preference != null && listView != null) {
for (int i = 0; i < listView.getAdapter().getCount(); i++) {
Preference iPref = (Preference) listView.getAdapter().getItem(i);
if (iPref == preference) {
listView.setSelection(i);
break;
}
}
}
}
Lets say you have settings.xml with this
<Preference
android:icon="#drawable/ic_action_email"
android:key="emailSupport"
android:title="#string/email_support" />
You can call
scrollToItem("emailSupport");
Note: You may need to replace listView.setSelection(i) with listView.smoothScrollToPosition(i)
Since PreferenceActivity extends ListActivity, you can call getListView() to get the ListView containing your preferences, and then use listView.smoothScrollToPosition() to scroll to a specific row in the list. I haven't actually tried this before, but it should work.

Separate preferences for each view in an Android app

I have multiple views that come and go as the application runs. I want each view to have its own personal preferences that are stored as the ID tag of the view. Above these is the "General Preferences" that the sub prefs reference to get their default values when a view it is created.
Right now I have it set up that the General Preferences are the default SharedPreferences. But I have no Idea how to create the new preferences and set up an activity UI so the user can change them. Is it pretty much the same as setting up the SharedPreferences?
this may not be exactly what you're asking for, but here's what I do:
in my main activity, when I call the preferences activity, I pass it the name of the custom preference file as extra data in the intent:
static final String EXTRA_PREFERENCES_NAME = "android.intent.extra.PREFERENCES_NAME";
...
Intent intent = new Intent(this, Preferences.class);
intent.putExtra(EXTRA_PREFERENCES_NAME, preferencesName);
startActivity(intent);
then, in my preferences activity, I get the custom preferences name and set it like this:
public class Preferences extends PreferenceActivity {
private String preferencesName = "";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get the custom preferences name from the extra data in the intent
preferencesName = getIntent().getExtras().getString(MainActivity.EXTRA_PREFERENCES_NAME);
// set the preferences file name
getPreferenceManager().setSharedPreferencesName(preferencesName);
// get the default preferences from XML
addPreferencesFromResource(R.xml.preferences);
}
lastly, in my main activity, I get specific preferences like this:
SharedPreferences preferences = getSharedPreferences(preferencesName, MODE_PRIVATE);
String somePreference = preferences.getString("somePreference", defaultValue);
Somehow I am not worthy to comment but to write an answer, so here we go:
I'd really like to know how to use sharedPreferences with PreferencesActivity instead of DefaultSharedPreferences.
One way I can think of to accomplish this is letting the preferenceActivity save the values to defaultSharedPreferences and then read these values out and save them into a sharedPreferences associated with a name that would match the kind of values saved.
But this seems very wrong. So how do you guys do this? Or do you save all your values from any PreferencesActivties into defaultSharedPreferences?
You can use PreferenceManager to achieve the objective.

Categories

Resources