Appcompatactivity with custom native (not compatibility) dialogpreference containing a TimePicker - android

I am building a preferences / settings screen inside an Android AppCompatActivity. One requirement is a custom [DialogPreference][1] with a TimePicker.
The DialogPreference must be 'native', meaning not the compatibility version like described here and here.
The code for the AppCompatActivity:
...
public class SettingsActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preferences);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar_settings);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
}
The layout of activity_preferences.xml:
...
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<fragment
android:name="nl.waywayway.broodkruimels.SettingsFragment"
android:id="#+id/settings_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.NestedScrollView>
The SettingsFragment class:
...
public class SettingsFragment extends PreferenceFragment
{
Context mContext;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
}
}
The preferences.xml file:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SwitchPreference
android:key="pref_notify"
android:title="#string/pref_notify"
android:summary="#string/pref_notify_summ"
android:defaultValue="false" />
<nl.waywayway.broodkruimels.TimePreference
android:dependency="pref_notify"
android:key="pref_notify_time"
android:title="#string/pref_notify_time"
android:summary="#string/pref_notify_time_summ"
android:defaultValue="390" />
</PreferenceScreen>
And the custom TimePreference class:
public class TimePreference extends DialogPreference
{
private TimePicker mTimePicker = null;
private int mTime;
private int mDialogLayoutResId = R.layout.preferences_timepicker_dialog;
// 4 constructors for the API levels,
// calling each other
public TimePreference(Context context)
{
this(context, null);
}
public TimePreference(Context context, AttributeSet attrs)
{
this(context, attrs, R.attr.preferenceStyle);
}
public TimePreference(Context context, AttributeSet attrs, int defStyleAttr)
{
this(context, attrs, defStyleAttr, defStyleAttr);
}
public TimePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
super(context, attrs, defStyleAttr, defStyleRes);
}
public int getTime()
{
return mTime;
}
public void setTime(int time)
{
mTime = time;
// Save to Shared Preferences
persistInt(time);
}
#Override
public int getDialogLayoutResource()
{
return mDialogLayoutResId;
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index)
{
// Default value from attribute. Fallback value is set to 0.
return a.getInt(index, 0);
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue)
{
// Read the value. Use the default value if it is not possible.
setTime(restorePersistedValue ?
getPersistedInt(mTime) : (int) defaultValue);
}
#Override
protected void onBindDialogView(View view)
{
super.onBindDialogView(view);
mTimePicker = (TimePicker) view.findViewById(R.id.preferences_timepicker);
if (mTimePicker == null)
{
throw new IllegalStateException("Dialog view must contain a TimePicker with id 'preferences_timepicker'");
}
// Get the time from the related Preference
Integer minutesAfterMidnight = null;
TimePreference preference = (TimePreference) findPreferenceInHierarchy("pref_notify_time");
minutesAfterMidnight = preference.getTime();
// Set the time to the TimePicker
if (minutesAfterMidnight != null)
{
int hours = minutesAfterMidnight / 60;
int minutes = minutesAfterMidnight % 60;
boolean is24hour = DateFormat.is24HourFormat(getContext());
mTimePicker.setIs24HourView(is24hour);
if (Build.VERSION.SDK_INT >= 23)
{
mTimePicker.setHour(hours);
mTimePicker.setMinute(minutes);
}
else
{
mTimePicker.setCurrentHour(hours);
mTimePicker.setCurrentMinute(minutes);
}
}
}
#Override
protected void onDialogClosed(boolean positiveResult)
{
if (positiveResult)
{
// Get the current values from the TimePicker
int hours;
int minutes;
if (Build.VERSION.SDK_INT >= 23)
{
hours = mTimePicker.getHour();
minutes = mTimePicker.getMinute();
}
else
{
hours = mTimePicker.getCurrentHour();
minutes = mTimePicker.getCurrentMinute();
}
// Generate value to save
int minutesAfterMidnight = (hours * 60) + minutes;
// Save the value
TimePreference timePreference = (TimePreference) findPreferenceInHierarchy("pref_notify_time");
// This allows the client to ignore the user value.
if (timePreference.callChangeListener(minutesAfterMidnight))
{
// Save the value
timePreference.setTime(minutesAfterMidnight);
}
}
}
}
The preferences_timepicker_dialog.xml file is as follows:
...
<TimePicker
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/preferences_timepicker"
android:layout_width="match_parent"
android:layout_height="match_parent" />
The result is like the screenshot below. On a Moto G5 plus phone with Android 7.
Question:
There should be two preferences. However, the custom DialogPreference is not showing in the settings list.
What is going wrong here?
Does the AppCompatActivity actually work with the 'native' DialogPreference?
The TimePreference class is actually instantiated from the preferences xml, that could be logged from the constructor. Also no compile time errors, no runtime errors.

Finally I found a different approach that looks clean, tested and works on real devices from Android 4 until 7. The Preference is showing in the Preference screen.
Also, the TimePicker dialog is properly showing in landscape orientation. This is a problem on some devices. See
Android TimePicker not displayed well on landscape mode
TimePickerDialog widget in landscape mode (PreferenceScreen)
Steps are:
Include a general Preference item in the preferences xml file
Set a click listener on this Preference using onPreferenceTreeClick()
When this Preference is clicked, show a regular (not compatibility library) TimePickerDialog
like described in the 'Pickers' guide
(https://developer.android.com/reference/android/app/TimePickerDialog.html)
Save the time set on the TimePicker manually in the SharedPreferences
The preferences Activity:
...
public class SettingsActivity extends AppCompatActivity
{
public static final String KEY_PREF_NOTIFY = "pref_notify";
public static final String KEY_PREF_NOTIFY_TIME = "pref_notify_time";
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preferences);
}
}
The layout file activity_preferences.xml:
...
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<fragment
android:name="nl.waywayway.broodkruimels.SettingsFragment"
android:id="#+id/settings_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.NestedScrollView>
The SettingsFragment class:
...
public class SettingsFragment extends PreferenceFragmentCompat
{
Context mContext;
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey)
{
setPreferencesFromResource(R.xml.preferences, rootKey);
}
// The Context object of this fragment is only available when this fragment is 'attached', so set the Context object inside the onAttach() method
#Override
public void onAttach(Context context)
{
super.onAttach(context);
mContext = context;
}
// This method sets the action of clicking the Preference
#Override
public boolean onPreferenceTreeClick(Preference preference)
{
switch (preference.getKey())
{
case SettingsActivity.KEY_PREF_NOTIFY_TIME:
showTimePickerDialog(preference);
break;
}
return super.onPreferenceTreeClick(preference);
}
private void showTimePickerDialog(Preference preference)
{
DialogFragment newFragment = new TimePickerFragment();
newFragment.show(getFragmentManager(), "timePicker");
}
}
The preferences.xml file:
...
<android.support.v7.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.preference.SwitchPreferenceCompat
android:key="pref_notify"
android:title="#string/pref_notify"
android:summary="#string/pref_notify_summ"
android:defaultValue="false" />
<android.support.v7.preference.Preference
android:dependency="pref_notify"
android:key="pref_notify_time"
android:title="#string/pref_notify_time"
android:summary="#string/pref_notify_time_summ"
android:defaultValue="390" />
</android.support.v7.preference.PreferenceScreen>
The TimePickerFragment class, see the Android 'Pickers' guide (https://developer.android.com/guide/topics/ui/controls/pickers.html) for an explanation:
...
public class TimePickerFragment extends DialogFragment
implements TimePickerDialog.OnTimeSetListener
{
private Context mContext;
private int mTime; // The time in minutes after midnight
// The Context object for this fragment is only available when this fragment is 'attached', so set the Context object inside the onAttach() method
#Override
public void onAttach(Context context)
{
super.onAttach(context);
mContext = context;
}
// Getter and setter for the time
public int getTime()
{
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mContext);
int prefDefault = mContext.getResources().getInteger(R.integer.preferences_time_default);
mTime = sharedPref.getInt(SettingsActivity.KEY_PREF_NOTIFY_TIME, prefDefault);
return mTime;
}
public void setTime(int time)
{
mTime = time;
// Save to Shared Preferences
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mContext);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putInt(SettingsActivity.KEY_PREF_NOTIFY_TIME, time);
editor.commit();
}
public Dialog onCreateDialog(Bundle savedInstanceState)
{
int minutesAfterMidnight = getTime();
int hour = minutesAfterMidnight / 60;
int minute = minutesAfterMidnight % 60;
Log.i("HermLog", "onCreateDialog(), tijd: " + hour + ":" + minute);
// Create a new instance of TimePickerDialog and return it
return new TimePickerDialog(
mContext,
this,
hour,
minute,
DateFormat.is24HourFormat(mContext)
);
}
#Override
public void onTimeSet(TimePicker view, int hour, int minute)
{
int minutesAfterMidnight = (hour * 60) + minute;
setTime(minutesAfterMidnight);
}
}

Related

Default value of custom DialogPreference is null

I have a TimePreference class that derives from DialogPreference. I'm using 3 NumberPicker to set hours, minutes and seconds. This works fine. But when onSetInitialValue is called defaultValue is always null. Whereas onGetDefaultValue returns the correct value that is defined in the preferences. Any ideas what is wrong?
public TimePreference(Context ctxt, AttributeSet attrs, int defStyle) {
super(ctxt, attrs, defStyle);
setPositiveButtonText(R.string.ok);
setNegativeButtonText(R.string.cancel);
setDialogLayoutResource(R.layout.time_preference_layout);
}
#Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
mHoursPicker = (NumberPicker)v.findViewById(R.id.hours);
mHoursPicker.setMinValue(0);
mHoursPicker.setMaxValue(23);
mHoursPicker.setFormatter(TWO_DIGIT_FORMATTER);
mMinutesPicker = (NumberPicker)v.findViewById(R.id.minutes);
mMinutesPicker.setMinValue(0);
mMinutesPicker.setMaxValue(59);
mMinutesPicker.setFormatter(TWO_DIGIT_FORMATTER);
mSecondsPicker = (NumberPicker)v.findViewById(R.id.seconds);
mSecondsPicker.setMinValue(0);
mSecondsPicker.setMaxValue(59);
mSecondsPicker.setFormatter(TWO_DIGIT_FORMATTER);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
long currentTimeInMillis = convertTimeToMillis();
if (callChangeListener(currentTimeInMillis)) {
persistLong(currentTimeInMillis);
notifyChanged();
}
}
CharSequence summary = getSummary();
setSummary(summary);
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
super.onGetDefaultValue(a, index);
return a.getString(index);
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
super.onSetInitialValue(restoreValue, defaultValue);
long value = 0;
if (restoreValue) {
if (defaultValue == null) {
Log.d( "bla", "No default value defined!");
} else {
value = Long.parseLong(getPersistedString((String) defaultValue));
}
} else {
if (defaultValue == null) {
Log.d( "bla", "No default value defined!");
} else {
value = Long.parseLong((String) defaultValue);
}
}
String result = convertMillisToTime(value);
setSummary(result);
}
#Override
public CharSequence getSummary() {
if (mHoursPicker == null) {
return null;
}
return convertTimeToString( mHoursPicker.getValue(), mMinutesPicker.getValue(), mSecondsPicker.getValue());
}
}
Why don't you try setting the default value manually? If you are saving this time value to sharedPreferences, you can specify it in your xml declaration of a PreferenceScreen:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<PreferenceCategory
android:title="#string/pref_cat_general"
android:layout_width="match_parent"
android:layout_height="match_parent">
<to.marcus.rxtesting.ui.widgets.ChoicePreference
android:key="#string/pref_key_sel_time"
android:title="#string/pref_title_time"
android:summary="set time"/>
Notice the custom preference ChoicePreference. Once this is declared, you can define it in its own class, similarly to what you've done above by extending DialogPreference:
public class ChoicePreference extends DialogPreference {
public ChoicePreference(Context context, AttributeSet attrs){
super(context,attrs);
}
#Override
protected void onClick(){
AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
dialog.setTitle("Check Time");
dialog.setMessage("confirm time:");
dialog.setCancelable(true);
dialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(getContext(), "time set!", Toast.LENGTH_SHORT).show();
putPrefValue("key_sel_time",true);
}
});
The putPrefValue(key,value) method will write/overwrite your sharedPreference value:
private void putPrefValue(String key, boolean value){
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
SharedPreferences.Editor editor = prefs.edit();
//removes existing key to trigger change listener
editor.remove(key);
editor.commit();
editor.putBoolean(key, value);
editor.apply();
}
Then from this point you'll need your Activity, for instance, to listen to these sharedPreference changes:
1.) set a listener variable for your sharedPrefs:
private static SharedPreferences sharedPrefs;
private static SharedPreferences.OnSharedPreferenceChangeListener mListener;
2.) register/unregister the listener
#Override
protected void onResume() {
super.onResume();
sharedPrefs.registerOnSharedPreferenceChangeListener(mListener);
}
#Override
protected void onPause() {
super.onPause();
sharedPrefs.unregisterOnSharedPreferenceChangeListener(mListener);
}
3.) get an instance and do something with the result/change
private void initSharedPrefsListener(){
sharedPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
mListener = new SharedPreferences.OnSharedPreferenceChangeListener(){
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPrefs, String key){
mBasePresenterImpl.onPrefSelected(key, sharedPrefs.getString(key, "default value here"));
}
};
}
The onPrefSelected method here is conceptual. You can could take the resulting value and persist it to a JsonString.

Create NumberPicker dialog in preference

I am trying to create a NumberPicker dialog in my preference screen. I have already made one following this:https://stackoverflow.com/a/5533295/2442638
However, for my second dialog, I only want one spinner, so I have adapted the code as follows:
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.NumberPicker;
public class SnoozeTPP extends DialogPreference {
private int Minute = 0;
private NumberPicker np= null;
public static int getMinute(String time) {
String[] pieces = time.split(":");
return (Integer.parseInt(pieces[1]));
}
public SnoozeTPP(Context context, AttributeSet attrs) {
super(context, attrs);
setPositiveButtonText("Set");
setNegativeButtonText("Cancel");
}
#Override
protected View onCreateDialogView() {
np = new NumberPicker(getContext());
return (np);
}
#Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
np.setMaxValue(60);
np.setValue(Minute);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
Minute = np.getValue();
String time = 0 + ":" + String.valueOf(Minute);
if (callChangeListener(time)) {
persistString(time);
}
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return (a.getString(index));
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
String time = null;
if (restoreValue) {
if (defaultValue == null) {
time = getPersistedString("08:00");
} else {
time = getPersistedString(defaultValue.toString());
}
} else {
time = defaultValue.toString();
}
Minute = getMinute(time);
}
}
There are no errors and the dialog pops up correctly, but the layout of it seems to be "messed up" :-). The blue line stretch across the whole dialog instead of just the width of the numbers.
The question is - how to set the layout correctly? (I am sure there are lots of other mistakes as well!)
Thank you
I solved this by using the CyanogenMod number picker.
Java file:
https://github.com/CyanogenMod/android_packages_apps_Trebuchet/blob/cm-10.2/src/com/cyanogenmod/trebuchet/preference/NumberPickerPreference.java
XML file:
https://github.com/CyanogenMod/android_packages_apps_Trebuchet/blob/cm-10.2/res/layout/number_picker_dialog.xml
Attributes:
https://github.com/CyanogenMod/android_packages_apps_Trebuchet/blob/cm-10.2/res/values/attrs.xml#L158
Here is an example of simple, but working NumberPickerPreference, saving integer value between 1 and 100:
NumberPickerPreference.java:
public class NumberPickerPreference extends DialogPreference {
private NumberPicker mPicker;
private Integer mNumber = 0;
public NumberPickerPreference(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public NumberPickerPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setPositiveButtonText(android.R.string.ok);
setNegativeButtonText(android.R.string.cancel);
}
#Override
protected View onCreateDialogView() {
mPicker = new NumberPicker(getContext());
mPicker.setMinValue(1);
mPicker.setMaxValue(100);
mPicker.setValue(mNumber);
return mPicker;
}
#Override
protected void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
// needed when user edits the text field and clicks OK
mPicker.clearFocus();
setValue(mPicker.getValue());
}
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
setValue(restoreValue ? getPersistedInt(mNumber) : (Integer) defaultValue);
}
public void setValue(int value) {
if (shouldPersist()) {
persistInt(value);
}
if (value != mNumber) {
mNumber = value;
notifyChanged();
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getInt(index, 0);
}
}
This is more a workaround than a solution, but i hope it helps. Adding a dummy textView solved the problem. I got exactly the same problem.
My xml File:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/textDummyEmpty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/textDummyEmpty" />
<NumberPicker
android:id="#+id/numberPicker1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
</LinearLayout>
and
android:text="#string/textDummyEmpty"
is an empty String. Maybe its also enough to use just a view instead of a textView.
return a LinearLayout in onCreateDialogView rather than NumberPicker as below:
#Override
protected View onCreateDialogView() {
numberPicker = new NumberPicker(getContext());
numberPicker.setMinValue(1);
numberPicker.setMaxValue(12);
numberPicker.setWrapSelectorWheel(false);
numberPicker.setValue(lastValue);
LinearLayout.LayoutParams pickerParams = new LinearLayout.LayoutParams
(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
pickerParams.gravity = Gravity.CENTER;
numberPicker.setLayoutParams(pickerParams);
LinearLayout layout = new LinearLayout(getContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams
(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setLayoutParams(params);
layout.addView(numberPicker);
return layout;
//return numberPicker;
}

Android DialogPreference NullPointerException in onRestoreInstanceState

I am trying to implement a DialogPreference with two NumberPicker objects, that restores the last changed NumberPicker values after orientation change:
public class CustomTimePreference extends DialogPreference {
public NumberPicker firstPicker, secondPicker;
private int lastHour = 0;
private int lastMinute = 15;
private int firstMaxValue;
private int tempHour;
private int tempMinute;
private int rotatedHour;
private int rotatedMinute;
private int firstMinValue = 0;
private int secondMinValue=0;
private int secondMaxValue=59;
private String headerText;
private boolean usedForApprox;
public static int getHour(String time){
String[] pieces = time.split(":");
return (Integer.parseInt(pieces[0]));
}
public static int getMinute(String time){
String[] pieces = time.split(":");
return (Integer.parseInt(pieces[1]));
}
public CustomTimePreference(Context context){
this(context, null);
}
public CustomTimePreference(Context context, AttributeSet attrs){
super(context, attrs);
init(attrs);
setDialogLayoutResource(R.layout.custom_time_preference);
setPositiveButtonText(context.getString(R.string.time_preference_set_text));
setNegativeButtonText(context.getString(R.string.time_preference_cancel_text));
}
private void init(AttributeSet attrs){
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CustomTimePreference);
firstMaxValue = a.getInteger(R.styleable.CustomTimePreference_firstMaxValue,10);
usedForApprox = a.getBoolean(R.styleable.CustomTimePreference_usedForApproximate, false);
headerText = a.getString(R.styleable.CustomTimePreference_customTimeDialogTopText);
a.recycle();
}
public void setFirstPickerValue(int value){
firstPicker.setValue(value);
}
public void setSecondPickerValue(int value){
secondPicker.setValue(value);
}
#Override
protected View onCreateDialogView(){
Log.d("OnCreateDialogView","nanana");
View root = super.onCreateDialogView();
TextView tv = (TextView)root.findViewById(R.id.custom_time_preference_title);
tv.setText(headerText);
firstPicker = (NumberPicker)root.findViewById(R.id.time_preference_first_picker);
firstPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// TODO Auto-generated method stub
tempHour = newVal;
}
});
secondPicker = (NumberPicker)root.findViewById(R.id.time_preference_second_picker);
secondPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
// TODO Auto-generated method stub
tempMinute = newVal;
}
});
if(usedForApprox){
int smallestValue = MainActivity.getShortestPeriodLength(getContext());
int second = smallestValue % 60;
second-=1;
firstPicker.setMaxValue(second);
secondPicker.setMaxValue(59);
} else {
firstPicker.setMaxValue(firstMaxValue);
secondPicker.setMaxValue(secondMaxValue);
}
firstPicker.setMinValue(firstMinValue);
secondPicker.setMinValue(secondMinValue);
return root;
}
#Override
protected void onBindDialogView(View v){
super.onBindDialogView(v);
firstPicker.setValue(lastHour);
secondPicker.setValue(lastMinute);
}
#Override
protected void onDialogClosed(boolean positiveResult){
super.onDialogClosed(positiveResult);
if(positiveResult){
lastHour = firstPicker.getValue();
lastMinute = secondPicker.getValue();
if (lastHour ==0 && lastMinute == 0){
lastMinute =1;
}
String time = String.valueOf(lastHour) + ":" + String.valueOf(lastMinute);
if(callChangeListener(time)){
persistString(time);
}
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index){
return a.getString(index);
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue){
String time = null;
if(restoreValue){
if (defaultValue == null){
time = getPersistedString("00:00");
} else {
time = getPersistedString(defaultValue.toString());
}
} else {
time = defaultValue.toString();
}
lastHour = tempHour = getHour(time);
lastMinute = tempMinute = getMinute(time);
}
private static class SavedState extends BaseSavedState {
// Member that holds the setting's value
// Change this data type to match the type saved by your Preference
String value;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
// Get the current preference's value
value = source.readString(); // Change this to read the appropriate data type
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
// Write the preference's value
dest.writeString(value); // Change this to write the appropriate data type
}
// Standard creator object using an instance of this class
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
#Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
// Check whether this Preference is persistent (continually saved)
/*
if (isPersistent()) {
// No need to save instance state since it's persistent, use superclass state
return superState;
}
*/
// Create instance of custom BaseSavedState
final SavedState myState = new SavedState(superState);
// Set the state's value with the class member that holds current setting value
myState.value = String.valueOf(tempHour) + ":" + String.valueOf(tempMinute);
return myState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
// Check whether we saved the state in onSaveInstanceState
if (state == null || !state.getClass().equals(SavedState.class)) {
// Didn't save the state, so call superclass
super.onRestoreInstanceState(state);
return;
}
// Cast state to custom BaseSavedState and pass to superclass
SavedState myState = (SavedState) state;
super.onRestoreInstanceState(myState.getSuperState());
// Set this Preference's widget to reflect the restored state
rotatedHour = getHour(myState.value);
rotatedMinute = getMinute(myState.value);
firstPicker.setValue(rotatedHour);
secondPicker.setValue(rotatedMinute);
}
}
There are two problems:
The app crashes, when i open my custom preference, change values on one of the pickers, and then rotate the phone from portrait to landscape. The error is NuLLpointerException and it points to the line where i try to assing the restored value to one of my NumberPicker objects.
This is more a question than a problem. I copied the BaseSavedState inner class and both onSaveInstanceState(), onRestoreInstanceState() functions from Android Developer homepage, but when i tried the app to restore values on orientation change, the phone showed the persisted values, not the latest values before orientation change. When i tried to examine the code with log messages, i discovered that my phone on SaveInstanceState isPersisted check exits the function and doesn`t operate with BaseSavedState object att all. I comented out that isPersisted check so now i can save and retrieve values from BaseSavedState object, but after that the problem nr. 1 appears. So my question is, what is the reasoning to skip the BaseSavedState object creation, if the preference is persistent. And is my decision to skip the persistent check and force the app to create BaseSavedState object a bad one?
Take a look at this implementation with 3 NumberPicker objects:
package com.bom.dom;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.NumberPicker;
import android.widget.NumberPicker.OnValueChangeListener;
public class TimePreference extends DialogPreference {
NumberPicker hoursNumberPicker;
NumberPicker minutesNumberPicker;
NumberPicker secondsNumberPicker;
int time;
int currentTime;
public TimePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onBindDialogView(View view) {
hoursNumberPicker = (NumberPicker) view.findViewById(R.id.numberpicker_hours);
hoursNumberPicker.setMaxValue(24);
hoursNumberPicker.setMinValue(0);
hoursNumberPicker.setOnValueChangedListener(new OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
updateCurrentTimeFromUI();
}
});
minutesNumberPicker = (NumberPicker) view.findViewById(R.id.numberpicker_minutes);
minutesNumberPicker.setMaxValue(59);
minutesNumberPicker.setMinValue(0);
minutesNumberPicker.setOnValueChangedListener(new OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
updateCurrentTimeFromUI();
}
});
secondsNumberPicker = (NumberPicker) view.findViewById(R.id.numberpicker_seconds);
secondsNumberPicker.setMaxValue(59);
secondsNumberPicker.setMinValue(0);
secondsNumberPicker.setOnValueChangedListener(new OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
updateCurrentTimeFromUI();
}
});
updateUI();
super.onBindDialogView(view);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
time = currentTime;
persistInt(time);
return;
}
currentTime = time;
}
private void updateCurrentTimeFromUI() {
int hours = hoursNumberPicker.getValue();
int minutes = minutesNumberPicker.getValue();
int seconds = secondsNumberPicker.getValue();
currentTime = hours * 3600 + minutes * 60 + seconds;
}
#Override
protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
if (restorePersistedValue) {
time = getPersistedInt(1);
} else {
time = (Integer) defaultValue;
persistInt(time);
}
currentTime = time;
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
Integer defaultValue = a.getInteger(index, 1);
return defaultValue;
}
private void updateUI() {
int hours = (int) (currentTime / 3600);
int minutes = ((int) (currentTime / 60)) % 60;
int seconds = currentTime % 60;
hoursNumberPicker.setValue(hours);
minutesNumberPicker.setValue(minutes);
secondsNumberPicker.setValue(seconds);
}
#Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
final SavedState myState = new SavedState(superState);
myState.value = currentTime;
return myState;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
if (state == null || !state.getClass().equals(SavedState.class)) {
super.onRestoreInstanceState(state);
return;
}
SavedState myState = (SavedState) state;
currentTime = myState.value;
super.onRestoreInstanceState(myState.getSuperState());
}
private static class SavedState extends BaseSavedState {
int value;
public SavedState(Parcelable superState) {
super(superState);
}
public SavedState(Parcel source) {
super(source);
value = source.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(value);
}
#SuppressWarnings("unused")
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
The layout file is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<NumberPicker
android:id="#+id/numberpicker_hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<NumberPicker
android:id="#+id/numberpicker_minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<NumberPicker
android:id="#+id/numberpicker_seconds"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
I found the solution on first problem:
To avoid the NullPointerException, i had to enclose the functions, that access NumberPicker objects, with a check, that determines if those pickers are initiated. I had this problem because on my app i have multiple my custom preference instances, and when i tried to follow the data path for saving/restoring functions, i discovered, that in logcat i had twice the amount of my own messages (not only for my onscreen preference, but also for the other prefrerence that uses my custom DialogPreference class). And because, i opened only one preference, the initialisation of NumberPicker objects in other preference didn`t happened, so accessing those pickers led (if i understood correctly) to NullPointerException.
But i still would like to hear someone more experienced, that could explain the default behaviour with save/restore functions.

Android TimePicker in PreferenceScreen -> read the values

I have a custom DialogPreference. The Dialog is called from a PreferenceScreen.
All works fine, the Dialog starts and shows the TimePicker.
But how do I get the selected values?
First of all, I tried to write the selected hours in the summary of the Preference. (therefore the var xxx :)
Later on, I want to save the values in SharedPreferences.
This is what I have for now:
public class Calendar extends DialogPreference implements
TimePicker.OnTimeChangedListener {
TimePicker tp;
int xxx;
public Calendar(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public Calendar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
private void initialize() {
setPersistent(true);
}
#Override
protected View onCreateDialogView() {
tp = new TimePicker(getContext());
tp.setIs24HourView(true);
return tp;
}
#Override
public void onTimeChanged(TimePicker arg0, int arg1, int arg2) {
}
#Override
public void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
// getEditor().
setTitle(getTitle());
setSummary(Integer.toString(xxx));
}
}
private TimePicker.OnTimeChangedListener mTimeSetListener =
new TimePicker.OnTimeChangedListener() {
#Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
xxx=hourOfDay;
}
};
}
Thanks a lot and best regards
Thanks for asking this question, it provided me with an important answer on how to create a DialogPreference.
I hope I might also have an answer for you. I modified your code a little bit and I can now store the time selected from the Dialog:
#Override
protected View onCreateDialogView() {
this.tp = new TimePicker(getContext());
this.tp.setIs24HourView(true);
final String storedValue = getPersistedString("07:00");
final String[] split = storedValue.split(":");
this.tp.setCurrentHour(Integer.parseInt(split[0]));
this.tp.setCurrentMinute(Integer.parseInt(split[1]));
return this.tp;
}
#Override
public void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
final String result = this.tp.getCurrentHour() + ":" + this.tp.getCurrentMinute();
persistString(result);
}
}
When the dialog is shown I retrieve the stored value and simply set the currentHour and currentMinute fields of the TimePicker. The other way round when the dialog is closed. Since I control both the format on the way in as well as on the way out there should not be a problem with illegal values.
Was this what you were looking for?
To store the value in shared pref, implement on preference Change Listener.
note that preference should be default Shared preference
preferences = PreferenceManager.getDefaultSharedPreferences(context);
editor = preferences.edit();
inside onprefchange:
if (preference.getKey().equals(getString(R.string.reminder_end_time_key))){
editor.putString("End_Date", String.valueOf(newValue));
editor.apply();
endTimePickerPreference.setSummary((CharSequence)newValue);
}

How to use DatePickerDialog as a Preference

I have preferences working and I am using a combination of CheckBoxPreference and EditTextPreference. I would like to replace one of them with a DatePickerDialog.
When my settings screen is showing, if you click on one of the preferences, I would like the date picker dialog to pop up for the user to select a date, and save the picked date in preferences. I have seen this work in other apps, but I cannot see how to do this.
I have the date picker dialog working from a regular view (as per tutorial), but I would to like to use it from a preference.
Thanks to #commonsware. I followed his project and created a date picker preference dialog. So it will help someone.
Follow the steps to open date picker in preference window.
1 . Create a custom dialog preference for date picker.
package com.packagename;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import android.content.Context;
import android.content.res.TypedArray;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import android.widget.DatePicker;
public class DatePreference extends DialogPreference {
private int lastDate = 0;
private int lastMonth = 0;
private int lastYear = 0;
private String dateval;
private CharSequence mSummary;
private DatePicker picker = null;
public static int getYear(String dateval) {
String[] pieces = dateval.split("-");
return (Integer.parseInt(pieces[0]));
}
public static int getMonth(String dateval) {
String[] pieces = dateval.split("-");
return (Integer.parseInt(pieces[1]));
}
public static int getDate(String dateval) {
String[] pieces = dateval.split("-");
return (Integer.parseInt(pieces[2]));
}
public DatePreference(Context ctxt, AttributeSet attrs) {
super(ctxt, attrs);
setPositiveButtonText("Set");
setNegativeButtonText("Cancel");
}
#Override
protected View onCreateDialogView() {
picker = new DatePicker(getContext());
// setCalendarViewShown(false) attribute is only available from API level 11
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
picker.setCalendarViewShown(false);
}
return (picker);
}
#Override
protected void onBindDialogView(View v) {
super.onBindDialogView(v);
picker.updateDate(lastYear, lastMonth + 1, lastDate);
}
#Override
protected void onDialogClosed(boolean positiveResult) {
super.onDialogClosed(positiveResult);
if (positiveResult) {
lastYear = picker.getYear();
lastMonth = picker.getMonth();
lastDate = picker.getDayOfMonth();
String dateval = String.valueOf(lastYear) + "-"
+ String.valueOf(lastMonth) + "-"
+ String.valueOf(lastDate);
if (callChangeListener(dateval)) {
persistString(dateval);
}
}
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return (a.getString(index));
}
#Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
dateval = null;
if (restoreValue) {
if (defaultValue == null) {
Calendar cal = Calendar.getInstance();
SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd");
String formatted = format1.format(cal.getTime());
dateval = getPersistedString(formatted);
} else {
dateval = getPersistedString(defaultValue.toString());
}
} else {
dateval = defaultValue.toString();
}
lastYear = getYear(dateval);
lastMonth = getMonth(dateval);
lastDate = getDate(dateval);
}
public void setText(String text) {
final boolean wasBlocking = shouldDisableDependents();
dateval = text;
persistString(text);
final boolean isBlocking = shouldDisableDependents();
if (isBlocking != wasBlocking) {
notifyDependencyChange(isBlocking);
}
}
public String getText() {
return dateval;
}
public CharSequence getSummary() {
return mSummary;
}
public void setSummary(CharSequence summary) {
if (summary == null && mSummary != null || summary != null
&& !summary.equals(mSummary)) {
mSummary = summary;
notifyChanged();
}
}
}
2 . Add the following code in preference xml located in "res/xml/yourpreference.xml"
<com.packagename.DatePreference
android:key="keyname"
android:title="Title of the preference"
android:defaultValue="2014-08-01"
android:summary="Summary"/>
Note: Change the "keyname","Title of the preference","2014-08-01","summary" as of your requirement
3 . If you want to change the default vaules through Preference Activity use the following code.
package com.packagename;
import android.os.Bundle;
import com.packagename.DatePreference;
public class CustomPreference extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
final DatePreference dp= (DatePreference) findPreference("keyname");
dp.setText("2014-08-02");
dp.setSummary("2014-08-02");
dp.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference,Object newValue) {
//your code to change values.
dp.setSummary((String) newValue);
return true;
}
});
}
}
Now Enjoy...
Here's an implementation ready to use in your project as a lib.
To quote the source:
Use it just like any other preference in your PreferenceScreen XML:
<org.bostonandroid.datepreference.DatePreference
android:key="dob" android:title="#string/dob"
android:defaultValue="1991.01.01" />
You would need to create a custom DialogPreference incorporating a DatePicker.
In androidx DialogPreference class implementation is split into DialogPreference that handles data persistence, and PreferenceDialogFragmentCompat that handles UI. Building on top of Mahendran Sakkarai's answer, this one and on EditTextPreference class as an example, it can be done like this.
1 . DatePreference class.
package com.example.util.timereminder.ui.prefs.custom;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import com.example.util.timereminder.R;
import androidx.preference.DialogPreference;
/**
* A dialog preference that shown calendar in the dialog.
*
* Saves a string value.
*/
public class DatePreference extends DialogPreference {
private String mDateValue;
public DatePreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected Object onGetDefaultValue(TypedArray a, int index) {
return a.getString(index);
}
#Override
protected void onSetInitialValue(Object defaultValue) {
setDate(getPersistedString((String) defaultValue));
}
/**
* Gets the date as a string from the current data storage.
*
* #return string representation of the date.
*/
public String getDate() {
return mDateValue;
}
/**
* Saves the date as a string in the current data storage.
*
* #param text string representation of the date to save.
*/
public void setDate(String text) {
final boolean wasBlocking = shouldDisableDependents();
mDateValue = text;
persistString(text);
final boolean isBlocking = shouldDisableDependents();
if (isBlocking != wasBlocking) {
notifyDependencyChange(isBlocking);
}
notifyChanged();
}
/**
* A simple {#link androidx.preference.Preference.SummaryProvider} implementation for an
* {#link DatePreference}. If no value has been set, the summary displayed will be 'Not
* set', otherwise the summary displayed will be the value set for this preference.
*/
public static final class SimpleSummaryProvider implements SummaryProvider<DatePreference> {
private static SimpleSummaryProvider sSimpleSummaryProvider;
private SimpleSummaryProvider() {}
/**
* Retrieve a singleton instance of this simple
* {#link androidx.preference.Preference.SummaryProvider} implementation.
*
* #return a singleton instance of this simple
* {#link androidx.preference.Preference.SummaryProvider} implementation
*/
public static SimpleSummaryProvider getInstance() {
if (sSimpleSummaryProvider == null) {
sSimpleSummaryProvider = new SimpleSummaryProvider();
}
return sSimpleSummaryProvider;
}
#Override
public CharSequence provideSummary(DatePreference preference) {
if (TextUtils.isEmpty(preference.getDate())) {
return (preference.getContext().getString(R.string.not_set));
} else {
return preference.getDate();
}
}
}
}
2 . DatePreferenceDialogFragment class.
package com.example.util.timereminder.ui.prefs.custom;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.DatePicker;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import androidx.preference.PreferenceDialogFragmentCompat;
public class DatePreferenceDialogFragment extends PreferenceDialogFragmentCompat {
private int mLastYear;
private int mLastMonth;
private int mLastDay;
private DatePicker mDatePicker;
public static DatePreferenceDialogFragment newInstance(String key) {
final DatePreferenceDialogFragment
fragment = new DatePreferenceDialogFragment();
final Bundle b = new Bundle(1);
b.putString(ARG_KEY, key);
fragment.setArguments(b);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String dateValue = getDatePreference().getDate();
if (dateValue == null || dateValue.isEmpty()) {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
dateValue = df.format(calendar.getTime());
}
mLastYear = getYear(dateValue);
mLastMonth = getMonth(dateValue);
mLastDay = getDay(dateValue);
}
#Override
protected View onCreateDialogView(Context context) {
mDatePicker = new DatePicker(getContext());
// Show spinner dialog for old APIs.
mDatePicker.setCalendarViewShown(false);
return mDatePicker;
}
#Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
mDatePicker.updateDate(mLastYear, mLastMonth - 1, mLastDay);
}
#Override
public void onDialogClosed(boolean positiveResult) {
if (positiveResult) {
mLastYear = mDatePicker.getYear();
mLastMonth = mDatePicker.getMonth() + 1;
mLastDay = mDatePicker.getDayOfMonth();
String dateVal = String.valueOf(mLastYear) + "-"
+ String.valueOf(mLastMonth) + "-"
+ String.valueOf(mLastDay);
final DatePreference preference = getDatePreference();
if (preference.callChangeListener(dateVal)) {
preference.setDate(dateVal);
}
}
}
private DatePreference getDatePreference() {
return (DatePreference) getPreference();
}
private int getYear(String dateString) {
String[] datePieces = dateString.split("-");
return (Integer.parseInt(datePieces[0]));
}
private int getMonth(String dateString) {
String[] datePieces = dateString.split("-");
return (Integer.parseInt(datePieces[1]));
}
private int getDay(String dateString) {
String[] datePieces = dateString.split("-");
return (Integer.parseInt(datePieces[2]));
}
}
3 . In PreferenceFragment.
package com.example.util.timereminder.ui.prefs;
import android.os.Bundle;
import com.example.util.timereminder.R;
import com.example.util.timereminder.ui.prefs.custom.DatePreferenceDialogFragment;
import com.example.util.timereminder.ui.prefs.custom.DatePreference;
import androidx.fragment.app.DialogFragment;
import androidx.preference.EditTextPreference;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup;
/**
* Displays different preferences.
*/
public class PrefsFragment extends PreferenceFragmentCompat {
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences);
initSummary(getPreferenceScreen());
}
#Override
public void onDisplayPreferenceDialog(Preference preference) {
if (preference instanceof DatePreference) {
final DialogFragment f;
f = DatePreferenceDialogFragment.newInstance(preference.getKey());
f.setTargetFragment(this, 0);
f.show(getFragmentManager(), null);
} else {
super.onDisplayPreferenceDialog(preference);
}
}
/**
* Walks through all preferences.
*
* #param p The starting preference to search from.
*/
private void initSummary(Preference p) {
if (p instanceof PreferenceGroup) {
PreferenceGroup pGrp = (PreferenceGroup) p;
for (int i = 0; i < pGrp.getPreferenceCount(); i++) {
initSummary(pGrp.getPreference(i));
}
} else {
setPreferenceSummary(p);
}
}
/**
* Sets up summary providers for the preferences.
*
* #param p The preference to set up summary provider.
*/
private void setPreferenceSummary(Preference p) {
// No need to set up preference summaries for checkbox preferences because
// they can be set up in xml using summaryOff and summary On
if (p instanceof DatePreference) {
p.setSummaryProvider(DatePreference.SimpleSummaryProvider.getInstance());
} else if (p instanceof EditTextPreference) {
p.setSummaryProvider(EditTextPreference.SimpleSummaryProvider.getInstance());
}
}
}
4 . And in preference.xml. If default value left out, calendar opens on the current date.
<com.example.util.timereminder.ui.prefs.custom.DatePreference
android:title="#string/prefs_date_of_birth_title"
android:key="#string/prefs_date_of_birth_key"
android:defaultValue="2014-08-01"
app:iconSpaceReserved="false"/>
Just a simple way to use TimePickerFragment in the settings, it doesn't really answer your question, but it can help some guys.
Please read this before: https://developer.android.com/guide/topics/ui/settings
From "Overview" to "Preference... attributes" of course... Oo
controller.fragments
public class TimePickerFragment extends DialogFragment {
private TimePickerDialog.OnTimeSetListener onTimeSetListener;
private int hours;
private int minutes;
TimePickerFragment(TimePickerDialog.OnTimeSetListener onTimeSetListener, int hours, int minutes) {
this.onTimeSetListener = onTimeSetListener;
this.hours = hours;
this.minutes = minutes;
}
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
return new TimePickerDialog(getActivity(), R.style.dateTimePicker,
onTimeSetListener, hours, minutes, DateFormat.is24HourFormat(getActivity()));
}
}
res.values.style
<style name="dateTimePicker" parent="ThemeOverlay.MaterialComponents.Dialog">
<item name="colorAccent">#color/colorPrimary</item>
</style>
res.xml.root_preferences.xml create the xml folder in res & the file root_preferences of course Oo
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory app:title="Set time">
<Preference
app:key="set_time"
app:title="Set time"
app:summary="bla bla bla"/>
</PreferenceCategory>
</PreferenceScreen>
controller.activities.SettingsActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_activity);
getSupportFragmentManager()
.beginTransaction()
.add(R.id.settings, new SettingsFragment())
.commit();
}
controller.fragments.SettingsFragment
public class SettingsFragment extends PreferenceFragmentCompat implements TimePickerDialog.OnTimeSetListener {
private Preference setTime;
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey);
getPreference();
configListener();
}
private void getPreference() {
setTime = findPreference("set_time");
}
private void configListener() {
if (setTime != null){
setTime.setOnPreferenceClickListener(preference -> {
showTimeDialog(preference);
return true;
});
}
}
private void showTimeDialog(Preference preference) {
String value = preference.getSharedPreferences().getString("set_time", "12:00");
String[] time = value.split(":");
int hours = Integer.parseInt(time[0]);
int minutes = Integer.parseInt(time[1]);
if (getFragmentManager() != null) {
new TimePickerFragment(this, hours, minutes)
.show(getFragmentManager(), getString(R.string.tag_time_picker));
}
}
#Override
public void onTimeSet(TimePicker timePicker, int h, int m) {
String time = format(Locale.getDefault(),"%02d", h) + ":" + format(Locale.getDefault(), "%02d", m);
SharedPreferences sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences.edit().putString("set_time", time).apply();
// if you use setOnPreferenceChangeListener on it, use setTime.callChangeListener(time);
}
}
I didn't document anything because after reading the guide, you should all understand ^^
While looking for a TimePicker to use in preferences I found this thread.
I'd like to point out that there is also a TimePicker project in that repo (link).
The only problem is that the default.jardesc is not available, but can easily be made from the corresponding one in the DatePicker project.
I will show my code to select and save the date from SettingsActivity.java, applied in the Preferences fragment class. Works on every devices, from API level 16, to API level 29.
First, put this tag between <PreferenceScreen>, or between <PreferenceCategory> tag (if you have) in your "root_preferences.xml" file:
<Preference
app:key="date_pref"
app:title="Set date"
app:useSimpleSummaryProvider="true" />
And final, put this code in the SettingsActivity.java file, in the sub class named SettingsFragment:
public static class SettingsFragment extends PreferenceFragmentCompat {
SharedPreferences settings;
#Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
settings = requireContext().getSharedPreferences(requireContext().getPackageName() + "_preferences", Context.MODE_PRIVATE);
setPreferencesFromResource(R.xml.root_preferences, rootKey);
Preference date_pref = getPreferenceManager().findPreference("date_pref");
assert date_pref != null;
date_pref.setSummary(settings.getString("date_pref", ""));
date_pref.setOnPreferenceClickListener(datePicker);
}
Preference.OnPreferenceClickListener datePicker = new Preference.OnPreferenceClickListener() {
#Override
public boolean onPreferenceClick(Preference preference) {
Calendar cal = Calendar.getInstance();
int cal_month = cal.get(Calendar.MONTH);
int cal_day = cal.get(Calendar.DAY_OF_MONTH);
int cal_year = cal.get(Calendar.YEAR);
DatePickerDialog datePickerDialog = new DatePickerDialog(requireActivity(), new DatePickerDialog.OnDateSetListener() {
#Override
public void onDateSet(DatePicker view, int year, int month, int day) {
String datePicked = day + "." + month + "." + year;
settings.edit().putString("date_pref", datePicked).apply();
preference.setSummary(datePicked);
}
}, cal_year, cal_month, cal_day);
datePickerDialog.show();
return false;
}
};
}
Don't forget this imports:
import android.app.DatePickerDialog;
import android.content.Context;
import android.content.SharedPreferences;
import java.util.Calendar;
And don't forget to Vote Up my post :)

Categories

Resources