@ -29,12 +29,15 @@ import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.R
import org.tasks.databinding.DialogDateTimePickerBinding
import org.tasks.databinding.DialogDateTimePickerBinding
import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.dialogs.MyTimePickerDialog.newTimePicker
import org.tasks.dialogs.MyTimePickerDialog.newTimePicker
import org.tasks.locale.Locale
import org.tasks.locale.Locale
import org.tasks.notifications.NotificationManager
import org.tasks.notifications.NotificationManager
import org.tasks.preferences.Preferences
import org.tasks.preferences.Preferences
import org.tasks.themes.Theme
import org.tasks.themes.Theme
import org.tasks.time.DateTime
import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils.millisOfDay
import org.tasks.time.DateTimeUtils.startOfDay
import java.time.format.FormatStyle
import java.time.format.FormatStyle
import javax.inject.Inject
import javax.inject.Inject
@ -49,12 +52,13 @@ class DateTimePicker : BottomSheetDialogFragment() {
@Inject lateinit var theme : Theme
@Inject lateinit var theme : Theme
lateinit var binding : DialogDateTimePickerBinding
lateinit var binding : DialogDateTimePickerBinding
private var customDate : DateTime ? = null
private var customDate = NO _DAY
private var selected : DateTime ? = null
private var customTime = NO _TIME
private var selectedDay = NO _DAY
private var selectedTime = NO _TIME
private val today = newDateTime ( ) . startOfDay ( )
private val today = newDateTime ( ) . startOfDay ( )
private val tomorrow = today . plusDays ( 1 )
private val tomorrow = today . plusDays ( 1 )
private val nextWeek = today . plusDays ( 7 )
private val nextWeek = today . plusDays ( 7 )
private var customTime = 0
private var morning = 32401000
private var morning = 32401000
private var afternoon = 46801000
private var afternoon = 46801000
private var evening = 61201000
private var evening = 61201000
@ -66,17 +70,25 @@ class DateTimePicker : BottomSheetDialogFragment() {
}
}
companion object {
companion object {
const val EXTRA _DAY = " extra_day "
const val EXTRA _TIME = " extra_time "
const val EXTRA _TASKS = " extra_tasks "
const val EXTRA _TIMESTAMP = " extra_timestamp "
const val EXTRA _TIMESTAMP = " extra_timestamp "
const val EXTRA _TASK = " extra_task "
private const val EXTRA _AUTO _CLOSE = " extra_auto_close "
private const val EXTRA _AUTO _CLOSE = " extra_auto_close "
private const val EXTRA _SELECTED = " extra_selected "
private const val REQUEST _TIME = 10101
private const val REQUEST _TIME = 10101
private const val FRAG _TAG _TIME _PICKER = " frag_tag_time_picker "
private const val FRAG _TAG _TIME _PICKER = " frag_tag_time_picker "
private const val NO _DAY = 0L
private const val NO _TIME = 0
private const val MULTIPLE _DAYS = - 1L
private const val MULTIPLE _TIMES = - 1
fun newDateTimePicker ( task : Long , current : Long , autoClose : Boolean ) : DateTimePicker {
fun newDateTimePicker ( autoClose: Boolean , vararg tasks : Task ) : DateTimePicker {
val bundle = Bundle ( )
val bundle = Bundle ( )
bundle . putLong ( EXTRA _TASK , task )
bundle . putLongArray ( EXTRA _TASKS , tasks . map { it . id } . toLongArray ( ) )
bundle . putLong ( EXTRA _TIMESTAMP , current )
val dueDates = tasks . map { it . dueDate . startOfDay ( ) } . toSet ( )
val dueTimes = tasks . map { it . dueDate . millisOfDay ( ) } . toSet ( )
bundle . putLong ( EXTRA _DAY , if ( dueDates . size == 1 ) dueDates . first ( ) else - 1 )
bundle . putInt ( EXTRA _TIME , if ( dueTimes . size == 1 ) dueTimes . first ( ) else - 1 )
bundle . putBoolean ( EXTRA _AUTO _CLOSE , autoClose )
bundle . putBoolean ( EXTRA _AUTO _CLOSE , autoClose )
val fragment = DateTimePicker ( )
val fragment = DateTimePicker ( )
fragment . arguments = bundle
fragment . arguments = bundle
@ -85,7 +97,8 @@ class DateTimePicker : BottomSheetDialogFragment() {
fun newDateTimePicker ( target : Fragment , rc : Int , current : Long , autoClose : Boolean ) : DateTimePicker {
fun newDateTimePicker ( target : Fragment , rc : Int , current : Long , autoClose : Boolean ) : DateTimePicker {
val bundle = Bundle ( )
val bundle = Bundle ( )
bundle . putLong ( EXTRA _TIMESTAMP , current )
bundle . putLong ( EXTRA _DAY , current . startOfDay ( ) )
bundle . putInt ( EXTRA _TIME , current . millisOfDay ( ) )
bundle . putBoolean ( EXTRA _AUTO _CLOSE , autoClose )
bundle . putBoolean ( EXTRA _AUTO _CLOSE , autoClose )
val fragment = DateTimePicker ( )
val fragment = DateTimePicker ( )
fragment . arguments = bundle
fragment . arguments = bundle
@ -108,18 +121,15 @@ class DateTimePicker : BottomSheetDialogFragment() {
binding . shortcuts . nextWeekButton . text =
binding . shortcuts . nextWeekButton . text =
getString ( R . string . next , DateUtilities . getWeekdayShort ( newDateTime ( ) . plusWeeks ( 1 ) , locale . locale ) )
getString ( R . string . next , DateUtilities . getWeekdayShort ( newDateTime ( ) . plusWeeks ( 1 ) , locale . locale ) )
binding . calendarView . setOnDateChangeListener { _ , y , m , d ->
binding . calendarView . setOnDateChangeListener { _ , y , m , d ->
selected = DateTime ( y , m + 1 , d , selected ?. hourOfDay ?: 0 , selected ?. minuteOfHour
returnDate ( day = DateTime ( y , m + 1 , d ) . millis )
?: 0 , selected ?. secondOfMinute ?: 0 )
returnDate ( selected !! . millis )
refreshButtons ( )
refreshButtons ( )
}
}
val firstDayOfWeek = preferences . firstDayOfWeek
val firstDayOfWeek = preferences . firstDayOfWeek
if ( firstDayOfWeek in 1. . 7 ) {
if ( firstDayOfWeek in 1. . 7 ) {
binding . calendarView . firstDayOfWeek = firstDayOfWeek
binding . calendarView . firstDayOfWeek = firstDayOfWeek
}
}
val timestamp = savedInstanceState ?. getLong ( EXTRA _SELECTED )
selectedDay = savedInstanceState ?. getLong ( EXTRA _DAY ) ?: requireArguments ( ) . getLong ( EXTRA _DAY )
?: requireArguments ( ) . getLong ( EXTRA _TIMESTAMP )
selectedTime = savedInstanceState ?. getInt ( EXTRA _TIME ) ?: requireArguments ( ) . getInt ( EXTRA _TIME )
selected = if ( timestamp > 0 ) DateTime ( timestamp ) else null
return binding . root
return binding . root
}
}
@ -141,54 +151,61 @@ class DateTimePicker : BottomSheetDialogFragment() {
}
}
private fun refreshButtons ( ) {
private fun refreshButtons ( ) {
when ( selected ?. startOf Day( ) ) {
when ( selected Day) {
null -> binding . shortcuts . dateGroup . check ( R . id . no _date _button )
0L -> binding . shortcuts . dateGroup . check ( R . id . no _date _button )
today -> binding . shortcuts . dateGroup . check ( R . id . today _button )
today . millis -> binding . shortcuts . dateGroup . check ( R . id . today _button )
tomorrow -> binding . shortcuts . dateGroup . check ( R . id . tomorrow _button )
tomorrow . millis -> binding . shortcuts . dateGroup . check ( R . id . tomorrow _button )
nextWeek -> binding . shortcuts . dateGroup . check ( R . id . next _week _button )
nextWeek . millis -> binding . shortcuts . dateGroup . check ( R . id . next _week _button )
else -> {
else -> {
customDate = selected
customDate = selected Day
binding . shortcuts . dateGroup . check ( R . id . current _date _selection )
binding . shortcuts . dateGroup . check ( R . id . current _date _selection )
binding . shortcuts . currentDateSelection . visibility = View . VISIBLE
binding . shortcuts . currentDateSelection . visibility = View . VISIBLE
binding . shortcuts . currentDateSelection . text =
binding . shortcuts . currentDateSelection . text = if ( customDate == MULTIPLE _DAYS ) {
DateUtilities . getRelativeDay ( context , selected !! . millis , locale . locale , FormatStyle . MEDIUM )
requireContext ( ) . getString ( R . string . date _picker _multiple )
} else {
DateUtilities . getRelativeDay ( context , selectedDay , locale . locale , FormatStyle . MEDIUM )
}
}
}
}
}
if ( Task . hasDueTime ( selected ?. millis ?: 0 ) ) {
if ( selectedTime == MULTIPLE _TIMES || Task . hasDueTime ( selected Time. toLong ( ) ) ) {
when ( selected ?. millisOfDay ) {
when ( selected Time ) {
morning -> binding . shortcuts . timeGroup . check ( R . id . morning _button )
morning -> binding . shortcuts . timeGroup . check ( R . id . morning _button )
afternoon -> binding . shortcuts . timeGroup . check ( R . id . afternoon _button )
afternoon -> binding . shortcuts . timeGroup . check ( R . id . afternoon _button )
evening -> binding . shortcuts . timeGroup . check ( R . id . evening _button )
evening -> binding . shortcuts . timeGroup . check ( R . id . evening _button )
night -> binding . shortcuts . timeGroup . check ( R . id . night _button )
night -> binding . shortcuts . timeGroup . check ( R . id . night _button )
else -> {
else -> {
customTime = selected !! . millisOfDay
customTime = selected Time
binding . shortcuts . timeGroup . check ( R . id . current _time _selection )
binding . shortcuts . timeGroup . check ( R . id . current _time _selection )
binding . shortcuts . currentTimeSelection . visibility = View . VISIBLE
binding . shortcuts . currentTimeSelection . visibility = View . VISIBLE
binding . shortcuts . currentTimeSelection . text = DateUtilities . getTimeString ( context , selected )
binding . shortcuts . currentTimeSelection . text = if ( customTime == MULTIPLE _TIMES ) {
requireContext ( ) . getString ( R . string . date _picker _multiple )
} else {
DateUtilities . getTimeString ( context , today . withMillisOfDay ( selectedTime ) )
}
}
}
}
}
} else {
} else {
binding . shortcuts . timeGroup . check ( R . id . no _time )
binding . shortcuts . timeGroup . check ( R . id . no _time )
}
}
if ( selected != null ) {
if ( selected Day > 0 ) {
binding . calendarView . setDate ( selected !! . millis , true , true )
binding . calendarView . setDate ( selected Day , true , true )
}
}
}
}
@OnClick ( R . id . no _date _button )
@OnClick ( R . id . no _date _button )
fun clearDate ( ) = returnDate ( 0 )
fun clearDate ( ) = returnDate ( day = 0 , time = 0 )
@OnClick ( R . id . no _time )
@OnClick ( R . id . no _time )
fun clearTime ( ) = returnDate ( selected?. startOfDay ( ) ?. millis ?: 0 )
fun clearTime ( ) = returnDate ( time = 0 )
@OnClick ( R . id . today _button )
@OnClick ( R . id . today _button )
fun setToday ( ) = returnDate ( today. withMillisOfDay ( selected ?. millisOfDay ?: 0 ) )
fun setToday ( ) = returnDate ( day = today . startOfDay ( ) . millis )
@OnClick ( R . id . tomorrow _button )
@OnClick ( R . id . tomorrow _button )
fun setTomorrow ( ) = returnDate ( tomorrow. withMillisOfDay ( selected ?. millisOfDay ?: 0 ) )
fun setTomorrow ( ) = returnDate ( day = tomorrow . startOfDay ( ) . millis )
@OnClick ( R . id . next _week _button )
@OnClick ( R . id . next _week _button )
fun setNextWeek ( ) = returnDate ( nextWeek. withMillisOfDay ( selected ?. millisOfDay ?: 0 ) )
fun setNextWeek ( ) = returnDate ( day = nextWeek . startOfDay ( ) . millis )
@OnClick ( R . id . morning _button )
@OnClick ( R . id . morning _button )
fun setMorning ( ) = returnSelectedTime ( morning )
fun setMorning ( ) = returnSelectedTime ( morning )
@ -203,33 +220,35 @@ class DateTimePicker : BottomSheetDialogFragment() {
fun setNight ( ) = returnSelectedTime ( night )
fun setNight ( ) = returnSelectedTime ( night )
@OnClick ( R . id . current _date _selection )
@OnClick ( R . id . current _date _selection )
fun currentDate ( ) = returnDate ( customDate)
fun currentDate ( ) = returnDate ( day = customDate)
@OnClick ( R . id . current _time _selection )
@OnClick ( R . id . current _time _selection )
fun currentTime ( ) = returnSelectedTime ( customTime )
fun currentTime ( ) = returnSelectedTime ( customTime )
@OnClick ( R . id . pick _time _button )
@OnClick ( R . id . pick _time _button )
fun pickTime ( ) {
fun pickTime ( ) {
newTimePicker ( this , REQUEST _TIME , selected ?. millis ?: today . noon ( ) . millis )
val time = if ( Task . hasDueTime ( today . withMillisOfDay ( selectedTime ) . millis ) ) {
selectedTime
} else {
today . noon ( ) . millisOfDay
}
newTimePicker ( this , REQUEST _TIME , today . withMillisOfDay ( time ) . millis )
. show ( parentFragmentManager , FRAG _TAG _TIME _PICKER )
. show ( parentFragmentManager , FRAG _TAG _TIME _PICKER )
}
}
private fun returnSelectedTime ( millisOfDay : Int ) {
private fun returnSelectedTime ( millisOfDay : Int ) {
if ( selected == null ) {
val day = when {
selected = today . withMillisOfDay ( millisOfDay )
selectedDay == MULTIPLE _DAYS -> MULTIPLE _DAYS
if ( selected !! . isBeforeNow ) {
selectedDay > 0 -> selectedDay
selected = selected !! . plusDays ( 1 )
today . withMillisOfDay ( millisOfDay ) . isAfterNow -> today . millis
}
else -> today . plusDays ( 1 ) . millis
} else {
selected = selected !! . withMillisOfDay ( millisOfDay )
}
}
returnDate ( selected!! . millis )
returnDate ( day = day , time = millisOfDay )
}
}
private fun returnDate ( dt : DateTime ? = selected ) = returnDate ( dt ?. millis ?: 0 )
private fun returnDate ( day : Long = selectedDay , time : Int = selectedTime ) {
selectedDay = day
private fun returnDate ( date : Long ? = selected ?. millis ) {
selectedTime = time
selected = if ( date == null || date <= 0 ) null else DateTime ( date )
if ( closeAutomatically ( ) ) {
if ( closeAutomatically ( ) ) {
sendSelected ( )
sendSelected ( )
} else {
} else {
@ -237,21 +256,37 @@ class DateTimePicker : BottomSheetDialogFragment() {
}
}
}
}
private val taskIds : LongArray
get ( ) = arguments ?. getLongArray ( EXTRA _TASKS ) ?: longArrayOf ( )
private fun sendSelected ( ) {
private fun sendSelected ( ) {
val taskId = arguments ?. getLong ( EXTRA _TASK ) ?: 0
if ( selectedDay != arguments ?. getLong ( EXTRA _DAY )
val dueDate = selected ?. millis ?: 0
|| selectedTime != arguments ?. getInt ( EXTRA _TIME ) ) {
if ( dueDate != arguments ?. getLong ( EXTRA _TIMESTAMP ) ) {
if ( taskIds . isEmpty ( ) ) {
if ( taskId > 0 ) {
val intent = Intent ( )
intent . putExtra ( EXTRA _TIMESTAMP , when {
selectedDay == NO _DAY -> 0
selectedTime == NO _TIME -> selectedDay
else -> selectedDay . toDateTime ( ) . withMillisOfDay ( selectedTime ) . millis
} )
targetFragment ?. onActivityResult ( targetRequestCode , RESULT _OK , intent )
} else {
lifecycleScope . launch ( NonCancellable ) {
lifecycleScope . launch ( NonCancellable ) {
taskIds . forEach { taskId ->
taskDao . fetch ( taskId ) ?. let {
taskDao . fetch ( taskId ) ?. let {
it . setDueDateAdjustingHideUntil ( dueDate )
it . setDueDateAdjustingHideUntil ( when {
selectedDay == MULTIPLE _DAYS ->
it . dueDate . toDateTime ( ) . withMillisOfDay ( selectedTime ) . millis
selectedDay == NO _DAY -> 0L
selectedTime == MULTIPLE _TIMES ->
selectedDay . toDateTime ( ) . withMillisOfDay ( it . dueDate . millisOfDay ( ) ) . millis
selectedTime == NO _TIME -> selectedDay
else -> selectedDay . toDateTime ( ) . withMillisOfDay ( selectedTime ) . millis
} )
taskDao . save ( it )
taskDao . save ( it )
}
}
}
}
} else {
}
val intent = Intent ( )
intent . putExtra ( EXTRA _TIMESTAMP , dueDate )
targetFragment ?. onActivityResult ( targetRequestCode , RESULT _OK , intent )
}
}
}
}
dismiss ( )
dismiss ( )
@ -268,7 +303,8 @@ class DateTimePicker : BottomSheetDialogFragment() {
override fun onSaveInstanceState ( outState : Bundle ) {
override fun onSaveInstanceState ( outState : Bundle ) {
super . onSaveInstanceState ( outState )
super . onSaveInstanceState ( outState )
outState . putSerializable ( EXTRA _SELECTED , selected ?. millis )
outState . putLong ( EXTRA _DAY , selectedDay )
outState . putInt ( EXTRA _TIME , selectedTime )
}
}
override fun onCreateDialog ( savedInstanceState : Bundle ? ) : Dialog {
override fun onCreateDialog ( savedInstanceState : Bundle ? ) : Dialog {