Convert StartDateControlSet to compose

pull/1924/head
Alex Baker 3 years ago
parent 91cb5a158b
commit 08069d0a3d

@ -21,7 +21,12 @@ class ReminderTests : BaseTaskEditViewModelTest() {
task.defaultReminders(Task.NOTIFY_AT_START) task.defaultReminders(Task.NOTIFY_AT_START)
setup(task) setup(task)
viewModel.hideUntil = Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME, currentTimeMillis()) viewModel.setStartDate(
Task.createDueDate(
Task.URGENCY_SPECIFIC_DAY_TIME,
currentTimeMillis()
)
)
save() save()

@ -1,19 +1,32 @@
package com.todoroo.astrid.ui package com.todoroo.astrid.ui
import android.app.Activity import android.app.Activity
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.view.ViewGroup import androidx.compose.foundation.layout.padding
import android.widget.TextView import androidx.compose.material.ContentAlpha
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.andlib.utility.DateUtilities.getTimeString
import com.todoroo.andlib.utility.DateUtilities.now import com.todoroo.andlib.utility.DateUtilities.now
import com.todoroo.astrid.data.Task import com.todoroo.astrid.ui.StartDateControlSet.Companion.getRelativeDateString
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R import org.tasks.R
import org.tasks.databinding.ControlSetHideBinding import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.dialogs.StartDatePicker import org.tasks.dialogs.StartDatePicker
import org.tasks.dialogs.StartDatePicker.Companion.DAY_BEFORE_DUE import org.tasks.dialogs.StartDatePicker.Companion.DAY_BEFORE_DUE
import org.tasks.dialogs.StartDatePicker.Companion.DUE_DATE import org.tasks.dialogs.StartDatePicker.Companion.DUE_DATE
@ -24,35 +37,26 @@ import org.tasks.dialogs.StartDatePicker.Companion.NO_DAY
import org.tasks.dialogs.StartDatePicker.Companion.NO_TIME import org.tasks.dialogs.StartDatePicker.Companion.NO_TIME
import org.tasks.dialogs.StartDatePicker.Companion.WEEK_BEFORE_DUE import org.tasks.dialogs.StartDatePicker.Companion.WEEK_BEFORE_DUE
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils.millisOfDay import org.tasks.ui.TaskEditControlComposeFragment
import org.tasks.time.DateTimeUtils.startOfDay
import org.tasks.ui.TaskEditControlFragment
import java.time.format.FormatStyle import java.time.format.FormatStyle
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class StartDateControlSet : TaskEditControlFragment() { class StartDateControlSet : TaskEditControlComposeFragment() {
@Inject lateinit var activity: Activity
@Inject lateinit var preferences: Preferences @Inject lateinit var preferences: Preferences
@Inject lateinit var locale: Locale @Inject lateinit var locale: Locale
private lateinit var startDate: TextView private val vm: StartDateViewModel by viewModels()
private val dueDateTime
get() = viewModel.dueDate.value
private var selectedDay = NO_DAY
private var selectedTime = NO_TIME
override fun onRowClick() { override fun onRowClick() {
val fragmentManager = parentFragmentManager val fragmentManager = parentFragmentManager
if (fragmentManager.findFragmentByTag(FRAG_TAG_DATE_PICKER) == null) { if (fragmentManager.findFragmentByTag(FRAG_TAG_DATE_PICKER) == null) {
StartDatePicker.newDateTimePicker( StartDatePicker.newDateTimePicker(
this, this,
REQUEST_HIDE_UNTIL, REQUEST_START_DATE,
selectedDay, vm.selectedDay.value,
selectedTime, vm.selectedTime.value,
preferences.getBoolean(R.string.p_auto_dismiss_datetime_edit_screen, false)) preferences.getBoolean(R.string.p_auto_dismiss_datetime_edit_screen, false))
.show(fragmentManager, FRAG_TAG_DATE_PICKER) .show(fragmentManager, FRAG_TAG_DATE_PICKER)
} }
@ -60,50 +64,24 @@ class StartDateControlSet : TaskEditControlFragment() {
override fun createView(savedInstanceState: Bundle?) { override fun createView(savedInstanceState: Bundle?) {
if (savedInstanceState == null) { if (savedInstanceState == null) {
val dueDay = dueDateTime.startOfDay() vm.init(viewModel.dueDate.value, viewModel.startDate.value, viewModel.isNew)
val dueTime = dueDateTime.millisOfDay()
val hideUntil = viewModel.hideUntil?.takeIf { it > 0 }?.toDateTime()
if (hideUntil == null) {
if (viewModel.isNew) {
when (preferences.getIntegerFromString(R.string.p_default_hideUntil_key, Task.HIDE_UNTIL_NONE)) {
Task.HIDE_UNTIL_DUE -> selectedDay = DUE_DATE
Task.HIDE_UNTIL_DUE_TIME -> selectedDay = DUE_TIME
Task.HIDE_UNTIL_DAY_BEFORE -> selectedDay = DAY_BEFORE_DUE
Task.HIDE_UNTIL_WEEK_BEFORE -> selectedDay = WEEK_BEFORE_DUE
}
}
} else {
selectedDay = hideUntil.startOfDay().millis
selectedTime = hideUntil.millisOfDay
selectedDay = when (selectedDay) {
dueDay -> if (selectedTime == dueTime) {
selectedTime = NO_TIME
DUE_TIME
} else {
DUE_DATE
}
dueDay.toDateTime().minusDays(1).millis ->
DAY_BEFORE_DUE
dueDay.toDateTime().minusDays(7).millis ->
WEEK_BEFORE_DUE
else -> selectedDay
}
}
} else {
selectedDay = savedInstanceState.getLong(EXTRA_DAY)
selectedTime = savedInstanceState.getInt(EXTRA_TIME)
} }
lifecycleScope.launchWhenResumed { lifecycleScope.launchWhenResumed {
viewModel.dueDate.collect { viewModel.dueDate.collect {
applySelectionToHideUntil() applySelected()
} }
} }
} }
override fun bind(parent: ViewGroup?) = @Composable
ControlSetHideBinding.inflate(layoutInflater, parent, true).let { override fun Body() {
startDate = it.startDate StartDate(
it.root startDate = viewModel.startDate.collectAsStateLifecycleAware().value,
selectedDay = vm.selectedDay.collectAsStateLifecycleAware().value,
selectedTime = vm.selectedTime.collectAsStateLifecycleAware().value,
displayFullDate = preferences.alwaysDisplayFullDate,
locale = locale,
)
} }
override val icon = R.drawable.ic_pending_actions_24px override val icon = R.drawable.ic_pending_actions_24px
@ -113,67 +91,113 @@ class StartDateControlSet : TaskEditControlFragment() {
override val isClickable = true override val isClickable = true
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_HIDE_UNTIL) { if (requestCode == REQUEST_START_DATE) {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
selectedDay = data?.getLongExtra(EXTRA_DAY, 0L) ?: NO_DAY vm.setSelected(
selectedDay = data?.getLongExtra(EXTRA_DAY, 0L) ?: NO_DAY,
selectedTime = data?.getIntExtra(EXTRA_TIME, 0) ?: NO_TIME selectedTime = data?.getIntExtra(EXTRA_TIME, 0) ?: NO_TIME
applySelectionToHideUntil() )
applySelected()
} }
} else { } else {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
} }
} }
override fun onSaveInstanceState(outState: Bundle) { private fun applySelected() {
super.onSaveInstanceState(outState) viewModel.setStartDate(vm.getSelectedValue(viewModel.dueDate.value))
outState.putLong(EXTRA_DAY, selectedDay)
outState.putInt(EXTRA_TIME, selectedTime)
} }
private fun getRelativeDateString(resId: Int) = if (selectedTime == NO_TIME) { companion object {
const val TAG = R.string.TEA_ctrl_hide_until_pref
private const val REQUEST_START_DATE = 11011
private const val FRAG_TAG_DATE_PICKER = "frag_tag_date_picker"
internal fun Context.getRelativeDateString(resId: Int, time: Int) =
if (time == NO_TIME) {
getString(resId) getString(resId)
} else { } else {
"${getString(resId)} ${DateUtilities.getTimeString(context, newDateTime().withMillisOfDay(selectedTime))}" "${getString(resId)} ${getTimeString(this, newDateTime().withMillisOfDay(time))}"
}
} }
}
private fun refreshDisplayView() { @Composable
startDate.text = when (selectedDay) { fun StartDate(
DUE_DATE -> getRelativeDateString(R.string.due_date) startDate: Long,
DUE_TIME -> getString(R.string.due_time) selectedDay: Long,
DAY_BEFORE_DUE -> getRelativeDateString(R.string.day_before_due) selectedTime: Int,
WEEK_BEFORE_DUE -> getRelativeDateString(R.string.week_before_due) displayFullDate: Boolean,
locale: Locale = Locale.getDefault(),
currentTime: Long = now(),
) {
val context = LocalContext.current
Text(
text = when (selectedDay) {
DUE_DATE -> context.getRelativeDateString(R.string.due_date, selectedTime)
DUE_TIME -> context.getString(R.string.due_time)
DAY_BEFORE_DUE -> context.getRelativeDateString(R.string.day_before_due, selectedTime)
WEEK_BEFORE_DUE -> context.getRelativeDateString(R.string.week_before_due, selectedTime)
in 1..Long.MAX_VALUE -> DateUtilities.getRelativeDateTime( in 1..Long.MAX_VALUE -> DateUtilities.getRelativeDateTime(
activity, LocalContext.current,
selectedDay + selectedTime, selectedDay + selectedTime,
locale, locale,
FormatStyle.FULL, FormatStyle.FULL,
preferences.alwaysDisplayFullDate, displayFullDate,
false false
) )
else -> null else -> stringResource(id = R.string.no_start_date)
} },
val started = viewModel.hideUntil?.takeIf { it > 0 }?.let { it < now() } ?: false color = when {
startDate.setTextColor( startDate == 0L -> MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
activity.getColor(if (started) R.color.overdue else R.color.text_primary) startDate < currentTime -> colorResource(id = R.color.overdue)
else -> MaterialTheme.colors.onSurface
},
modifier = Modifier.padding(vertical = 20.dp),
) )
} }
private fun applySelectionToHideUntil() { @Preview(showBackground = true)
val due = dueDateTime.takeIf { it > 0 }?.toDateTime() @Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
val millisOfDay = selectedTime @Composable
viewModel.hideUntil = when (selectedDay) { fun NoStartDate() {
DUE_DATE -> due?.withMillisOfDay(millisOfDay)?.millis ?: 0 MdcTheme {
DUE_TIME -> due?.millis ?: 0 StartDate(
DAY_BEFORE_DUE -> due?.minusDays(1)?.withMillisOfDay(millisOfDay)?.millis ?: 0 startDate = 0L,
WEEK_BEFORE_DUE -> due?.minusDays(7)?.withMillisOfDay(millisOfDay)?.millis ?: 0 selectedDay = NO_DAY,
else -> selectedDay + selectedTime selectedTime = NO_TIME,
displayFullDate = false,
currentTime = 1657080392000L
)
} }
refreshDisplayView() }
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun FutureStartDate() {
MdcTheme {
StartDate(
startDate = 1657080392000L,
selectedDay = DUE_DATE,
selectedTime = NO_TIME,
displayFullDate = false,
currentTime = 1657080392000L
)
} }
}
companion object { @Preview(showBackground = true)
const val TAG = R.string.TEA_ctrl_hide_until_pref @Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
private const val REQUEST_HIDE_UNTIL = 11011 @Composable
private const val FRAG_TAG_DATE_PICKER = "frag_tag_date_picker" fun PastStartDate() {
MdcTheme {
StartDate(
startDate = 1657080392000L,
selectedDay = DUE_TIME,
selectedTime = NO_TIME,
displayFullDate = false,
currentTime = 1657080392001L
)
} }
} }

@ -0,0 +1,81 @@
package com.todoroo.astrid.ui
import androidx.lifecycle.ViewModel
import com.todoroo.astrid.data.Task
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import org.tasks.R
import org.tasks.date.DateTimeUtils.toDateTime
import org.tasks.dialogs.StartDatePicker
import org.tasks.dialogs.StartDatePicker.Companion.DAY_BEFORE_DUE
import org.tasks.dialogs.StartDatePicker.Companion.DUE_DATE
import org.tasks.dialogs.StartDatePicker.Companion.DUE_TIME
import org.tasks.dialogs.StartDatePicker.Companion.WEEK_BEFORE_DUE
import org.tasks.preferences.Preferences
import org.tasks.time.DateTimeUtils.millisOfDay
import org.tasks.time.DateTimeUtils.startOfDay
import javax.inject.Inject
@HiltViewModel
class StartDateViewModel @Inject constructor(
private val preferences: Preferences
) : ViewModel() {
private val _selectedDay = MutableStateFlow(StartDatePicker.NO_DAY)
val selectedDay: StateFlow<Long>
get() = _selectedDay.asStateFlow()
private val _selectedTime = MutableStateFlow(StartDatePicker.NO_TIME)
val selectedTime: StateFlow<Int>
get() = _selectedTime.asStateFlow()
fun init(dueDate: Long, startDate: Long, isNew: Boolean) {
val dueDay = dueDate.startOfDay()
val dueTime = dueDate.millisOfDay()
val hideUntil = startDate.takeIf { it > 0 }?.toDateTime()
if (hideUntil == null) {
if (isNew) {
_selectedDay.value = when (preferences.getIntegerFromString(R.string.p_default_hideUntil_key, Task.HIDE_UNTIL_NONE)) {
Task.HIDE_UNTIL_DUE -> DUE_DATE
Task.HIDE_UNTIL_DUE_TIME -> DUE_TIME
Task.HIDE_UNTIL_DAY_BEFORE -> DAY_BEFORE_DUE
Task.HIDE_UNTIL_WEEK_BEFORE -> WEEK_BEFORE_DUE
else -> 0L
}
}
} else {
_selectedDay.value = hideUntil.startOfDay().millis
_selectedTime.value = hideUntil.millisOfDay
_selectedDay.value = when (_selectedDay.value) {
dueDay -> if (_selectedTime.value == dueTime) {
_selectedTime.value = StartDatePicker.NO_TIME
DUE_TIME
} else {
DUE_DATE
}
dueDay.toDateTime().minusDays(1).millis ->
DAY_BEFORE_DUE
dueDay.toDateTime().minusDays(7).millis ->
WEEK_BEFORE_DUE
else -> _selectedDay.value
}
}
}
fun setSelected(selectedDay: Long, selectedTime: Int) {
_selectedDay.value = selectedDay
_selectedTime.value = selectedTime
}
fun getSelectedValue(dueDate: Long): Long {
val due = dueDate.takeIf { it > 0 }?.toDateTime()
return when (selectedDay.value) {
DUE_DATE -> due?.withMillisOfDay(selectedTime.value)?.millis ?: 0
DUE_TIME -> due?.millis ?: 0
DAY_BEFORE_DUE -> due?.minusDays(1)?.withMillisOfDay(selectedTime.value)?.millis ?: 0
WEEK_BEFORE_DUE -> due?.minusDays(7)?.withMillisOfDay(selectedTime.value)?.millis ?: 0
else -> selectedDay.value + selectedTime.value
}
}
}

@ -87,6 +87,7 @@ class TaskEditViewModel @Inject constructor(
) { ) {
this.task = task this.task = task
dueDate.value = task.dueDate dueDate.value = task.dueDate
startDate.value = task.hideUntil
isNew = task.isNew isNew = task.isNew
originalList = list originalList = list
selectedList.value = list selectedList.value = list
@ -154,11 +155,10 @@ class TaskEditViewModel @Inject constructor(
var description: String? = null var description: String? = null
get() = field ?: task.notes.stripCarriageReturns() get() = field ?: task.notes.stripCarriageReturns()
var hideUntil: Long? = null val startDate = MutableStateFlow(0L)
get() = field ?: task.hideUntil
set(value) { fun setStartDate(value: Long) {
field = when { startDate.value = when {
value == null -> null
value == 0L -> 0 value == 0L -> 0
hasDueTime(value) -> hasDueTime(value) ->
value.toDateTime().withSecondOfMinute(1).withMillisOfSecond(0).millis value.toDateTime().withSecondOfMinute(1).withMillisOfSecond(0).millis
@ -294,7 +294,7 @@ class TaskEditViewModel @Inject constructor(
} else { } else {
task.notes != description task.notes != description
} || } ||
task.hideUntil != hideUntil || task.hideUntil != startDate.value ||
if (task.recurrence.isNullOrBlank()) { if (task.recurrence.isNullOrBlank()) {
!recurrence.isNullOrBlank() !recurrence.isNullOrBlank()
} else { } else {
@ -335,7 +335,7 @@ class TaskEditViewModel @Inject constructor(
task.dueDate = dueDate.value task.dueDate = dueDate.value
task.priority = priority.value task.priority = priority.value
task.notes = description task.notes = description
task.hideUntil = hideUntil!! task.hideUntil = startDate.value
task.recurrence = recurrence task.recurrence = recurrence
task.repeatUntil = repeatUntil!! task.repeatUntil = repeatUntil!!
task.elapsedSeconds = elapsedSeconds!! task.elapsedSeconds = elapsedSeconds!!

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/start_date"
style="@style/TaskEditTextPrimary"
android:hint="@string/no_start_date"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart" />
Loading…
Cancel
Save