package com.orgzly.android.ui.dialogs;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.DatePicker;
import android.widget.TimePicker;
import com.orgzly.BuildConfig;
import com.orgzly.R;
import com.orgzly.android.util.LogUtils;
import com.orgzly.android.util.UserTimeFormatter;
import com.orgzly.org.datetime.OrgDateTime;
import com.orgzly.org.datetime.OrgRepeater;
import java.util.Calendar;
import java.util.TreeSet;
public class TimestampDialogFragment extends DialogFragment {
private static final String TAG = TimestampDialogFragment.class.getName();
public static final String FRAGMENT_TAG = TimestampDialogFragment.class.getName();
private static final String ARG_DIALOG_ID = "id";
private static final String ARG_TITLE = "title";
private static final String ARG_NOTE_IDS = "note_ids";
private static final String ARG_TIME = "time";
private static final String ARG_YEAR = "year";
private static final String ARG_MONTH = "month";
private static final String ARG_DAY = "day";
private static final String ARG_USE_TIME = "use_time";
private static final String ARG_HOUR = "hour";
private static final String ARG_MINUTE = "minute";
private static final String ARG_USE_REPEAT = "use_repeat";
private static final String ARG_IS_ACTIVE = "is_active";
private static final String ARG_USE_END_TIME = "use_end_time";
private static final String ARG_END_TIME_HOUR = "end_time_hour";
private static final String ARG_END_TIME_MINUTE = "end_time_minute";
private AlertDialog mDialog;
/** Use by caller to know what's the timestamp for (scheduled, deadline, etc.). */
private int mId;
private TreeSet<Long> mNoteIds;
private OnDateTimeSetListener mActivityListener;
private Context mContext;
private UserTimeFormatter mUserTimeFormatter;
/*
* Buttons.
*/
private CompoundButton mIsActive;
private Button mDatePicker;
private Button mTimePicker;
private CompoundButton mIsTimeUsed;
private Button mRepeaterPicker;
private CompoundButton mIsRepeaterUsed;
private Button mEndTimePicker;
private CompoundButton mEndTime;
/*
*
*/
private int mCurrentYear;
private int mCurrentMonth;
private int mCurrentDay;
private int mCurrentHour;
private int mCurrentMinute;
// private EditText mRepeatValue;
// private Spinner mRepeatUnit;
private int mCurrentEndTimeHour;
private int mCurrentEndTimeMinute;
/* Without these, if creating local variables, getting:
*
* 11-05 19:44:20.080 E/WindowManager( 4250): android.view.WindowLeaked:
* Activity com.orgzly.android.ui.MainActivity has leaked window
* com.android.internal.policy.impl.PhoneWindow$DecorView{2a02b17e V.E..... R.....I. 0,0-800,442}
* that was originally added here
*/
private DatePickerDialog mDatePickerDialog;
private TimePickerDialog mTimePickerDialog;
private RepeaterPickerDialog mRepeaterPickerDialog;
private TimePickerDialog mEndTimePickerDialog;
/**
* @param id unique ID passed to every callback method. Useful for identifying dialog's invoker
* @param time
* @return
*/
public static TimestampDialogFragment getInstance(int id, int title, long noteId, OrgDateTime time) {
TreeSet<Long> noteIds = new TreeSet<>();
noteIds.add(noteId);
return TimestampDialogFragment.getInstance(id, title, noteIds, time);
}
public static TimestampDialogFragment getInstance(int id, int title, TreeSet<Long> noteIds, OrgDateTime time) {
TimestampDialogFragment fragment = new TimestampDialogFragment();
/* Set arguments for fragment. */
Bundle bundle = new Bundle();
bundle.putInt(ARG_DIALOG_ID, id);
bundle.putInt(ARG_TITLE, title);
bundle.putLongArray(ARG_NOTE_IDS, toArray(noteIds));
if (time != null) {
bundle.putString(ARG_TIME, time.toString());
}
fragment.setArguments(bundle);
return fragment;
}
private static long[] toArray(TreeSet<Long> set) {
int i = 0;
long[] result = new long[set.size()];
for (long e : set) {
result[i++] = e;
}
return result;
}
@Override
public void onSaveInstanceState(Bundle state) {
super.onSaveInstanceState(state);
state.putInt(ARG_YEAR, mCurrentYear);
state.putInt(ARG_MONTH, mCurrentMonth);
state.putInt(ARG_DAY, mCurrentDay);
state.putBoolean(ARG_USE_TIME, mIsTimeUsed.isChecked());
state.putInt(ARG_HOUR, mCurrentHour);
state.putInt(ARG_MINUTE, mCurrentMinute);
state.putBoolean(ARG_USE_REPEAT, mIsRepeaterUsed.isChecked());
state.putBoolean(ARG_IS_ACTIVE, mIsActive.isChecked());
state.putBoolean(ARG_USE_END_TIME, mEndTime.isChecked());
state.putInt(ARG_END_TIME_HOUR, mCurrentEndTimeHour);
state.putInt(ARG_END_TIME_MINUTE, mCurrentEndTimeMinute);
}
@Override
public void onCreate(Bundle savedInstanceState) {
if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, savedInstanceState);
super.onCreate(savedInstanceState);
}
/**
* Create a new instance of a dialog.
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, savedInstanceState);
mUserTimeFormatter = new UserTimeFormatter(getActivity());
/* This makes sure that the fragment has implemented
* the callback interface. If not, it throws an exception
*/
if (!(getTargetFragment() instanceof OnDateTimeSetListener)) {
throw new IllegalStateException("Fragment " + getTargetFragment() + " must implement " + OnDateTimeSetListener.class);
}
mActivityListener = (OnDateTimeSetListener) getTargetFragment();
mContext = getActivity();
mId = getArguments().getInt(ARG_DIALOG_ID);
mNoteIds = new TreeSet<>();
for (long e : getArguments().getLongArray(ARG_NOTE_IDS)) {
mNoteIds.add(e);
}
LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@SuppressLint("InflateParams") View view = inflater.inflate(R.layout.dialog_timestamp, null, false);
mIsActive = (CompoundButton) view.findViewById(R.id.dialog_timestamp_is_active);
mDatePicker = (Button) view.findViewById(R.id.dialog_timestamp_date_picker);
mTimePicker = (Button) view.findViewById(R.id.dialog_timestamp_time_picker);
mIsTimeUsed = (CompoundButton) view.findViewById(R.id.dialog_timestamp_time);
mRepeaterPicker = (Button) view.findViewById(R.id.dialog_timestamp_repeater);
mIsRepeaterUsed = (CompoundButton) view.findViewById(R.id.dialog_timestamp_repeat);
mEndTimePicker = (Button) view.findViewById(R.id.dialog_timestamp_end_time_picker);
mEndTime = (CompoundButton) view.findViewById(R.id.dialog_timestamp_end_time);
/*
* Also make labels toggle the compound buttons.
*/
view.findViewById(R.id.dialog_timestamp_is_active_label).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mIsActive.toggle();
}
});
// view.findViewById(R.id.dialog_timestamp_time_label).setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// mIsTimeUsed.toggle();
// }
// });
//
// view.findViewById(R.id.dialog_timestamp_repeat_label).setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// mIsRepeaterUsed.toggle();
// }
// });
//
// view.findViewById(R.id.dialog_timestamp_end_time_label).setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// mEndTime.toggle();
// }
// });
// mRepeatValue = (EditText) view.findViewById(R.id.dialog_timestamp_repeat_value);
// mRepeatUnit = (Spinner) view.findViewById(R.id.dialog_timestamp_repeat_unit);
/* Set before toggle buttons are setup, as they trigger dialog title update .*/
setValues(OrgDateTime.getInstanceOrNull(getArguments().getString(ARG_TIME)));
// mDialog = new AlertDialog.Builder(new ContextThemeWrapper(mContext, R.style.TimestampDialog))
mDialog = new AlertDialog.Builder(mContext)
.setTitle(getArguments().getInt(ARG_TITLE))
.setView(view)
.setPositiveButton(R.string.set, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (mActivityListener != null) {
OrgDateTime time = getCurrentOrgTime();
mActivityListener.onDateTimeSet(mId, mNoteIds, time);
}
}
})
.setNeutralButton(R.string.clear, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (mActivityListener != null) {
mActivityListener.onDateTimeCleared(mId, mNoteIds);
}
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (mActivityListener != null) {
mActivityListener.onDateTimeAborted(mId, mNoteIds);
}
}
})
.create();
setupDateShortcutsButtons(view);
setupPickerButtons();
setupToggleButtons();
restoreState(savedInstanceState);
setViewsFromCurrentValues();
return mDialog;
}
private void restoreState(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mCurrentYear = savedInstanceState.getInt(ARG_YEAR);
mCurrentMonth = savedInstanceState.getInt(ARG_MONTH);
mCurrentDay = savedInstanceState.getInt(ARG_DAY);
mIsTimeUsed.setChecked(savedInstanceState.getBoolean(ARG_USE_TIME));
mCurrentHour = savedInstanceState.getInt(ARG_HOUR);
mCurrentMinute = savedInstanceState.getInt(ARG_MINUTE);
mIsRepeaterUsed.setChecked(savedInstanceState.getBoolean(ARG_USE_REPEAT));
mIsActive.setChecked(savedInstanceState.getBoolean(ARG_IS_ACTIVE));
mEndTime.setChecked(savedInstanceState.getBoolean(ARG_USE_END_TIME));
mCurrentEndTimeHour = savedInstanceState.getInt(ARG_END_TIME_HOUR);
mCurrentEndTimeMinute = savedInstanceState.getInt(ARG_END_TIME_MINUTE);
}
}
/**
* Sets values from {@link OrgDateTime}.
* Uses *now* if time is not provided.
*/
private void setValues(OrgDateTime time) {
Calendar cal;
if (time != null) {
cal = time.getCalendar();
} else {
cal = Calendar.getInstance();
}
mIsActive.setChecked(time == null || time.isActive());
mCurrentYear = cal.get(Calendar.YEAR);
mCurrentMonth = cal.get(Calendar.MONTH);
mCurrentDay = cal.get(Calendar.DAY_OF_MONTH);
mIsTimeUsed.setChecked(time != null && time.hasTime());
mCurrentHour = cal.get(Calendar.HOUR_OF_DAY);
mCurrentMinute = cal.get(Calendar.MINUTE);
mEndTime.setChecked(false);
mCurrentEndTimeHour = cal.get(Calendar.HOUR_OF_DAY);
mCurrentEndTimeMinute = cal.get(Calendar.MINUTE);
if (time != null) {
mIsRepeaterUsed.setChecked(time.hasRepeater());
if (time.hasRepeater()) {
mRepeaterPicker.setText(time.getRepeater().toString());
}
}
}
private void setupDateShortcutsButtons(View view) {
/* Button - today. */
view.findViewById(R.id.dialog_timestamp_today_shortcut).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Calendar cal = Calendar.getInstance();
mCurrentYear = cal.get(Calendar.YEAR);
mCurrentMonth = cal.get(Calendar.MONTH);
mCurrentDay = cal.get(Calendar.DAY_OF_MONTH);
setViewsFromCurrentValues();
}
});
/* Button - tomorrow. */
view.findViewById(R.id.dialog_timestamp_tomorrow_shortcut).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, 1);
mCurrentYear = cal.get(Calendar.YEAR);
mCurrentMonth = cal.get(Calendar.MONTH);
mCurrentDay = cal.get(Calendar.DAY_OF_MONTH);
setViewsFromCurrentValues();
}
});
/* Button - next week. */
view.findViewById(R.id.dialog_timestamp_next_week_shortcut).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
cal.add(Calendar.DATE, 7);
mCurrentYear = cal.get(Calendar.YEAR);
mCurrentMonth = cal.get(Calendar.MONTH);
mCurrentDay = cal.get(Calendar.DAY_OF_MONTH);
setViewsFromCurrentValues();
}
});
}
private void setupPickerButtons() {
mDatePicker.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mDatePickerDialog = new DatePickerDialog(mContext, new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
mCurrentYear = year;
mCurrentMonth = monthOfYear;
mCurrentDay = dayOfMonth;
setViewsFromCurrentValues();
}
}, mCurrentYear, mCurrentMonth, mCurrentDay);
mDatePickerDialog.show();
}
});
mTimePicker.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTimePickerDialog = new TimePickerDialog(mContext, new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
mCurrentHour = hourOfDay;
mCurrentMinute = minute;
mIsTimeUsed.setChecked(true);
setViewsFromCurrentValues();
}
}, mCurrentHour, mCurrentMinute, true);
mTimePickerDialog.show();
}
});
mRepeaterPicker.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRepeaterPickerDialog = new RepeaterPickerDialog(mContext, new RepeaterPickerDialog.OnRepeaterSetListener() {
@Override
public void onRepeaterSet(OrgRepeater repeater) {
mRepeaterPicker.setText(repeater.toString());
mIsRepeaterUsed.setChecked(true);
setViewsFromCurrentValues();
}
}, mRepeaterPicker.getText().toString());
mRepeaterPickerDialog.show();
}
});
mEndTimePicker.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mEndTimePickerDialog = new TimePickerDialog(mContext, new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
mCurrentEndTimeHour = hourOfDay;
mCurrentEndTimeMinute = minute;
mEndTime.setChecked(true);
setViewsFromCurrentValues();
}
}, mCurrentEndTimeHour, mCurrentEndTimeMinute, true);
mEndTimePickerDialog.show();
}
});
}
/**
* NOTE: These callbacks are called not only on user press, but during initialization as well.
* Because of that, it's important that this method is called after dialog has been created.
*/
private void setupToggleButtons() {
mIsActive.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setViewsFromCurrentValues();
}
});
mIsTimeUsed.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setViewsFromCurrentValues();
}
});
mEndTime.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setViewsFromCurrentValues();
}
});
mIsRepeaterUsed.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
setViewsFromCurrentValues();
}
});
}
private OrgDateTime getCurrentOrgTime() {
OrgDateTime.Builder builder = new OrgDateTime.Builder()
.setIsActive(mIsActive.isChecked())
.setYear(mCurrentYear)
.setMonth(mCurrentMonth)
.setHasTime(mIsTimeUsed.isChecked())
.setDay(mCurrentDay)
.setHour(mCurrentHour)
.setMinute(mCurrentMinute);
if (mIsRepeaterUsed.isChecked()) {
OrgRepeater repeater = OrgRepeater.getInstance(mRepeaterPicker.getText().toString());
builder.setHasRepeater(true);
builder.setRepeater(repeater);
}
return builder.build();
//
//
// return OrgDateTime.getInstance(
// mIsActive.isChecked(),
// mIsTimeUsed.isChecked(),
// mCurrentYear,
// mCurrentMonth,
// mCurrentDay,
// mCurrentHour,
// mCurrentMinute);
}
private void setViewsFromCurrentValues() {
/*
* Check if dialog has been created.
* Toggle buttons get fired on initialization, calling this method.
* Should not happen before with the current ordering of setup methods, but just in case.
*/
if (mDialog != null) {
OrgDateTime time = getCurrentOrgTime();
mDatePicker.setText(mUserTimeFormatter.formatDate(time));
mTimePicker.setText(mUserTimeFormatter.formatTime(time));
if (time.hasRepeater()) {
mRepeaterPicker.setText(mUserTimeFormatter.formatRepeater(time));
}
// mEndTimePicker.setText(mOrgTimeFormatter.formatEndTime(time));
mDialog.setTitle(mUserTimeFormatter.formatAll(time));
}
}
@Override
public void onDismiss(DialogInterface dialog) {
if (BuildConfig.LOG_DEBUG) LogUtils.d(TAG, dialog);
super.onDismiss(dialog);
}
/**
* The callback used to indicate the user is done filling in the date.
*/
public interface OnDateTimeSetListener {
void onDateTimeSet(int id, TreeSet<Long> noteIds, OrgDateTime time);
void onDateTimeCleared(int id, TreeSet<Long> noteIds);
void onDateTimeAborted(int id, TreeSet<Long> noteIds);
}
}