mirror of https://github.com/tasks/tasks
Moving logic into composables, adding previews
parent
5c3af50c9d
commit
3e3de3c1d6
@ -0,0 +1,81 @@
|
||||
package org.tasks.compose.edit
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
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 com.google.android.material.composethemeadapter.MdcTheme
|
||||
import org.tasks.R
|
||||
import org.tasks.compose.DisabledText
|
||||
import org.tasks.compose.TaskEditRow
|
||||
|
||||
@Composable
|
||||
fun DueDateRow(
|
||||
dueDate: String?,
|
||||
overdue: Boolean,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TaskEditRow(
|
||||
iconRes = R.drawable.ic_outline_schedule_24px,
|
||||
content = {
|
||||
DueDate(
|
||||
dueDate = dueDate,
|
||||
overdue = overdue,
|
||||
)
|
||||
},
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
@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.colors.onSurface
|
||||
},
|
||||
modifier = Modifier.padding(top = 20.dp, bottom = 20.dp, end = 16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalComposeUiApi
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun DueDatePreview() {
|
||||
MdcTheme {
|
||||
DueDateRow(
|
||||
dueDate = "Today",
|
||||
overdue = false,
|
||||
) {}
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalComposeUiApi
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun NoDueDatePreview() {
|
||||
MdcTheme {
|
||||
DueDateRow(
|
||||
dueDate = null,
|
||||
overdue = false,
|
||||
) {}
|
||||
}
|
||||
}
|
@ -0,0 +1,123 @@
|
||||
package org.tasks.compose.edit
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.ContentAlpha
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Notifications
|
||||
import androidx.compose.material.icons.outlined.NotificationsOff
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.android.material.composethemeadapter.MdcTheme
|
||||
import org.tasks.R
|
||||
import org.tasks.compose.DisabledText
|
||||
import org.tasks.compose.TaskEditRow
|
||||
import org.tasks.data.Geofence
|
||||
import org.tasks.data.Location
|
||||
import org.tasks.data.Place
|
||||
|
||||
@Composable
|
||||
fun LocationRow(
|
||||
location: Location?,
|
||||
hasPermissions: Boolean,
|
||||
onClick: () -> Unit,
|
||||
openGeofenceOptions: () -> Unit,
|
||||
) {
|
||||
TaskEditRow(
|
||||
iconRes = R.drawable.ic_outline_place_24px,
|
||||
content = {
|
||||
if (location == null) {
|
||||
DisabledText(
|
||||
text = stringResource(id = R.string.add_location),
|
||||
modifier = Modifier.padding(vertical = 20.dp)
|
||||
)
|
||||
} else {
|
||||
Location(
|
||||
name = location.displayName,
|
||||
address = location.displayAddress,
|
||||
openGeofenceOptions = openGeofenceOptions,
|
||||
geofenceOn = hasPermissions && (location.isArrival || location.isDeparture)
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = onClick
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Location(
|
||||
name: String,
|
||||
address: String?,
|
||||
geofenceOn: Boolean,
|
||||
openGeofenceOptions: () -> Unit,
|
||||
) {
|
||||
Row {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = 20.dp)
|
||||
) {
|
||||
Text(text = name)
|
||||
address?.takeIf { it.isNotBlank() && it != name }?.let {
|
||||
Text(text = address)
|
||||
}
|
||||
}
|
||||
IconButton(
|
||||
onClick = openGeofenceOptions,
|
||||
modifier = Modifier.padding(top = 8.dp /* + 12dp from icon */)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (geofenceOn) {
|
||||
Icons.Outlined.Notifications
|
||||
} else {
|
||||
Icons.Outlined.NotificationsOff
|
||||
},
|
||||
contentDescription = null,
|
||||
modifier = Modifier.alpha(ContentAlpha.medium),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun NoLocation() {
|
||||
MdcTheme {
|
||||
LocationRow(
|
||||
location = null,
|
||||
hasPermissions = true,
|
||||
onClick = {},
|
||||
openGeofenceOptions = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun SampleLocation() {
|
||||
MdcTheme {
|
||||
LocationRow(
|
||||
location = Location(
|
||||
Geofence(),
|
||||
Place().apply {
|
||||
name = "Googleplex"
|
||||
address = "1600 Amphitheatre Pkwy, Mountain View, CA 94043"
|
||||
},
|
||||
),
|
||||
hasPermissions = true,
|
||||
onClick = {},
|
||||
openGeofenceOptions = {},
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package org.tasks.compose.edit
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.RadioButton
|
||||
import androidx.compose.material.RadioButtonDefaults
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.android.material.composethemeadapter.MdcTheme
|
||||
import com.todoroo.astrid.data.Task
|
||||
import org.tasks.R
|
||||
import org.tasks.compose.TaskEditRow
|
||||
|
||||
@Composable
|
||||
fun PriorityRow(
|
||||
priority: Int,
|
||||
onChangePriority: (Int) -> Unit,
|
||||
) {
|
||||
TaskEditRow(
|
||||
iconRes = R.drawable.ic_outline_flag_24px,
|
||||
content = {
|
||||
Priority(
|
||||
selected = priority,
|
||||
onClick = { onChangePriority(it) }
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Priority(
|
||||
selected: Int,
|
||||
onClick: (Int) -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.padding(
|
||||
top = dimensionResource(id = R.dimen.half_keyline_first),
|
||||
bottom = dimensionResource(id = R.dimen.half_keyline_first),
|
||||
end = 16.dp
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.TEA_importance_label),
|
||||
style = MaterialTheme.typography.body1,
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
for (i in Task.Priority.HIGH..Task.Priority.NONE) {
|
||||
PriorityButton(priority = i, selected = selected, onClick = onClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PriorityButton(
|
||||
@Task.Priority priority: Int,
|
||||
selected: Int,
|
||||
onClick: (Int) -> Unit,
|
||||
) {
|
||||
val color = when (priority) {
|
||||
in Int.MIN_VALUE..Task.Priority.HIGH -> colorResource(id = R.color.red_500)
|
||||
Task.Priority.MEDIUM -> colorResource(id = R.color.amber_500)
|
||||
Task.Priority.LOW -> colorResource(id = R.color.blue_500)
|
||||
else -> colorResource(R.color.grey_500)
|
||||
}
|
||||
RadioButton(
|
||||
selected = priority == selected,
|
||||
onClick = { onClick(priority) },
|
||||
colors = RadioButtonDefaults.colors(
|
||||
selectedColor = color,
|
||||
unselectedColor = color,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ExperimentalComposeUiApi
|
||||
@Preview(showBackground = true)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
fun PriorityPreview() {
|
||||
MdcTheme {
|
||||
PriorityRow(priority = Task.Priority.MEDIUM) {}
|
||||
}
|
||||
}
|
@ -0,0 +1,127 @@
|
||||
package org.tasks.compose.edit
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.android.material.composethemeadapter.MdcTheme
|
||||
import org.tasks.R
|
||||
import org.tasks.compose.DisabledText
|
||||
import org.tasks.compose.TaskEditRow
|
||||
|
||||
@Composable
|
||||
fun RepeatRow(
|
||||
recurrence: String?,
|
||||
repeatAfterCompletion: Boolean,
|
||||
onClick: () -> Unit,
|
||||
onRepeatFromChanged: (Boolean) -> Unit,
|
||||
) {
|
||||
TaskEditRow(
|
||||
iconRes = R.drawable.ic_outline_repeat_24px,
|
||||
content = {
|
||||
Repeat(
|
||||
recurrence = recurrence,
|
||||
repeatFromCompletion = repeatAfterCompletion,
|
||||
onRepeatFromChanged = onRepeatFromChanged,
|
||||
)
|
||||
},
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Repeat(
|
||||
recurrence: String?,
|
||||
repeatFromCompletion: Boolean,
|
||||
onRepeatFromChanged: (Boolean) -> Unit,
|
||||
) {
|
||||
Column {
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
if (recurrence.isNullOrBlank()) {
|
||||
DisabledText(text = stringResource(id = R.string.repeat_option_does_not_repeat))
|
||||
} else {
|
||||
Text(
|
||||
text = recurrence,
|
||||
modifier = Modifier.defaultMinSize(minHeight = 24.dp).padding(end = 16.dp).fillMaxWidth(),
|
||||
maxLines = Int.MAX_VALUE,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row {
|
||||
Text(text = stringResource(id = R.string.repeats_from))
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = if (repeatFromCompletion)
|
||||
R.string.repeat_type_completion
|
||||
else
|
||||
R.string.repeat_type_due
|
||||
),
|
||||
style = MaterialTheme.typography.body1.copy(
|
||||
textDecoration = TextDecoration.Underline,
|
||||
),
|
||||
modifier = Modifier.clickable { expanded = true }
|
||||
)
|
||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
expanded = false
|
||||
onRepeatFromChanged(false)
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.repeat_type_due))
|
||||
}
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
expanded = false
|
||||
onRepeatFromChanged(true)
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.repeat_type_completion))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalComposeUiApi
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun RepeatPreview() {
|
||||
MdcTheme {
|
||||
RepeatRow(
|
||||
recurrence = "Repeats weekly on Mon, Tue, Wed, Thu, Fri",
|
||||
repeatAfterCompletion = false,
|
||||
onClick = {},
|
||||
onRepeatFromChanged = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalComposeUiApi
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun NoRepeatPreview() {
|
||||
MdcTheme {
|
||||
RepeatRow(
|
||||
recurrence = null,
|
||||
repeatAfterCompletion = false,
|
||||
onClick = {},
|
||||
onRepeatFromChanged = {},
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
package org.tasks.compose.edit
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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 com.google.android.material.composethemeadapter.MdcTheme
|
||||
import com.todoroo.andlib.utility.DateUtilities
|
||||
import com.todoroo.astrid.ui.StartDateControlSet.Companion.getRelativeDateString
|
||||
import org.tasks.R
|
||||
import org.tasks.compose.TaskEditRow
|
||||
import org.tasks.dialogs.StartDatePicker
|
||||
|
||||
@Composable
|
||||
fun StartDateRow(
|
||||
startDate: Long,
|
||||
selectedDay: Long,
|
||||
selectedTime: Int,
|
||||
currentTime: Long = DateUtilities.now(),
|
||||
hasDueDate: Boolean,
|
||||
printDate: () -> String,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TaskEditRow(
|
||||
iconRes = R.drawable.ic_pending_actions_24px,
|
||||
content = {
|
||||
StartDate(
|
||||
startDate = startDate,
|
||||
selectedDay = selectedDay,
|
||||
selectedTime = selectedTime,
|
||||
currentTime = currentTime,
|
||||
hasDueDate = hasDueDate,
|
||||
printDate = printDate,
|
||||
)
|
||||
},
|
||||
onClick = onClick
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StartDate(
|
||||
startDate: Long,
|
||||
selectedDay: Long,
|
||||
selectedTime: Int,
|
||||
currentTime: Long,
|
||||
hasDueDate: Boolean,
|
||||
printDate: () -> String,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
Text(
|
||||
text = when (selectedDay) {
|
||||
StartDatePicker.DUE_DATE -> context.getRelativeDateString(R.string.due_date, selectedTime)
|
||||
StartDatePicker.DUE_TIME -> context.getString(R.string.due_time)
|
||||
StartDatePicker.DAY_BEFORE_DUE -> context.getRelativeDateString(R.string.day_before_due, selectedTime)
|
||||
StartDatePicker.WEEK_BEFORE_DUE -> context.getRelativeDateString(R.string.week_before_due, selectedTime)
|
||||
in 1..Long.MAX_VALUE -> printDate()
|
||||
else -> stringResource(id = R.string.no_start_date)
|
||||
},
|
||||
color = when {
|
||||
selectedDay < 0 && !hasDueDate -> colorResource(id = R.color.overdue)
|
||||
startDate == 0L -> MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.disabled)
|
||||
startDate < currentTime -> colorResource(id = R.color.overdue)
|
||||
else -> MaterialTheme.colors.onSurface
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(vertical = 20.dp)
|
||||
.height(24.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun NoStartDate() {
|
||||
MdcTheme {
|
||||
StartDateRow(
|
||||
startDate = 0L,
|
||||
selectedDay = StartDatePicker.NO_DAY,
|
||||
selectedTime = StartDatePicker.NO_TIME,
|
||||
currentTime = 1657080392000L,
|
||||
hasDueDate = false,
|
||||
printDate = { "" },
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun FutureStartDate() {
|
||||
MdcTheme {
|
||||
StartDateRow(
|
||||
startDate = 1657080392000L,
|
||||
selectedDay = StartDatePicker.DUE_DATE,
|
||||
selectedTime = StartDatePicker.NO_TIME,
|
||||
currentTime = 1657080392000L,
|
||||
hasDueDate = false,
|
||||
printDate = { "" },
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun PastStartDate() {
|
||||
MdcTheme {
|
||||
StartDateRow(
|
||||
startDate = 1657080392000L,
|
||||
selectedDay = StartDatePicker.DUE_TIME,
|
||||
selectedTime = StartDatePicker.NO_TIME,
|
||||
currentTime = 1657080392001L,
|
||||
hasDueDate = false,
|
||||
printDate = { "" },
|
||||
onClick = {},
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue