Correct the calculations for choosing the earliest day to show in the calendar / selected day (#3923)
To determine the earliest day to show in the calendar, take the current date/time, add the minimum scheduled seconds buffer (which may roll the date/time over to the next day), and then clamp to the start of that day. So it's either today (if the current time + minimum scheduled seconds is less than midnight) or it's tomorrow. When displaying the calendar work around a misfeature in Material Date Picker. It accepts UTC seconds-since-epoch, but does not convert it to the local time for display. While I'm here, show the selected day in the time picker's title. Fixes https://github.com/tuskyapp/Tusky/issues/3916
This commit is contained in:
parent
846289b8cc
commit
5d4c14aed9
|
@ -57,7 +57,9 @@ class ComposeScheduleView
|
||||||
).apply {
|
).apply {
|
||||||
timeZone = TimeZone.getTimeZone("UTC")
|
timeZone = TimeZone.getTimeZone("UTC")
|
||||||
}
|
}
|
||||||
private var scheduleDateTime: Calendar? = null
|
|
||||||
|
/** The date/time the user has chosen to schedule the status, in UTC */
|
||||||
|
private var scheduleDateTimeUtc: Calendar? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
binding.scheduledDateTime.setOnClickListener { openPickDateDialog() }
|
binding.scheduledDateTime.setOnClickListener { openPickDateDialog() }
|
||||||
|
@ -71,13 +73,13 @@ class ComposeScheduleView
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateScheduleUi() {
|
private fun updateScheduleUi() {
|
||||||
if (scheduleDateTime == null) {
|
if (scheduleDateTimeUtc == null) {
|
||||||
binding.scheduledDateTime.text = ""
|
binding.scheduledDateTime.text = ""
|
||||||
binding.invalidScheduleWarning.visibility = GONE
|
binding.invalidScheduleWarning.visibility = GONE
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val scheduled = scheduleDateTime!!.time
|
val scheduled = scheduleDateTimeUtc!!.time
|
||||||
binding.scheduledDateTime.text = String.format(
|
binding.scheduledDateTime.text = String.format(
|
||||||
"%s %s",
|
"%s %s",
|
||||||
dateFormat.format(scheduled),
|
dateFormat.format(scheduled),
|
||||||
|
@ -98,21 +100,37 @@ class ComposeScheduleView
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resetSchedule() {
|
fun resetSchedule() {
|
||||||
scheduleDateTime = null
|
scheduleDateTimeUtc = null
|
||||||
updateScheduleUi()
|
updateScheduleUi()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openPickDateDialog() {
|
fun openPickDateDialog() {
|
||||||
val yesterday = Calendar.getInstance().timeInMillis - 24 * 60 * 60 * 1000
|
// The earliest point in time the calendar should display. Start with current date/time
|
||||||
|
val earliest = calendar().apply {
|
||||||
|
// Add the minimum scheduling interval. This may roll the calendar over to the
|
||||||
|
// next day (e.g. if the current time is 23:57).
|
||||||
|
add(Calendar.SECOND, MINIMUM_SCHEDULED_SECONDS)
|
||||||
|
// Clear out the time components, so it's midnight
|
||||||
|
set(Calendar.HOUR_OF_DAY, 0)
|
||||||
|
set(Calendar.MINUTE, 0)
|
||||||
|
set(Calendar.SECOND, 0)
|
||||||
|
set(Calendar.MILLISECOND, 0)
|
||||||
|
}
|
||||||
val calendarConstraints = CalendarConstraints.Builder()
|
val calendarConstraints = CalendarConstraints.Builder()
|
||||||
.setValidator(
|
.setValidator(DateValidatorPointForward.from(earliest.timeInMillis))
|
||||||
DateValidatorPointForward.from(yesterday)
|
|
||||||
)
|
|
||||||
.build()
|
.build()
|
||||||
initializeSuggestedTime()
|
initializeSuggestedTime()
|
||||||
|
|
||||||
|
// Work around a misfeature in MaterialDatePicker. The `selection` is treated as
|
||||||
|
// millis-from-epoch, in UTC, which is good. However, it is also *displayed* in UTC
|
||||||
|
// instead of converting to the user's local timezone.
|
||||||
|
//
|
||||||
|
// So we have to add the TZ offset before setting it in the picker
|
||||||
|
val tzOffset = TimeZone.getDefault().getOffset(scheduleDateTimeUtc!!.timeInMillis)
|
||||||
|
|
||||||
val picker = MaterialDatePicker.Builder
|
val picker = MaterialDatePicker.Builder
|
||||||
.datePicker()
|
.datePicker()
|
||||||
.setSelection(scheduleDateTime!!.timeInMillis)
|
.setSelection(scheduleDateTimeUtc!!.timeInMillis + tzOffset)
|
||||||
.setCalendarConstraints(calendarConstraints)
|
.setCalendarConstraints(calendarConstraints)
|
||||||
.build()
|
.build()
|
||||||
picker.addOnPositiveButtonClickListener { selection: Long -> onDateSet(selection) }
|
picker.addOnPositiveButtonClickListener { selection: Long -> onDateSet(selection) }
|
||||||
|
@ -129,11 +147,12 @@ class ComposeScheduleView
|
||||||
|
|
||||||
private fun openPickTimeDialog() {
|
private fun openPickTimeDialog() {
|
||||||
val pickerBuilder = MaterialTimePicker.Builder()
|
val pickerBuilder = MaterialTimePicker.Builder()
|
||||||
scheduleDateTime?.let {
|
scheduleDateTimeUtc?.let {
|
||||||
pickerBuilder.setHour(it[Calendar.HOUR_OF_DAY])
|
pickerBuilder.setHour(it[Calendar.HOUR_OF_DAY])
|
||||||
.setMinute(it[Calendar.MINUTE])
|
.setMinute(it[Calendar.MINUTE])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pickerBuilder.setTitleText(dateFormat.format(scheduleDateTimeUtc!!.timeInMillis))
|
||||||
pickerBuilder.setTimeFormat(getTimeFormat(context))
|
pickerBuilder.setTimeFormat(getTimeFormat(context))
|
||||||
|
|
||||||
val picker = pickerBuilder.build()
|
val picker = pickerBuilder.build()
|
||||||
|
@ -154,7 +173,7 @@ class ComposeScheduleView
|
||||||
fun setDateTime(scheduledAt: String?) {
|
fun setDateTime(scheduledAt: String?) {
|
||||||
val date = getDateTime(scheduledAt) ?: return
|
val date = getDateTime(scheduledAt) ?: return
|
||||||
initializeSuggestedTime()
|
initializeSuggestedTime()
|
||||||
scheduleDateTime!!.time = date
|
scheduleDateTimeUtc!!.time = date
|
||||||
updateScheduleUi()
|
updateScheduleUi()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,24 +199,24 @@ class ComposeScheduleView
|
||||||
// see https://github.com/material-components/material-components-android/issues/882
|
// see https://github.com/material-components/material-components-android/issues/882
|
||||||
newDate.timeZone = TimeZone.getTimeZone("UTC")
|
newDate.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
newDate.timeInMillis = selection
|
newDate.timeInMillis = selection
|
||||||
scheduleDateTime!![newDate[Calendar.YEAR], newDate[Calendar.MONTH]] = newDate[Calendar.DATE]
|
scheduleDateTimeUtc!![newDate[Calendar.YEAR], newDate[Calendar.MONTH]] = newDate[Calendar.DATE]
|
||||||
openPickTimeDialog()
|
openPickTimeDialog()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onTimeSet(hourOfDay: Int, minute: Int) {
|
private fun onTimeSet(hourOfDay: Int, minute: Int) {
|
||||||
initializeSuggestedTime()
|
initializeSuggestedTime()
|
||||||
scheduleDateTime?.set(Calendar.HOUR_OF_DAY, hourOfDay)
|
scheduleDateTimeUtc?.set(Calendar.HOUR_OF_DAY, hourOfDay)
|
||||||
scheduleDateTime?.set(Calendar.MINUTE, minute)
|
scheduleDateTimeUtc?.set(Calendar.MINUTE, minute)
|
||||||
updateScheduleUi()
|
updateScheduleUi()
|
||||||
listener?.onTimeSet(time)
|
listener?.onTimeSet(time)
|
||||||
}
|
}
|
||||||
|
|
||||||
val time: String?
|
val time: String?
|
||||||
get() = scheduleDateTime?.time?.let { iso8601.format(it) }
|
get() = scheduleDateTimeUtc?.time?.let { iso8601.format(it) }
|
||||||
|
|
||||||
private fun initializeSuggestedTime() {
|
private fun initializeSuggestedTime() {
|
||||||
if (scheduleDateTime == null) {
|
if (scheduleDateTimeUtc == null) {
|
||||||
scheduleDateTime = calendar().apply {
|
scheduleDateTimeUtc = calendar().apply {
|
||||||
add(Calendar.MINUTE, 15)
|
add(Calendar.MINUTE, 15)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue