Error indicators for relative alarms

Use error colors if a relative start or end alarm is set without a start
or end date
pull/3486/head
Alex Baker 8 months ago
parent 5692f48e75
commit 03cfe259f0

@ -7,7 +7,6 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -68,7 +67,6 @@ class TaskEditFragment : Fragment() {
} }
val context = LocalContext.current val context = LocalContext.current
val keyboard = LocalSoftwareKeyboardController.current val keyboard = LocalSoftwareKeyboardController.current
val scope = rememberCoroutineScope()
TaskEditScreen( TaskEditScreen(
editViewModel = editViewModel, editViewModel = editViewModel,

@ -80,6 +80,8 @@ class ReminderControlSet : TaskEditControlFragment() {
viewModel.addAlarm(Alarm(time = timestamp, type = TYPE_DATE_TIME)) viewModel.addAlarm(Alarm(time = timestamp, type = TYPE_DATE_TIME))
} }
val viewState = viewModel.viewState.collectAsStateWithLifecycle().value val viewState = viewModel.viewState.collectAsStateWithLifecycle().value
val startDate = viewModel.startDate.collectAsStateWithLifecycle().value
val dueDate = viewModel.dueDate.collectAsStateWithLifecycle().value
val context = LocalContext.current val context = LocalContext.current
AlarmRow( AlarmRow(
alarms = viewState.alarms, alarms = viewState.alarms,
@ -94,6 +96,8 @@ class ReminderControlSet : TaskEditControlFragment() {
}, },
ringMode = ringMode, ringMode = ringMode,
addAlarm = viewModel::addAlarm, addAlarm = viewModel::addAlarm,
hasStartDate = startDate > 0,
hasDueDate = dueDate > 0,
openRingType = { openRingType = {
val modes = resources.getStringArray(R.array.reminder_ring_modes) val modes = resources.getStringArray(R.array.reminder_ring_modes)
val selectedIndex = when { val selectedIndex = when {

@ -5,12 +5,14 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.tasks.R import org.tasks.R
import org.tasks.compose.edit.StartDateRow import org.tasks.compose.edit.StartDateRow
import org.tasks.data.entity.Alarm
import org.tasks.dialogs.StartDatePicker import org.tasks.dialogs.StartDatePicker
import org.tasks.dialogs.StartDatePicker.Companion.EXTRA_DAY import org.tasks.dialogs.StartDatePicker.Companion.EXTRA_DAY
import org.tasks.dialogs.StartDatePicker.Companion.EXTRA_TIME import org.tasks.dialogs.StartDatePicker.Companion.EXTRA_TIME
@ -44,10 +46,14 @@ class StartDateControlSet : TaskEditControlFragment() {
val dueDate = viewModel.dueDate.collectAsStateWithLifecycle().value val dueDate = viewModel.dueDate.collectAsStateWithLifecycle().value
val selectedDay = vm.selectedDay.collectAsStateWithLifecycle().value val selectedDay = vm.selectedDay.collectAsStateWithLifecycle().value
val selectedTime = vm.selectedTime.collectAsStateWithLifecycle().value val selectedTime = vm.selectedTime.collectAsStateWithLifecycle().value
val viewState = viewModel.viewState.collectAsStateWithLifecycle().value
StartDateRow( StartDateRow(
startDate = viewModel.startDate.collectAsStateWithLifecycle().value, startDate = viewModel.startDate.collectAsStateWithLifecycle().value,
selectedDay = selectedDay, selectedDay = selectedDay,
selectedTime = selectedTime, selectedTime = selectedTime,
hasStartAlarm = remember (viewState.alarms) {
viewState.alarms.any { it.type == Alarm.TYPE_REL_START }
},
hasDueDate = dueDate > 0, hasDueDate = dueDate > 0,
printDate = { printDate = {
runBlocking { runBlocking {

@ -12,8 +12,8 @@ import androidx.compose.ui.unit.dp
@Composable @Composable
fun DisabledText( fun DisabledText(
modifier: Modifier = Modifier,
text: String, text: String,
modifier: Modifier = Modifier
) { ) {
Text( Text(
text = text, text = text,

@ -13,8 +13,8 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextDecoration import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
@ -42,6 +42,8 @@ fun AlarmRow(
fixNotificationPermissions: () -> Unit, fixNotificationPermissions: () -> Unit,
alarms: ImmutableSet<Alarm>, alarms: ImmutableSet<Alarm>,
ringMode: Int, ringMode: Int,
hasStartDate: Boolean,
hasDueDate: Boolean,
addAlarm: (Alarm) -> Unit, addAlarm: (Alarm) -> Unit,
deleteAlarm: (Alarm) -> Unit, deleteAlarm: (Alarm) -> Unit,
openRingType: () -> Unit, openRingType: () -> Unit,
@ -55,6 +57,8 @@ fun AlarmRow(
Alarms( Alarms(
alarms = alarms, alarms = alarms,
ringMode = ringMode, ringMode = ringMode,
hasStartDate = hasStartDate,
hasDueDate = hasDueDate,
replaceAlarm = { replaceAlarm = {
vm.setReplace(it) vm.setReplace(it)
vm.showAddAlarm(visible = true) vm.showAddAlarm(visible = true)
@ -74,12 +78,12 @@ fun AlarmRow(
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
Text( Text(
text = stringResource(id = R.string.enable_reminders), text = stringResource(id = R.string.enable_reminders),
color = colorResource(id = org.tasks.kmp.R.color.red_500), color = MaterialTheme.colorScheme.error,
) )
Text( Text(
text = stringResource(id = R.string.enable_reminders_description), text = stringResource(id = R.string.enable_reminders_description),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = colorResource(id = org.tasks.kmp.R.color.red_500), color = MaterialTheme.colorScheme.error,
) )
Spacer(modifier = Modifier.height(20.dp)) Spacer(modifier = Modifier.height(20.dp))
} }
@ -123,6 +127,8 @@ fun AlarmRow(
fun Alarms( fun Alarms(
alarms: ImmutableSet<Alarm>, alarms: ImmutableSet<Alarm>,
ringMode: Int, ringMode: Int,
hasStartDate: Boolean,
hasDueDate: Boolean,
replaceAlarm: (Alarm) -> Unit, replaceAlarm: (Alarm) -> Unit,
addAlarm: () -> Unit, addAlarm: () -> Unit,
deleteAlarm: (Alarm) -> Unit, deleteAlarm: (Alarm) -> Unit,
@ -133,6 +139,19 @@ fun Alarms(
alarms.forEach { alarm -> alarms.forEach { alarm ->
AlarmRow( AlarmRow(
text = AlarmToString(LocalContext.current).toString(alarm), 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) }, onClick = { replaceAlarm(alarm) },
remove = { deleteAlarm(alarm) } remove = { deleteAlarm(alarm) }
) )
@ -171,6 +190,7 @@ fun Alarms(
@Composable @Composable
private fun AlarmRow( private fun AlarmRow(
text: String, text: String,
color: Color,
onClick: () -> Unit, onClick: () -> Unit,
remove: () -> Unit, remove: () -> Unit,
) { ) {
@ -184,7 +204,7 @@ private fun AlarmRow(
modifier = Modifier modifier = Modifier
.padding(vertical = 12.dp) .padding(vertical = 12.dp)
.weight(weight = 1f), .weight(weight = 1f),
color = MaterialTheme.colorScheme.onSurface, color = color,
) )
ClearButton(onClick = remove) ClearButton(onClick = remove)
} }
@ -198,6 +218,8 @@ fun NoAlarms() {
AlarmRow( AlarmRow(
alarms = persistentSetOf(), alarms = persistentSetOf(),
ringMode = 0, ringMode = 0,
hasStartDate = true,
hasDueDate = true,
addAlarm = {}, addAlarm = {},
deleteAlarm = {}, deleteAlarm = {},
openRingType = {}, openRingType = {},
@ -216,6 +238,8 @@ fun PermissionDenied() {
AlarmRow( AlarmRow(
alarms = persistentSetOf(), alarms = persistentSetOf(),
ringMode = 0, ringMode = 0,
hasStartDate = true,
hasDueDate = true,
addAlarm = {}, addAlarm = {},
deleteAlarm = {}, deleteAlarm = {},
openRingType = {}, openRingType = {},

@ -2,18 +2,19 @@ package org.tasks.compose.edit
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.ContentAlpha
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier 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.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.tasks.R import org.tasks.R
import org.tasks.compose.DisabledText
import org.tasks.compose.TaskEditRow import org.tasks.compose.TaskEditRow
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.newDateTime
@ -24,13 +25,21 @@ import org.tasks.themes.TasksTheme
@Composable @Composable
fun DueDateRow( fun DueDateRow(
dueDate: Long, dueDate: Long,
hasDueDateAlarm: Boolean,
is24HourFormat: Boolean, is24HourFormat: Boolean,
alwaysDisplayFullDate: Boolean, alwaysDisplayFullDate: Boolean,
onClick: () -> Unit, onClick: () -> Unit,
) { ) {
val overdue = remember (dueDate) {
when {
Task.hasDueTime(dueDate) -> newDateTime(dueDate).isBeforeNow
dueDate > 0 -> newDateTime(dueDate).endOfDay().isBeforeNow
else -> false
}
}
DueDateRow( DueDateRow(
dueDate = if (dueDate == 0L) { dueDate = if (dueDate == 0L) {
null stringResource(id = R.string.no_due_date)
} else { } else {
runBlocking { runBlocking {
getRelativeDateTime( getRelativeDateTime(
@ -41,10 +50,11 @@ fun DueDateRow(
) )
} }
}, },
overdue = if (Task.hasDueTime(dueDate)) { color = when {
newDateTime(dueDate).isBeforeNow overdue -> MaterialTheme.colorScheme.error
} else { dueDate == 0L && hasDueDateAlarm -> MaterialTheme.colorScheme.error
newDateTime(dueDate).endOfDay().isBeforeNow dueDate == 0L -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled)
else -> MaterialTheme.colorScheme.onSurface
}, },
onClick = { onClick() }, onClick = { onClick() },
) )
@ -52,8 +62,8 @@ fun DueDateRow(
@Composable @Composable
private fun DueDateRow( private fun DueDateRow(
dueDate: String?, dueDate: String,
overdue: Boolean, color: Color,
onClick: () -> Unit, onClick: () -> Unit,
) { ) {
TaskEditRow( TaskEditRow(
@ -61,7 +71,7 @@ private fun DueDateRow(
content = { content = {
DueDate( DueDate(
dueDate = dueDate, dueDate = dueDate,
overdue = overdue, color = color,
) )
}, },
onClick = onClick, onClick = onClick,
@ -69,23 +79,15 @@ private fun DueDateRow(
} }
@Composable @Composable
fun DueDate(dueDate: String?, overdue: Boolean) { fun DueDate(
if (dueDate.isNullOrBlank()) { dueDate: String,
DisabledText( color: Color,
text = stringResource(id = R.string.no_due_date), ) {
modifier = Modifier.padding(top = 20.dp, bottom = 20.dp, end = 16.dp) Text(
) text = dueDate,
} else { color = color,
Text( modifier = Modifier.padding(top = 20.dp, bottom = 20.dp, end = 16.dp)
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)
)
}
} }
@ExperimentalComposeUiApi @ExperimentalComposeUiApi
@ -96,7 +98,7 @@ fun DueDatePreview() {
TasksTheme { TasksTheme {
DueDateRow( DueDateRow(
dueDate = "Today", dueDate = "Today",
overdue = false, color = MaterialTheme.colorScheme.onSurface,
) {} ) {}
} }
} }
@ -108,8 +110,8 @@ fun DueDatePreview() {
fun NoDueDatePreview() { fun NoDueDatePreview() {
TasksTheme { TasksTheme {
DueDateRow( DueDateRow(
dueDate = null, dueDate = stringResource(R.string.no_due_date),
overdue = false, color = MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled),
) {} ) {}
} }
} }

@ -9,7 +9,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -26,6 +25,7 @@ fun StartDateRow(
selectedDay: Long, selectedDay: Long,
selectedTime: Int, selectedTime: Int,
currentTime: Long = currentTimeMillis(), currentTime: Long = currentTimeMillis(),
hasStartAlarm: Boolean,
hasDueDate: Boolean, hasDueDate: Boolean,
printDate: () -> String, printDate: () -> String,
onClick: () -> Unit, onClick: () -> Unit,
@ -38,6 +38,7 @@ fun StartDateRow(
selectedDay = selectedDay, selectedDay = selectedDay,
selectedTime = selectedTime, selectedTime = selectedTime,
currentTime = currentTime, currentTime = currentTime,
hasStartAlarm = hasStartAlarm,
hasDueDate = hasDueDate, hasDueDate = hasDueDate,
printDate = printDate, printDate = printDate,
) )
@ -52,6 +53,7 @@ fun StartDate(
selectedDay: Long, selectedDay: Long,
selectedTime: Int, selectedTime: Int,
currentTime: Long, currentTime: Long,
hasStartAlarm: Boolean,
hasDueDate: Boolean, hasDueDate: Boolean,
printDate: () -> String, printDate: () -> String,
) { ) {
@ -66,9 +68,10 @@ fun StartDate(
else -> stringResource(id = R.string.no_start_date) else -> stringResource(id = R.string.no_start_date)
}, },
color = when { 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 == 0L -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled)
startDate < currentTime -> colorResource(id = R.color.overdue) startDate < currentTime -> MaterialTheme.colorScheme.error
else -> MaterialTheme.colorScheme.onSurface else -> MaterialTheme.colorScheme.onSurface
}, },
modifier = Modifier modifier = Modifier
@ -87,6 +90,7 @@ fun NoStartDate() {
selectedDay = StartDatePicker.NO_DAY, selectedDay = StartDatePicker.NO_DAY,
selectedTime = StartDatePicker.NO_TIME, selectedTime = StartDatePicker.NO_TIME,
currentTime = 1657080392000L, currentTime = 1657080392000L,
hasStartAlarm = true,
hasDueDate = false, hasDueDate = false,
printDate = { "" }, printDate = { "" },
onClick = {}, onClick = {},
@ -104,6 +108,7 @@ fun FutureStartDate() {
selectedDay = StartDatePicker.DUE_DATE, selectedDay = StartDatePicker.DUE_DATE,
selectedTime = StartDatePicker.NO_TIME, selectedTime = StartDatePicker.NO_TIME,
currentTime = 1657080392000L, currentTime = 1657080392000L,
hasStartAlarm = true,
hasDueDate = false, hasDueDate = false,
printDate = { "" }, printDate = { "" },
onClick = {}, onClick = {},
@ -121,6 +126,7 @@ fun PastStartDate() {
selectedDay = StartDatePicker.DUE_TIME, selectedDay = StartDatePicker.DUE_TIME,
selectedTime = StartDatePicker.NO_TIME, selectedTime = StartDatePicker.NO_TIME,
currentTime = 1657080392001L, currentTime = 1657080392001L,
hasStartAlarm = true,
hasDueDate = false, hasDueDate = false,
printDate = { "" }, printDate = { "" },
onClick = {}, onClick = {},

@ -29,6 +29,7 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -51,6 +52,7 @@ import org.tasks.R
import org.tasks.compose.BeastModeBanner import org.tasks.compose.BeastModeBanner
import org.tasks.compose.FilterSelectionActivity.Companion.EXTRA_FILTER import org.tasks.compose.FilterSelectionActivity.Companion.EXTRA_FILTER
import org.tasks.compose.FilterSelectionActivity.Companion.launch import org.tasks.compose.FilterSelectionActivity.Companion.launch
import org.tasks.data.entity.Alarm
import org.tasks.data.entity.UserActivity import org.tasks.data.entity.UserActivity
import org.tasks.dialogs.Linkify import org.tasks.dialogs.Linkify
import org.tasks.extensions.Context.findActivity import org.tasks.extensions.Context.findActivity
@ -217,6 +219,9 @@ fun TaskEditScreen(
TAG_DUE_DATE -> DueDateRow( TAG_DUE_DATE -> DueDateRow(
dueDate = editViewModel.dueDate.collectAsStateWithLifecycle().value, dueDate = editViewModel.dueDate.collectAsStateWithLifecycle().value,
hasDueDateAlarm = remember (viewState.alarms) {
viewState.alarms.any { it.type == Alarm.TYPE_REL_END }
},
is24HourFormat = context.is24HourFormat, is24HourFormat = context.is24HourFormat,
alwaysDisplayFullDate = viewState.alwaysDisplayFullDate, alwaysDisplayFullDate = viewState.alwaysDisplayFullDate,
onClick = onClickDueDate, onClick = onClickDueDate,

Loading…
Cancel
Save