From 03cfe259f05849e37fe4badaaa853a32e2377574 Mon Sep 17 00:00:00 2001 From: Alex Baker Date: Fri, 4 Apr 2025 01:58:27 -0500 Subject: [PATCH] Error indicators for relative alarms Use error colors if a relative start or end alarm is set without a start or end date --- .../astrid/activity/TaskEditFragment.kt | 2 - .../todoroo/astrid/ui/ReminderControlSet.kt | 4 ++ .../todoroo/astrid/ui/StartDateControlSet.kt | 6 ++ .../java/org/tasks/compose/DisabledText.kt | 2 +- .../java/org/tasks/compose/edit/AlarmRow.kt | 32 ++++++++-- .../java/org/tasks/compose/edit/DueDateRow.kt | 62 ++++++++++--------- .../org/tasks/compose/edit/StartDateRow.kt | 12 +++- .../org/tasks/compose/edit/TaskEditScreen.kt | 5 ++ 8 files changed, 85 insertions(+), 40 deletions(-) diff --git a/app/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.kt b/app/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.kt index 268bc85c6..9181f6e10 100755 --- a/app/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.kt +++ b/app/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.kt @@ -7,7 +7,6 @@ import android.view.LayoutInflater import android.view.ViewGroup import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.fragment.app.Fragment @@ -68,7 +67,6 @@ class TaskEditFragment : Fragment() { } val context = LocalContext.current val keyboard = LocalSoftwareKeyboardController.current - val scope = rememberCoroutineScope() TaskEditScreen( editViewModel = editViewModel, diff --git a/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.kt b/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.kt index 46ccc0019..0021075fc 100644 --- a/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.kt +++ b/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.kt @@ -80,6 +80,8 @@ class ReminderControlSet : TaskEditControlFragment() { viewModel.addAlarm(Alarm(time = timestamp, type = TYPE_DATE_TIME)) } val viewState = viewModel.viewState.collectAsStateWithLifecycle().value + val startDate = viewModel.startDate.collectAsStateWithLifecycle().value + val dueDate = viewModel.dueDate.collectAsStateWithLifecycle().value val context = LocalContext.current AlarmRow( alarms = viewState.alarms, @@ -94,6 +96,8 @@ class ReminderControlSet : TaskEditControlFragment() { }, ringMode = ringMode, addAlarm = viewModel::addAlarm, + hasStartDate = startDate > 0, + hasDueDate = dueDate > 0, openRingType = { val modes = resources.getStringArray(R.array.reminder_ring_modes) val selectedIndex = when { diff --git a/app/src/main/java/com/todoroo/astrid/ui/StartDateControlSet.kt b/app/src/main/java/com/todoroo/astrid/ui/StartDateControlSet.kt index 087db0a91..725bbab49 100644 --- a/app/src/main/java/com/todoroo/astrid/ui/StartDateControlSet.kt +++ b/app/src/main/java/com/todoroo/astrid/ui/StartDateControlSet.kt @@ -5,12 +5,14 @@ import android.content.Context import android.content.Intent import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.fragment.app.viewModels import androidx.lifecycle.compose.collectAsStateWithLifecycle import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.runBlocking import org.tasks.R import org.tasks.compose.edit.StartDateRow +import org.tasks.data.entity.Alarm import org.tasks.dialogs.StartDatePicker import org.tasks.dialogs.StartDatePicker.Companion.EXTRA_DAY import org.tasks.dialogs.StartDatePicker.Companion.EXTRA_TIME @@ -44,10 +46,14 @@ class StartDateControlSet : TaskEditControlFragment() { val dueDate = viewModel.dueDate.collectAsStateWithLifecycle().value val selectedDay = vm.selectedDay.collectAsStateWithLifecycle().value val selectedTime = vm.selectedTime.collectAsStateWithLifecycle().value + val viewState = viewModel.viewState.collectAsStateWithLifecycle().value StartDateRow( startDate = viewModel.startDate.collectAsStateWithLifecycle().value, selectedDay = selectedDay, selectedTime = selectedTime, + hasStartAlarm = remember (viewState.alarms) { + viewState.alarms.any { it.type == Alarm.TYPE_REL_START } + }, hasDueDate = dueDate > 0, printDate = { runBlocking { diff --git a/app/src/main/java/org/tasks/compose/DisabledText.kt b/app/src/main/java/org/tasks/compose/DisabledText.kt index 4b45ac9be..9949ca6bd 100644 --- a/app/src/main/java/org/tasks/compose/DisabledText.kt +++ b/app/src/main/java/org/tasks/compose/DisabledText.kt @@ -12,8 +12,8 @@ import androidx.compose.ui.unit.dp @Composable fun DisabledText( + modifier: Modifier = Modifier, text: String, - modifier: Modifier = Modifier ) { Text( text = text, diff --git a/app/src/main/java/org/tasks/compose/edit/AlarmRow.kt b/app/src/main/java/org/tasks/compose/edit/AlarmRow.kt index 868fef5f0..cb868fc10 100644 --- a/app/src/main/java/org/tasks/compose/edit/AlarmRow.kt +++ b/app/src/main/java/org/tasks/compose/edit/AlarmRow.kt @@ -13,8 +13,8 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.tooling.preview.Preview @@ -42,6 +42,8 @@ fun AlarmRow( fixNotificationPermissions: () -> Unit, alarms: ImmutableSet, ringMode: Int, + hasStartDate: Boolean, + hasDueDate: Boolean, addAlarm: (Alarm) -> Unit, deleteAlarm: (Alarm) -> Unit, openRingType: () -> Unit, @@ -55,6 +57,8 @@ fun AlarmRow( Alarms( alarms = alarms, ringMode = ringMode, + hasStartDate = hasStartDate, + hasDueDate = hasDueDate, replaceAlarm = { vm.setReplace(it) vm.showAddAlarm(visible = true) @@ -74,12 +78,12 @@ fun AlarmRow( Spacer(modifier = Modifier.height(20.dp)) Text( text = stringResource(id = R.string.enable_reminders), - color = colorResource(id = org.tasks.kmp.R.color.red_500), + color = MaterialTheme.colorScheme.error, ) Text( text = stringResource(id = R.string.enable_reminders_description), style = MaterialTheme.typography.bodySmall, - color = colorResource(id = org.tasks.kmp.R.color.red_500), + color = MaterialTheme.colorScheme.error, ) Spacer(modifier = Modifier.height(20.dp)) } @@ -123,6 +127,8 @@ fun AlarmRow( fun Alarms( alarms: ImmutableSet, ringMode: Int, + hasStartDate: Boolean, + hasDueDate: Boolean, replaceAlarm: (Alarm) -> Unit, addAlarm: () -> Unit, deleteAlarm: (Alarm) -> Unit, @@ -133,6 +139,19 @@ fun Alarms( alarms.forEach { alarm -> AlarmRow( text = AlarmToString(LocalContext.current).toString(alarm), + color = when (alarm.type) { + Alarm.TYPE_REL_START -> if (hasStartDate) { + MaterialTheme.colorScheme.onSurface + } else { + MaterialTheme.colorScheme.error + } + Alarm.TYPE_REL_END -> if (hasDueDate) { + MaterialTheme.colorScheme.onSurface + } else { + MaterialTheme.colorScheme.error + } + else -> MaterialTheme.colorScheme.onSurface + }, onClick = { replaceAlarm(alarm) }, remove = { deleteAlarm(alarm) } ) @@ -171,6 +190,7 @@ fun Alarms( @Composable private fun AlarmRow( text: String, + color: Color, onClick: () -> Unit, remove: () -> Unit, ) { @@ -184,7 +204,7 @@ private fun AlarmRow( modifier = Modifier .padding(vertical = 12.dp) .weight(weight = 1f), - color = MaterialTheme.colorScheme.onSurface, + color = color, ) ClearButton(onClick = remove) } @@ -198,6 +218,8 @@ fun NoAlarms() { AlarmRow( alarms = persistentSetOf(), ringMode = 0, + hasStartDate = true, + hasDueDate = true, addAlarm = {}, deleteAlarm = {}, openRingType = {}, @@ -216,6 +238,8 @@ fun PermissionDenied() { AlarmRow( alarms = persistentSetOf(), ringMode = 0, + hasStartDate = true, + hasDueDate = true, addAlarm = {}, deleteAlarm = {}, openRingType = {}, diff --git a/app/src/main/java/org/tasks/compose/edit/DueDateRow.kt b/app/src/main/java/org/tasks/compose/edit/DueDateRow.kt index e99bb66fc..8509de8fe 100644 --- a/app/src/main/java/org/tasks/compose/edit/DueDateRow.kt +++ b/app/src/main/java/org/tasks/compose/edit/DueDateRow.kt @@ -2,18 +2,19 @@ package org.tasks.compose.edit import android.content.res.Configuration import androidx.compose.foundation.layout.padding +import androidx.compose.material.ContentAlpha import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.res.colorResource +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.coroutines.runBlocking import org.tasks.R -import org.tasks.compose.DisabledText import org.tasks.compose.TaskEditRow import org.tasks.data.entity.Task import org.tasks.date.DateTimeUtils.newDateTime @@ -24,13 +25,21 @@ import org.tasks.themes.TasksTheme @Composable fun DueDateRow( dueDate: Long, + hasDueDateAlarm: Boolean, is24HourFormat: Boolean, alwaysDisplayFullDate: Boolean, onClick: () -> Unit, ) { + val overdue = remember (dueDate) { + when { + Task.hasDueTime(dueDate) -> newDateTime(dueDate).isBeforeNow + dueDate > 0 -> newDateTime(dueDate).endOfDay().isBeforeNow + else -> false + } + } DueDateRow( dueDate = if (dueDate == 0L) { - null + stringResource(id = R.string.no_due_date) } else { runBlocking { getRelativeDateTime( @@ -41,10 +50,11 @@ fun DueDateRow( ) } }, - overdue = if (Task.hasDueTime(dueDate)) { - newDateTime(dueDate).isBeforeNow - } else { - newDateTime(dueDate).endOfDay().isBeforeNow + color = when { + overdue -> MaterialTheme.colorScheme.error + dueDate == 0L && hasDueDateAlarm -> MaterialTheme.colorScheme.error + dueDate == 0L -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled) + else -> MaterialTheme.colorScheme.onSurface }, onClick = { onClick() }, ) @@ -52,8 +62,8 @@ fun DueDateRow( @Composable private fun DueDateRow( - dueDate: String?, - overdue: Boolean, + dueDate: String, + color: Color, onClick: () -> Unit, ) { TaskEditRow( @@ -61,7 +71,7 @@ private fun DueDateRow( content = { DueDate( dueDate = dueDate, - overdue = overdue, + color = color, ) }, onClick = onClick, @@ -69,23 +79,15 @@ private fun DueDateRow( } @Composable -fun DueDate(dueDate: String?, overdue: Boolean) { - if (dueDate.isNullOrBlank()) { - DisabledText( - text = stringResource(id = R.string.no_due_date), - modifier = Modifier.padding(top = 20.dp, bottom = 20.dp, end = 16.dp) - ) - } else { - Text( - text = dueDate, - color = if (overdue) { - colorResource(id = R.color.overdue) - } else { - MaterialTheme.colorScheme.onSurface - }, - modifier = Modifier.padding(top = 20.dp, bottom = 20.dp, end = 16.dp) - ) - } +fun DueDate( + dueDate: String, + color: Color, +) { + Text( + text = dueDate, + color = color, + modifier = Modifier.padding(top = 20.dp, bottom = 20.dp, end = 16.dp) + ) } @ExperimentalComposeUiApi @@ -96,7 +98,7 @@ fun DueDatePreview() { TasksTheme { DueDateRow( dueDate = "Today", - overdue = false, + color = MaterialTheme.colorScheme.onSurface, ) {} } } @@ -108,8 +110,8 @@ fun DueDatePreview() { fun NoDueDatePreview() { TasksTheme { DueDateRow( - dueDate = null, - overdue = false, + dueDate = stringResource(R.string.no_due_date), + color = MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled), ) {} } } \ No newline at end of file diff --git a/app/src/main/java/org/tasks/compose/edit/StartDateRow.kt b/app/src/main/java/org/tasks/compose/edit/StartDateRow.kt index 4b6ab4565..3bf40a858 100644 --- a/app/src/main/java/org/tasks/compose/edit/StartDateRow.kt +++ b/app/src/main/java/org/tasks/compose/edit/StartDateRow.kt @@ -9,7 +9,6 @@ import androidx.compose.material3.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 @@ -26,6 +25,7 @@ fun StartDateRow( selectedDay: Long, selectedTime: Int, currentTime: Long = currentTimeMillis(), + hasStartAlarm: Boolean, hasDueDate: Boolean, printDate: () -> String, onClick: () -> Unit, @@ -38,6 +38,7 @@ fun StartDateRow( selectedDay = selectedDay, selectedTime = selectedTime, currentTime = currentTime, + hasStartAlarm = hasStartAlarm, hasDueDate = hasDueDate, printDate = printDate, ) @@ -52,6 +53,7 @@ fun StartDate( selectedDay: Long, selectedTime: Int, currentTime: Long, + hasStartAlarm: Boolean, hasDueDate: Boolean, printDate: () -> String, ) { @@ -66,9 +68,10 @@ fun StartDate( else -> stringResource(id = R.string.no_start_date) }, color = when { - selectedDay < 0 && !hasDueDate -> colorResource(id = R.color.overdue) + selectedDay < 0 && !hasDueDate -> MaterialTheme.colorScheme.error + startDate == 0L && hasStartAlarm -> MaterialTheme.colorScheme.error startDate == 0L -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled) - startDate < currentTime -> colorResource(id = R.color.overdue) + startDate < currentTime -> MaterialTheme.colorScheme.error else -> MaterialTheme.colorScheme.onSurface }, modifier = Modifier @@ -87,6 +90,7 @@ fun NoStartDate() { selectedDay = StartDatePicker.NO_DAY, selectedTime = StartDatePicker.NO_TIME, currentTime = 1657080392000L, + hasStartAlarm = true, hasDueDate = false, printDate = { "" }, onClick = {}, @@ -104,6 +108,7 @@ fun FutureStartDate() { selectedDay = StartDatePicker.DUE_DATE, selectedTime = StartDatePicker.NO_TIME, currentTime = 1657080392000L, + hasStartAlarm = true, hasDueDate = false, printDate = { "" }, onClick = {}, @@ -121,6 +126,7 @@ fun PastStartDate() { selectedDay = StartDatePicker.DUE_TIME, selectedTime = StartDatePicker.NO_TIME, currentTime = 1657080392001L, + hasStartAlarm = true, hasDueDate = false, printDate = { "" }, onClick = {}, diff --git a/app/src/main/java/org/tasks/compose/edit/TaskEditScreen.kt b/app/src/main/java/org/tasks/compose/edit/TaskEditScreen.kt index 9817676c1..d00c15887 100644 --- a/app/src/main/java/org/tasks/compose/edit/TaskEditScreen.kt +++ b/app/src/main/java/org/tasks/compose/edit/TaskEditScreen.kt @@ -29,6 +29,7 @@ import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext @@ -51,6 +52,7 @@ import org.tasks.R import org.tasks.compose.BeastModeBanner import org.tasks.compose.FilterSelectionActivity.Companion.EXTRA_FILTER import org.tasks.compose.FilterSelectionActivity.Companion.launch +import org.tasks.data.entity.Alarm import org.tasks.data.entity.UserActivity import org.tasks.dialogs.Linkify import org.tasks.extensions.Context.findActivity @@ -217,6 +219,9 @@ fun TaskEditScreen( TAG_DUE_DATE -> DueDateRow( dueDate = editViewModel.dueDate.collectAsStateWithLifecycle().value, + hasDueDateAlarm = remember (viewState.alarms) { + viewState.alarms.any { it.type == Alarm.TYPE_REL_END } + }, is24HourFormat = context.is24HourFormat, alwaysDisplayFullDate = viewState.alwaysDisplayFullDate, onClick = onClickDueDate,