New due date picker

pull/996/head
Alex Baker 4 years ago
parent 5078cd2d15
commit 84f0842449

@ -138,6 +138,10 @@ public class AndroidUtilities {
return !atLeastLollipop();
}
public static boolean preMarshmallow() {
return !atLeastMarshmallow();
}
public static boolean preOreo() {
return !atLeastOreo();
}

@ -16,6 +16,7 @@ import com.google.common.base.Strings;
import com.todoroo.astrid.data.Task;
import org.tasks.BuildConfig;
import org.tasks.R;
import org.tasks.locale.Locale;
import org.tasks.time.DateTime;
import org.threeten.bp.format.DateTimeFormatter;
import org.threeten.bp.format.FormatStyle;
@ -71,7 +72,7 @@ public class DateUtilities {
*/
public static String getDateString(Context context, DateTime date) {
return getRelativeDay(
context, date.getMillis(), java.util.Locale.getDefault(), FormatStyle.MEDIUM);
context, date.getMillis(), Locale.getInstance().getLocale(), FormatStyle.MEDIUM);
}
static String getWeekday(DateTime date, java.util.Locale locale) {
@ -79,7 +80,7 @@ public class DateUtilities {
}
/** @return weekday */
static String getWeekdayShort(DateTime date, java.util.Locale locale) {
public static String getWeekdayShort(DateTime date, java.util.Locale locale) {
return date.toLocalDate().getDayOfWeek().getDisplayName(TextStyle.SHORT, locale);
}
@ -107,7 +108,7 @@ public class DateUtilities {
return style == FormatStyle.SHORT || style == FormatStyle.MEDIUM;
}
static String getRelativeDay(
public static String getRelativeDay(
Context context,
long date,
java.util.Locale locale,

@ -0,0 +1,264 @@
package org.tasks.dialogs
import android.app.Activity
import android.app.Activity.RESULT_OK
import android.app.Dialog
import android.content.Intent
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.fragment.app.Fragment
import butterknife.ButterKnife
import butterknife.OnClick
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.AndroidUtilities.atLeastMarshmallow
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.data.Task
import org.tasks.R
import org.tasks.databinding.DialogDateTimePickerBinding
import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.dialogs.MyDatePickerDialog.newDatePicker
import org.tasks.dialogs.MyTimePickerDialog.newTimePicker
import org.tasks.injection.DialogFragmentComponent
import org.tasks.injection.InjectingBottomSheetDialogFragment
import org.tasks.locale.Locale
import org.tasks.preferences.Preferences
import org.tasks.time.DateTime
import org.threeten.bp.format.FormatStyle
import javax.inject.Inject
class DateTimePicker : InjectingBottomSheetDialogFragment() {
@Inject lateinit var activity: Activity
@Inject lateinit var preferences: Preferences
@Inject lateinit var locale: Locale
lateinit var binding: DialogDateTimePickerBinding
private var selected: DateTime? = null
private val today = newDateTime().startOfDay()
private val tomorrow = today.plusDays(1)
private val nextWeek = today.plusDays(7)
private var morning = 32401000
private var afternoon = 46801000
private var evening = 61201000
private var night = 72001000
companion object {
const val EXTRA_TIMESTAMP = "extra_timestamp"
private const val EXTRA_SELECTED = "extra_selected"
private const val REQUEST_TIME = 10101
private const val REQUEST_DATE = 10102
private const val FRAG_TAG_TIME_PICKER = "frag_tag_time_picker"
private const val FRAG_TAG_DATE_PICKER = "frag_tag_date_picker"
fun newDateTimePicker(target: Fragment, rc: Int, current: Long): DateTimePicker {
val bundle = Bundle()
bundle.putLong(EXTRA_TIMESTAMP, current)
val fragment = DateTimePicker()
fragment.arguments = bundle
fragment.setTargetFragment(target, rc)
return fragment
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DialogDateTimePickerBinding.inflate(inflater)
if (AndroidUtilities.preMarshmallow()) {
binding.shortcuts.pickDateButton.visibility = View.VISIBLE
}
morning = preferences.dateShortcutMorning + 1000
afternoon = preferences.dateShortcutAfternoon + 1000
evening = preferences.dateShortcutEvening + 1000
night = preferences.dateShortcutNight + 1000
binding.shortcuts.morningButton.text = DateUtilities.getTimeString(context, newDateTime().withMillisOfDay(morning))
binding.shortcuts.afternoonButton.text = DateUtilities.getTimeString(context, newDateTime().withMillisOfDay(afternoon))
binding.shortcuts.eveningButton.text = DateUtilities.getTimeString(context, newDateTime().withMillisOfDay(evening))
binding.shortcuts.nightButton.text = DateUtilities.getTimeString(context, newDateTime().withMillisOfDay(night))
ButterKnife.bind(this, binding.root)
binding.shortcuts.nextWeekButton.text =
getString(R.string.next, DateUtilities.getWeekdayShort(newDateTime().plusWeeks(1), locale.locale))
if (atLeastMarshmallow()) {
binding.calendarView.setOnDateChangeListener { _, y, m, d ->
selected = DateTime(y, m + 1, d, selected?.hourOfDay ?: 0, selected?.minuteOfHour
?: 0, selected?.secondOfMinute ?: 0)
returnDate(selected!!.millis)
refreshButtons()
}
val firstDayOfWeek = preferences.firstDayOfWeek
if (firstDayOfWeek in 1..7) {
binding.calendarView.firstDayOfWeek = firstDayOfWeek
}
}
val timestamp = savedInstanceState?.getLong(EXTRA_SELECTED, -1)
?: arguments!!.getLong(EXTRA_TIMESTAMP)
selected = if (timestamp > 0) DateTime(timestamp) else null
return binding.root
}
override fun onResume() {
super.onResume()
refreshButtons()
}
private fun refreshButtons() {
when (selected?.startOfDay()) {
null -> binding.shortcuts.dateGroup.check(R.id.no_date_button)
today -> binding.shortcuts.dateGroup.check(R.id.today_button)
tomorrow -> binding.shortcuts.dateGroup.check(R.id.tomorrow_button)
nextWeek -> binding.shortcuts.dateGroup.check(R.id.next_week_button)
else -> {
binding.shortcuts.dateGroup.check(R.id.current_date_selection)
binding.shortcuts.currentDateSelection.visibility = View.VISIBLE
binding.shortcuts.currentDateSelection.text =
DateUtilities.getRelativeDay(context, selected!!.millis, locale.locale, FormatStyle.MEDIUM)
}
}
if (Task.hasDueTime(selected?.millis ?: 0)) {
when (selected?.millisOfDay) {
morning -> binding.shortcuts.timeGroup.check(R.id.morning_button)
afternoon -> binding.shortcuts.timeGroup.check(R.id.afternoon_button)
evening -> binding.shortcuts.timeGroup.check(R.id.evening_button)
night -> binding.shortcuts.timeGroup.check(R.id.night_button)
else -> {
binding.shortcuts.timeGroup.check(R.id.current_time_selection)
binding.shortcuts.currentTimeSelection.visibility = View.VISIBLE
binding.shortcuts.currentTimeSelection.text = DateUtilities.getTimeString(context, selected)
}
}
} else {
binding.shortcuts.timeGroup.check(R.id.no_time)
}
if (atLeastMarshmallow() && selected != null) {
binding.calendarView.setDate(selected!!.millis, false, true)
}
}
@OnClick(R.id.no_date_button)
fun clearDate() = returnDate(0)
@OnClick(R.id.no_time)
fun clearTime() = returnDate(selected?.startOfDay()?.millis ?: 0)
@OnClick(R.id.today_button)
fun setToday() = returnDate(today.withMillisOfDay(selected?.millisOfDay ?: 0))
@OnClick(R.id.tomorrow_button)
fun setTomorrow() = returnDate(tomorrow.withMillisOfDay(selected?.millisOfDay ?: 0))
@OnClick(R.id.next_week_button)
fun setNextWeek() = returnDate(nextWeek.withMillisOfDay(selected?.millisOfDay ?: 0))
@OnClick(R.id.morning_button)
fun setMorning() = returnSelectedTime(morning)
@OnClick(R.id.afternoon_button)
fun setAfternoon() = returnSelectedTime(afternoon)
@OnClick(R.id.evening_button)
fun setEvening() = returnSelectedTime(evening)
@OnClick(R.id.night_button)
fun setNight() = returnSelectedTime(night)
@OnClick(R.id.current_date_selection)
fun currentDate() = dismiss()
@OnClick(R.id.current_time_selection)
fun currentTime() = dismiss()
@OnClick(R.id.pick_time_button)
fun pickTime() {
newTimePicker(this, REQUEST_TIME, selected?.millis ?: today.noon().millis)
.show(parentFragmentManager, FRAG_TAG_TIME_PICKER)
}
@OnClick(R.id.pick_date_button)
fun pickDate() {
newDatePicker(this, REQUEST_DATE, selected?.millis ?: today.millis)
.show(parentFragmentManager, FRAG_TAG_DATE_PICKER)
}
private fun returnSelectedTime(millisOfDay: Int) {
if (selected == null) {
selected = today.withMillisOfDay(millisOfDay)
if (selected!!.isBeforeNow) {
selected = selected!!.plusDays(1)
}
} else {
selected = selected!!.withMillisOfDay(millisOfDay)
}
returnDate(selected!!.millis)
}
private fun returnDate(dt: DateTime? = selected) = returnDate(dt?.millis ?: 0)
private fun returnDate(date: Long? = selected?.millis) {
selected = if (date == null || date <= 0) null else DateTime(date)
targetFragment?.onActivityResult(targetRequestCode, RESULT_OK, Intent().putExtra(EXTRA_TIMESTAMP, selected?.millis ?: 0))
dismiss()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putSerializable(EXTRA_SELECTED, selected?.millis)
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val bottomSheetDialog = super.onCreateDialog(savedInstanceState) as BottomSheetDialog
bottomSheetDialog.setOnShowListener {
val coordinator = (it as BottomSheetDialog)
.findViewById<CoordinatorLayout>(com.google.android.material.R.id.coordinator)
val containerLayout =
it.findViewById<FrameLayout>(com.google.android.material.R.id.container)
val buttons = bottomSheetDialog.layoutInflater.inflate(R.layout.dialog_date_time_picker_buttons, null)
buttons.findViewById<View>(R.id.cancel_button).setOnClickListener { dismiss() }
buttons.findViewById<View>(R.id.ok_button).setOnClickListener { dismiss() }
buttons.layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM
).apply {
gravity = Gravity.BOTTOM
}
containerLayout!!.addView(buttons)
buttons.post {
(coordinator!!.layoutParams as ViewGroup.MarginLayoutParams).apply {
buttons.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
this.bottomMargin = buttons.measuredHeight
containerLayout.requestLayout()
}
}
}
return bottomSheetDialog
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_TIME) {
if (resultCode == RESULT_OK) {
val timestamp = data!!.getLongExtra(MyTimePickerDialog.EXTRA_TIMESTAMP, today.millis)
returnSelectedTime(newDateTime(timestamp).millisOfDay + 1000)
}
} else if (requestCode == REQUEST_DATE) {
if (resultCode == RESULT_OK) {
val timestamp = data!!.getLongExtra(MyDatePickerDialog.EXTRA_TIMESTAMP, today.millis)
returnDate(timestamp)
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
override fun inject(component: DialogFragmentComponent) = component.inject(this)
}

@ -6,6 +6,7 @@ import org.tasks.calendars.CalendarPicker;
import org.tasks.dialogs.AddAttachmentDialog;
import org.tasks.dialogs.ColorPalettePicker;
import org.tasks.dialogs.ColorWheelPicker;
import org.tasks.dialogs.DateTimePicker;
import org.tasks.dialogs.ExportTasksDialog;
import org.tasks.dialogs.GeofenceDialog;
import org.tasks.dialogs.IconPickerDialog;
@ -58,4 +59,6 @@ public interface DialogFragmentComponent {
void inject(ColorWheelPicker colorWheelPicker);
void inject(ColorPalettePicker colorPalettePicker);
void inject(DateTimePicker dateTimePicker);
}

@ -0,0 +1,21 @@
package org.tasks.injection;
import android.app.Activity;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
public abstract class InjectingBottomSheetDialogFragment extends BottomSheetDialogFragment {
private boolean injected;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if (!injected) {
inject(((InjectingActivity) activity).getComponent().plus(new DialogFragmentModule(this)));
injected = true;
}
}
protected abstract void inject(DialogFragmentComponent component);
}

@ -26,6 +26,7 @@ import java.util.List;
import org.tasks.R;
import org.tasks.data.TaskContainer;
import org.tasks.dialogs.Linkify;
import org.tasks.locale.Locale;
import org.tasks.preferences.Preferences;
import org.tasks.ui.CheckBoxProvider;
import org.tasks.ui.ChipProvider;
@ -248,7 +249,7 @@ public class ViewHolder extends RecyclerView.ViewHolder {
}
String dateValue =
getRelativeDateTime(
context, task.getDueDate(), java.util.Locale.getDefault(), FormatStyle.MEDIUM);
context, task.getDueDate(), Locale.getInstance().getLocale(), FormatStyle.MEDIUM);
dueDate.setText(dateValue);
dueDate.setVisibility(View.VISIBLE);
} else {

@ -168,7 +168,13 @@ object CustomIcons {
1128 to R.drawable.ic_baseline_lens_24px,
1129 to R.drawable.ic_map_24px,
1130 to R.drawable.ic_check_black_24dp,
1131 to R.drawable.ic_undo_24px
1131 to R.drawable.ic_undo_24px,
1132 to R.drawable.ic_next_week_24px,
1133 to R.drawable.ic_local_cafe_24px,
1134 to R.drawable.ic_nights_stay_24px,
1135 to R.drawable.ic_single_bed_24px,
1136 to R.drawable.ic_weather_sunset,
1137 to R.drawable.ic_calendar_today_24px
)
@kotlin.jvm.JvmStatic

@ -1,127 +1,53 @@
package org.tasks.ui;
import static androidx.core.content.ContextCompat.getColor;
import static com.google.common.collect.Lists.newArrayList;
import static java.util.Arrays.asList;
import static org.tasks.date.DateTimeUtils.newDateTime;
import static org.tasks.dialogs.MyDatePickerDialog.newDatePicker;
import static org.tasks.dialogs.MyTimePickerDialog.newTimePicker;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.fragment.app.Fragment;
import butterknife.BindView;
import butterknife.OnClick;
import butterknife.OnItemSelected;
import butterknife.OnTouch;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.data.Task;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.tasks.R;
import org.tasks.dialogs.MyDatePickerDialog;
import org.tasks.dialogs.MyTimePickerDialog;
import org.tasks.dialogs.DateTimePicker;
import org.tasks.injection.ForActivity;
import org.tasks.injection.FragmentComponent;
import org.tasks.locale.Locale;
import org.tasks.preferences.Preferences;
import org.tasks.time.DateTime;
import org.threeten.bp.format.FormatStyle;
public class DeadlineControlSet extends TaskEditControlFragment {
public static final int TAG = R.string.TEA_ctrl_when_pref;
private static final int REQUEST_DATE = 504;
private static final int REQUEST_TIME = 505;
private static final String EXTRA_DATE = "extra_date";
private static final String EXTRA_TIME = "extra_time";
private static final String FRAG_TAG_DATE_PICKER = "frag_tag_date_picker";
private static final String FRAG_TAG_TIME_PICKER = "frag_tag_time_picker";
@Inject Preferences preferences;
@Inject @ForActivity Context context;
@Inject Locale locale;
@BindView(R.id.due_date)
Spinner dueDateSpinner;
@BindView(R.id.due_time)
Spinner dueTimeSpinner;
@BindView(R.id.clear)
View clearButton;
TextView dueDate;
private DueDateChangeListener callback;
private List<String> dueDateOptions = new ArrayList<>();
private List<String> dueTimeOptions = new ArrayList<>();
private List<String> dueTimeHint = new ArrayList<>();
private int dateShortcutMorning;
private int dateShortcutAfternoon;
private int dateShortcutEvening;
private int dateShortcutNight;
private String nightString;
private String eveningString;
private String afternoonString;
private String morningString;
private String noTimeString;
private String todayString;
private String tomorrowString;
private ArrayAdapter<String> dueDateAdapter;
private ArrayAdapter<String> dueTimeAdapter;
private long date = 0;
private int time = -1;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
callback = (DueDateChangeListener) activity;
dateShortcutMorning = preferences.getDateShortcutMorning();
dateShortcutAfternoon = preferences.getDateShortcutAfternoon();
dateShortcutEvening = preferences.getDateShortcutEvening();
dateShortcutNight = preferences.getDateShortcutNight();
dueTimeHint =
asList(
"",
"",
getTimeHint(dateShortcutMorning),
getTimeHint(dateShortcutAfternoon),
getTimeHint(dateShortcutEvening),
getTimeHint(dateShortcutNight));
nightString = activity.getString(R.string.date_shortcut_night);
eveningString = activity.getString(R.string.date_shortcut_evening);
afternoonString = activity.getString(R.string.date_shortcut_afternoon);
morningString = activity.getString(R.string.date_shortcut_morning);
noTimeString = activity.getString(R.string.TEA_no_time);
todayString = activity.getString(R.string.today);
tomorrowString = activity.getString(R.string.tomorrow);
dueDateOptions =
newArrayList("", todayString, tomorrowString, "", activity.getString(R.string.pick_a_date));
dueTimeOptions =
newArrayList(
"",
noTimeString,
morningString,
afternoonString,
eveningString,
nightString,
activity.getString(R.string.pick_a_time));
}
@Override
@ -135,144 +61,24 @@ public class DeadlineControlSet extends TaskEditControlFragment {
final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (savedInstanceState == null) {
if (task.hasDueDate()) {
DateTime dateTime = newDateTime(task.getDueDate());
date = dateTime.startOfDay().getMillis();
time = task.hasDueTime() ? dateTime.getMillisOfDay() : -1;
} else {
date = 0;
time = -1;
}
date = task.getDueDate();
} else {
date = savedInstanceState.getLong(EXTRA_DATE);
time = savedInstanceState.getInt(EXTRA_TIME);
}
final int overdueColor = getColor(context, R.color.overdue);
dueDateAdapter =
new HiddenTopArrayAdapter<String>(
context, android.R.layout.simple_spinner_item, dueDateOptions) {
@NonNull
@Override
public View getView(
final int position, final View convertView, @NonNull final ViewGroup parent) {
int selectedItemPosition = position;
if (parent instanceof AdapterView) {
selectedItemPosition = ((AdapterView) parent).getSelectedItemPosition();
}
TextView tv =
(TextView) inflater.inflate(android.R.layout.simple_spinner_item, parent, false);
tv.setPadding(0, 0, 0, 0);
tv.setText(dueDateOptions.get(selectedItemPosition));
int textColor;
if (date == 0) {
textColor = getColor(context, R.color.text_tertiary);
} else if (date < newDateTime().startOfDay().getMillis()) {
textColor = overdueColor;
} else {
textColor = getColor(context, R.color.text_primary);
}
dueDateSpinner.setBackgroundDrawable(getUnderline(textColor));
tv.setTextColor(textColor);
return tv;
}
};
dueDateSpinner.setAdapter(dueDateAdapter);
dueTimeAdapter =
new HiddenTopArrayAdapter<String>(
context, android.R.layout.simple_spinner_item, dueTimeOptions, dueTimeHint) {
@NonNull
@Override
public View getView(
final int position, final View convertView, @NonNull final ViewGroup parent) {
int selectedItemPosition = position;
if (parent instanceof AdapterView) {
selectedItemPosition = ((AdapterView) parent).getSelectedItemPosition();
}
TextView tv =
(TextView) inflater.inflate(android.R.layout.simple_spinner_item, parent, false);
tv.setPadding(0, 0, 0, 0);
tv.setText(dueTimeOptions.get(selectedItemPosition));
int textColor;
if (time == -1) {
textColor = getColor(context, R.color.text_tertiary);
} else if (newDateTime(date).withMillisOfDay(time).isBeforeNow()) {
textColor = overdueColor;
} else {
textColor = getColor(context, R.color.text_primary);
}
tv.setTextColor(textColor);
dueTimeSpinner.setBackgroundDrawable(getUnderline(textColor));
return tv;
}
};
dueTimeSpinner.setAdapter(dueTimeAdapter);
refreshDisplayView();
return view;
}
@OnClick(R.id.clear)
void clearTime(View view) {
setDate(0);
}
@OnTouch({R.id.due_date, R.id.due_time})
boolean onSpinnersTouched() {
AndroidUtilities.hideKeyboard(getActivity());
return false;
}
@OnItemSelected(R.id.due_date)
void onDateSelected(int position) {
DateTime today = newDateTime().startOfDay();
switch (position) {
case 0:
return;
case 1:
setDate(today.getMillis());
break;
case 2:
setDate(today.plusDays(1).getMillis());
break;
case 3:
setDate(today.plusWeeks(1).getMillis());
break;
case 4:
newDatePicker(this, REQUEST_DATE, date)
.show(getParentFragmentManager(), FRAG_TAG_DATE_PICKER);
updateDueDateOptions();
break;
}
}
@OnItemSelected(R.id.due_time)
void onTimeSelected(int position) {
switch (position) {
case 0:
return;
case 1:
setTime(-1);
break;
case 2:
setTime(dateShortcutMorning);
break;
case 3:
setTime(dateShortcutAfternoon);
break;
case 4:
setTime(dateShortcutEvening);
break;
case 5:
setTime(dateShortcutNight);
break;
case 6:
newTimePicker(this, REQUEST_TIME, getDueDateTime())
.show(getParentFragmentManager(), FRAG_TAG_TIME_PICKER);
updateDueTimeOptions();
break;
@OnTouch(R.id.due_date)
boolean showDateTimePicker() {
Fragment fragment = getParentFragmentManager().findFragmentByTag(FRAG_TAG_DATE_PICKER);
if (fragment == null) {
DateTimePicker.Companion.newDateTimePicker(this, REQUEST_DATE, getDueDateTime())
.show(getParentFragmentManager(), FRAG_TAG_DATE_PICKER);
}
return true;
}
@Override
@ -308,30 +114,23 @@ public class DeadlineControlSet extends TaskEditControlFragment {
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_DATE) {
if (resultCode == Activity.RESULT_OK) {
long timestamp = data.getLongExtra(MyDatePickerDialog.EXTRA_TIMESTAMP, 0L);
long timestamp = data.getLongExtra(DateTimePicker.EXTRA_TIMESTAMP, 0L);
DateTime dateTime = new DateTime(timestamp);
setDate(dateTime.getMillis());
} else {
refreshDisplayView();
}
} else if (requestCode == REQUEST_TIME) {
if (resultCode == Activity.RESULT_OK) {
long timestamp = data.getLongExtra(MyTimePickerDialog.EXTRA_TIMESTAMP, 0L);
DateTime dateTime = new DateTime(timestamp);
setTime(dateTime.getMillisOfDay());
} else {
refreshDisplayView();
date = dateTime.getMillis();
callback.dueDateChanged(getDueDateTime());
}
refreshDisplayView();
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
private long getDueDateTime() {
return time >= 0
? Task.createDueDate(
Task.URGENCY_SPECIFIC_DAY_TIME, newDateTime(date).withMillisOfDay(time).getMillis())
: Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, date);
return date == 0
? 0
: Task.createDueDate(
Task.hasDueTime(date) ? Task.URGENCY_SPECIFIC_DAY_TIME : Task.URGENCY_SPECIFIC_DAY,
date);
}
@Override
@ -339,98 +138,25 @@ public class DeadlineControlSet extends TaskEditControlFragment {
super.onSaveInstanceState(outState);
outState.putLong(EXTRA_DATE, date);
outState.putInt(EXTRA_TIME, time);
}
private String getTimeHint(int millisOfDay) {
DateTime dateTime = newDateTime().withMillisOfDay(millisOfDay);
return DateUtilities.getTimeString(context, dateTime);
}
private void refreshDisplayView() {
updateDueDateOptions();
updateDueTimeOptions();
clearButton.setVisibility(date > 0 ? View.VISIBLE : View.GONE);
}
private void updateDueDateOptions() {
DateTime today = newDateTime().startOfDay();
String nextWeekString = getString(R.string.next, today.plusWeeks(1).toString("EEEE"));
if (date == 0) {
dueDateOptions.set(0, getString(R.string.TEA_no_date));
dueDate.setText("");
setTextColor(false);
} else {
if (date == today.getMillis()) {
dueDateOptions.set(0, todayString);
} else if (date == today.plusDays(1).getMillis()) {
dueDateOptions.set(0, tomorrowString);
} else if (date == today.plusWeeks(1).getMillis()) {
dueDateOptions.set(0, nextWeekString);
} else {
dueDateOptions.set(0, DateUtilities.getLongDateString(newDateTime(date), locale.getLocale()));
}
}
dueDateOptions.set(3, nextWeekString);
dueDateAdapter.notifyDataSetChanged();
dueDateSpinner.setSelection(0);
}
private void updateDueTimeOptions() {
if (time == -1) {
dueTimeOptions.set(0, noTimeString);
} else {
int compareTime =
newDateTime()
.withMillisOfDay(time)
.withSecondOfMinute(0)
.withMillisOfSecond(0)
.getMillisOfDay();
if (compareTime == dateShortcutMorning) {
dueTimeOptions.set(0, morningString);
} else if (compareTime == dateShortcutAfternoon) {
dueTimeOptions.set(0, afternoonString);
} else if (compareTime == dateShortcutEvening) {
dueTimeOptions.set(0, eveningString);
} else if (compareTime == dateShortcutNight) {
dueTimeOptions.set(0, nightString);
} else {
dueTimeOptions.set(
0, DateUtilities.getTimeString(context, newDateTime().withMillisOfDay(time)));
}
}
dueTimeAdapter.notifyDataSetChanged();
dueTimeSpinner.setSelection(0);
}
private Drawable getUnderline(int color) {
Drawable drawable =
DrawableCompat.wrap(
ContextCompat.getDrawable(context, R.drawable.textfield_underline_black));
drawable.mutate();
DrawableCompat.setTint(drawable, color);
return drawable;
}
private void setDate(long millis) {
date = millis;
if (date == 0) {
time = -1;
dueDate.setText(
DateUtilities.getRelativeDateTime(context, date, locale.getLocale(), FormatStyle.FULL));
setTextColor(
Task.hasDueTime(date)
? newDateTime(date).isBeforeNow()
: newDateTime(date).endOfDay().isBeforeNow());
}
callback.dueDateChanged(getDueDateTime());
refreshDisplayView();
}
private void setTime(int millisOfDay) {
time = millisOfDay;
if (date == 0 && time >= 0) {
DateTime dateTime = newDateTime().withMillisOfDay(time);
if (dateTime.isBeforeNow()) {
dateTime = dateTime.plusDays(1);
}
date = dateTime.startOfDay().getMillis();
}
callback.dueDateChanged(getDueDateTime());
refreshDisplayView();
private void setTextColor(boolean overdue) {
dueDate.setTextColor(
ContextCompat.getColor(context, overdue ? R.color.overdue : R.color.text_primary));
}
public interface DueDateChangeListener {

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M20,3h-1L19,1h-2v2L7,3L7,1L5,1v2L4,3c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,5c0,-1.1 -0.9,-2 -2,-2zM20,21L4,21L4,10h16v11zM20,8L4,8L4,5h16v3z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M16,5v8c0,1.1 -0.9,2 -2,2L8,15c-1.1,0 -2,-0.9 -2,-2L6,5h10m4,-2L4,3v10c0,2.21 1.79,4 4,4h6c2.21,0 4,-1.79 4,-4v-3h2c1.11,0 2,-0.89 2,-2L22,5c0,-1.11 -0.89,-2 -2,-2zM18,8L18,5h2v3h-2zM20,19L2,19v2h18v-2z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M11,18.5l4,-4 -4,-4 -1,1 3,3 -3,3zM20,7h-4L16,5c0,-0.55 -0.22,-1.05 -0.59,-1.41C15.05,3.22 14.55,3 14,3h-4c-1.1,0 -2,0.9 -2,2v2L4,7c-1.1,0 -2,0.9 -2,2v11c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,9c0,-1.1 -0.9,-2 -2,-2zM10,5h4v2h-4L10,5zM20,20L4,20L4,9h16v11z"/>
</vector>

@ -0,0 +1,6 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M19.78,17.51c-2.47,0 -6.57,-1.33 -8.68,-5.43C8.77,7.57 10.6,3.6 11.63,2.01C6.27,2.2 1.98,6.59 1.98,12c0,0.14 0.02,0.28 0.02,0.42C2.61,12.16 3.28,12 3.98,12c0,0 0,0 0,0c0,-3.09 1.73,-5.77 4.3,-7.1C7.78,7.09 7.74,9.94 9.32,13c1.57,3.04 4.18,4.95 6.8,5.86c-1.23,0.74 -2.65,1.15 -4.13,1.15c-0.5,0 -1,-0.05 -1.48,-0.14c-0.37,0.7 -0.94,1.27 -1.64,1.64c0.98,0.32 2.03,0.5 3.11,0.5c3.5,0 6.58,-1.8 8.37,-4.52C20.18,17.5 19.98,17.51 19.78,17.51z"/>
<path android:fillColor="#FF000000" android:pathData="M7,16l-0.18,0C6.4,14.84 5.3,14 4,14c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.62,0 2.49,0 3,0c1.1,0 2,-0.9 2,-2C9,16.9 8.1,16 7,16z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M20,12c0,-1.1 -0.9,-2 -2,-2V7c0,-1.1 -0.9,-2 -2,-2H8C6.9,5 6,5.9 6,7v3c-1.1,0 -2,0.9 -2,2v5h1.33L6,19h1l0.67,-2h8.67L17,19h1l0.67,-2H20V12zM16,10h-3V7h3V10zM8,7h3v3H8V7zM6,12h12v3H6V12z"/>
</vector>

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M3,12H7A5,5 0,0 1,12 7A5,5 0,0 1,17 12H21A1,1 0,0 1,22 13A1,1 0,0 1,21 14H3A1,1 0,0 1,2 13A1,1 0,0 1,3 12M5,16H19A1,1 0,0 1,20 17A1,1 0,0 1,19 18H5A1,1 0,0 1,4 17A1,1 0,0 1,5 16M17,20A1,1 0,0 1,18 21A1,1 0,0 1,17 22H7A1,1 0,0 1,6 21A1,1 0,0 1,7 20H17M15,12A3,3 0,0 0,12 9A3,3 0,0 0,9 12H15M12,2L14.39,5.42C13.65,5.15 12.84,5 12,5C11.16,5 10.35,5.15 9.61,5.42L12,2M3.34,7L7.5,6.65C6.9,7.16 6.36,7.78 5.94,8.5C5.5,9.24 5.25,10 5.11,10.79L3.34,7M20.65,7L18.88,10.79C18.74,10 18.47,9.23 18.05,8.5C17.63,7.78 17.1,7.15 16.5,6.64L20.65,7Z"/>
</vector>

@ -1,32 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/due_date"
style="@style/TaskEditTextPrimary"
android:clickable="true"
android:focusable="true"
android:focusableInTouchMode="true"
android:hint="@string/no_due_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="DisableBaselineAlignment">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="100"
android:orientation="horizontal">
<Spinner
android:id="@+id/due_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"/>
<Spinner
android:id="@+id/due_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:background="@null"/>
</LinearLayout>
<include layout="@layout/control_set_clear_button"/>
</LinearLayout>
android:textAlignment="viewStart" />

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/date_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginLeft="16dp"
android:layout_marginBottom="16dp"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintRight_toLeftOf="@id/guideline"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.5"
app:singleSelection="true"
android:layout_marginStart="16dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/current_date_selection"
style="@style/DateTimeShortcuts"
tools:text="Nov 24, 2021"
android:visibility="gone"
app:icon="@drawable/ic_outline_today_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/today_button"
style="@style/DateTimeShortcuts"
android:text="@string/today"
app:icon="@drawable/ic_calendar_today_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/tomorrow_button"
style="@style/DateTimeShortcuts"
android:text="@string/tomorrow"
app:icon="@drawable/ic_outline_wb_sunny_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/today_button" />
<com.google.android.material.button.MaterialButton
android:id="@+id/next_week_button"
style="@style/DateTimeShortcuts"
tools:text="Next Thurs"
app:icon="@drawable/ic_next_week_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tomorrow_button" />
<com.google.android.material.button.MaterialButton
android:id="@+id/pick_date_button"
style="@style/DateTimeShortcuts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shortcut_pick_date"
android:visibility="gone"
app:icon="@drawable/ic_outline_today_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/next_week_button" />
<com.google.android.material.button.MaterialButton
android:id="@+id/no_date_button"
style="@style/DateTimeShortcuts"
android:text="@string/no_date"
app:icon="@drawable/ic_outline_not_interested_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pick_date_button" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.60" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/time_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="16dp"
android:orientation="vertical"
app:layout_constraintLeft_toRightOf="@id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_percent="0.5"
app:selectionRequired="true"
app:singleSelection="true"
android:layout_marginEnd="16dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/current_time_selection"
style="@style/DateTimeShortcuts"
app:icon="@drawable/ic_outline_schedule_24px"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="08:15" />
<com.google.android.material.button.MaterialButton
android:id="@+id/morning_button"
style="@style/DateTimeShortcuts"
tools:text="9 AM"
app:icon="@drawable/ic_local_cafe_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.button.MaterialButton
android:id="@+id/afternoon_button"
style="@style/DateTimeShortcuts"
android:layout_marginTop="1dp"
tools:text="1 PM"
app:icon="@drawable/ic_outline_wb_sunny_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/morning_button" />
<com.google.android.material.button.MaterialButton
android:id="@+id/evening_button"
style="@style/DateTimeShortcuts"
tools:text="5 PM"
app:icon="@drawable/ic_weather_sunset"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/afternoon_button" />
<com.google.android.material.button.MaterialButton
android:id="@+id/night_button"
style="@style/DateTimeShortcuts"
tools:text="8 PM"
app:icon="@drawable/ic_nights_stay_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/evening_button" />
<com.google.android.material.button.MaterialButton
android:id="@+id/pick_time_button"
style="@style/DateTimeShortcuts"
android:text="@string/shortcut_pick_time"
app:icon="@drawable/ic_outline_schedule_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/evening_button" />
<com.google.android.material.button.MaterialButton
android:id="@+id/no_time"
style="@style/DateTimeShortcuts"
android:text="@string/no_time"
app:icon="@drawable/ic_outline_not_interested_24px"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/night_button" />
</com.google.android.material.button.MaterialButtonToggleGroup>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
android:background="@color/dialog_background">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/keyline_first">
<include
layout="@layout/date_time_picker_shortcuts"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/shortcuts" />
<CalendarView
android:id="@+id/calendar_view"
android:layout_below="@id/shortcuts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@integer/calendar_view_visible"/>
</RelativeLayout>
</androidx.core.widget.NestedScrollView>

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/dialog_background">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end">
<View
style="@style/horizontal_divider"
android:id="@+id/divider"
android:background="@color/divider"
android:layout_alignParentTop="true" />
<com.google.android.material.button.MaterialButton
style="@style/Widget.MaterialComponents.Button.TextButton"
android:textColor="?attr/colorAccent"
android:id="@+id/ok_button"
android:text="@android:string/ok"
android:layout_below="@id/divider"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true" />
<com.google.android.material.button.MaterialButton
style="@style/Widget.MaterialComponents.Button.TextButton"
android:textColor="?attr/colorAccent"
android:layout_below="@id/divider"
android:id="@+id/cancel_button"
android:text="@android:string/cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/ok_button"
android:paddingEnd="@dimen/keyline_first"
android:paddingRight="@dimen/keyline_first"
android:layout_toStartOf="@id/ok_button" />
</RelativeLayout>
</FrameLayout>

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="calendar_view_visible">0</integer> <!-- View.VISIBLE -->
</resources>

@ -7,4 +7,5 @@
<integer name="default_afternoon">46800000</integer> <!-- 13:00 -->
<integer name="default_evening">61200000</integer> <!-- 17:00 -->
<integer name="default_night">72000000</integer> <!-- 20:00 -->
<integer name="calendar_view_visible">2</integer> <!-- View.GONE -->
</resources>

@ -574,4 +574,8 @@ File %1$s contained %2$s.\n\n
<string name="chip_appearance_text_and_icon">Text and icon</string>
<string name="chip_appearance_text_only">Text only</string>
<string name="chip_appearance_icon_only">Icon only</string>
<string name="no_date">No date</string>
<string name="no_time">No time</string>
<string name="shortcut_pick_time">Pick time</string>
<string name="shortcut_pick_date">Pick date</string>
</resources>

@ -91,6 +91,22 @@
<item name="android:textColor">@color/text_primary</item>
</style>
<style name="DateTimeShortcuts" parent="Widget.MaterialComponents.Button.TextButton">
<item name="android:textColor">@color/button_accent_text</item>
<item name="android:textAppearance">@style/TextAppearance.MaterialComponents.Body1</item>
<item name="android:paddingTop">8dp</item>
<item name="android:paddingBottom">8dp</item>
<item name="android:textSize">18sp</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="iconPadding">@dimen/keyline_first</item>
<item name="iconTint">@color/button_accent_text</item>
<item name="android:gravity">start|center_vertical</item>
<item name="backgroundTint">@android:color/transparent</item>
<item name="android:background">?attr/selectableItemBackground</item>
<item name="rippleColor">?attr/colorSecondary</item>
</style>
<!--=============================================== MainActivity == -->
<style name="BaseHorizontalDivider">

Loading…
Cancel
Save