mirror of https://github.com/tasks/tasks
Move more composable logic
parent
e7b6c96576
commit
00c80337de
@ -0,0 +1,103 @@
|
||||
package org.tasks.compose.edit
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.Delete
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
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.TaskAttachment
|
||||
|
||||
@Composable
|
||||
fun AttachmentRow(
|
||||
attachments: List<TaskAttachment>,
|
||||
openAttachment: (TaskAttachment) -> Unit,
|
||||
deleteAttachment: (TaskAttachment) -> Unit,
|
||||
addAttachment: () -> Unit,
|
||||
) {
|
||||
TaskEditRow(
|
||||
iconRes = R.drawable.ic_outline_attachment_24px,
|
||||
content = {
|
||||
Column(
|
||||
modifier = Modifier.padding(top = if (attachments.isEmpty()) 0.dp else 8.dp),
|
||||
) {
|
||||
attachments.forEach {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable { openAttachment(it) },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = it.name!!,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
IconButton(onClick = { deleteAttachment(it) }) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = stringResource(
|
||||
id = R.string.delete
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
DisabledText(
|
||||
text = stringResource(id = R.string.add_attachment),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { addAttachment() }
|
||||
.padding(
|
||||
top = if (attachments.isEmpty()) 20.dp else 8.dp,
|
||||
bottom = 20.dp,
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun NoAttachments() {
|
||||
MdcTheme {
|
||||
AttachmentRow(
|
||||
attachments = emptyList(),
|
||||
openAttachment = {},
|
||||
deleteAttachment = {},
|
||||
addAttachment = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun AttachmentPreview() {
|
||||
MdcTheme {
|
||||
AttachmentRow(
|
||||
attachments = listOf(
|
||||
TaskAttachment("", "file://attachment.txt".toUri(), "attachment.txt")
|
||||
),
|
||||
openAttachment = {},
|
||||
deleteAttachment = {},
|
||||
addAttachment = {},
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package org.tasks.compose.edit
|
||||
|
||||
import android.content.res.Configuration
|
||||
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.Delete
|
||||
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
|
||||
|
||||
@Composable
|
||||
fun CalendarRow(
|
||||
eventUri: String?,
|
||||
selectedCalendar: String?,
|
||||
onClick: () -> Unit,
|
||||
clear: () -> Unit,
|
||||
) {
|
||||
TaskEditRow(
|
||||
iconRes = R.drawable.ic_outline_event_24px,
|
||||
content = {
|
||||
if (eventUri?.isNotBlank() == true) {
|
||||
Row {
|
||||
Text(
|
||||
text = stringResource(id = R.string.gcal_TEA_showCalendar_label),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = 20.dp)
|
||||
)
|
||||
IconButton(
|
||||
onClick = { clear() },
|
||||
Modifier.padding(vertical = 8.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Delete,
|
||||
contentDescription = stringResource(id = R.string.delete),
|
||||
modifier = Modifier.alpha(ContentAlpha.medium),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if (selectedCalendar?.isNotBlank() == true) {
|
||||
Text(
|
||||
text = selectedCalendar,
|
||||
modifier = Modifier.padding(vertical = 20.dp),
|
||||
)
|
||||
} else {
|
||||
DisabledText(
|
||||
text = stringResource(id = R.string.dont_add_to_calendar),
|
||||
modifier = Modifier.padding(vertical = 20.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = onClick
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun NoCalendar() {
|
||||
MdcTheme {
|
||||
CalendarRow(eventUri = null, selectedCalendar = null, onClick = {}, clear = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun NewCalendar() {
|
||||
MdcTheme {
|
||||
CalendarRow(eventUri = null, selectedCalendar = "Personal", onClick = {}, clear = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun ExistingCalendar() {
|
||||
MdcTheme {
|
||||
CalendarRow(eventUri = "abcd", selectedCalendar = null, onClick = {}, clear = {})
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package org.tasks.compose.edit
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.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.TaskEditRow
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@Composable
|
||||
fun InfoRow(
|
||||
creationDate: Long?,
|
||||
modificationDate: Long?,
|
||||
completionDate: Long?,
|
||||
locale: Locale = Locale.getDefault(),
|
||||
) {
|
||||
TaskEditRow(
|
||||
iconRes = R.drawable.ic_outline_info_24px,
|
||||
content = {
|
||||
Column(modifier = Modifier.padding(vertical = 20.dp)) {
|
||||
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm", locale)
|
||||
creationDate?.let {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.sort_created_group,
|
||||
formatter.format(it)
|
||||
)
|
||||
)
|
||||
}
|
||||
modificationDate?.let {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.sort_modified_group,
|
||||
formatter.format(it)
|
||||
)
|
||||
)
|
||||
}
|
||||
completionDate?.takeIf { it > 0 }?.let {
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.sort_completion_group,
|
||||
formatter.format(it)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ExperimentalComposeUiApi
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun InfoPreview() {
|
||||
MdcTheme {
|
||||
InfoRow(
|
||||
creationDate = 1658727180000,
|
||||
modificationDate = 1658813557000,
|
||||
completionDate = 1658813557000,
|
||||
locale = Locale.US,
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
package org.tasks.compose.edit
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.text.format.DateUtils
|
||||
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.Pause
|
||||
import androidx.compose.material.icons.outlined.PlayArrow
|
||||
import androidx.compose.runtime.*
|
||||
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 kotlinx.coroutines.delay
|
||||
import org.tasks.R
|
||||
import org.tasks.compose.DisabledText
|
||||
import org.tasks.compose.TaskEditRow
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
@Composable
|
||||
fun TimerRow(
|
||||
started: Long,
|
||||
estimated: Int,
|
||||
elapsed: Int,
|
||||
timerClicked: () -> Unit,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
TaskEditRow(
|
||||
iconRes = R.drawable.ic_outline_timer_24px,
|
||||
content = {
|
||||
var now by remember { mutableStateOf(System.currentTimeMillis()) }
|
||||
|
||||
val newElapsed = if (started > 0) (now - started) / 1000L else 0
|
||||
val estimatedString = estimated
|
||||
.takeIf { it > 0 }
|
||||
?.let {
|
||||
stringResource(
|
||||
id = R.string.TEA_timer_est,
|
||||
DateUtils.formatElapsedTime(it.toLong())
|
||||
)
|
||||
}
|
||||
val elapsedString =
|
||||
(newElapsed + elapsed)
|
||||
.takeIf { it > 0 }
|
||||
?.let {
|
||||
stringResource(
|
||||
id = R.string.TEA_timer_elap,
|
||||
DateUtils.formatElapsedTime(it)
|
||||
)
|
||||
}
|
||||
val text = when {
|
||||
estimatedString != null && elapsedString != null -> "$estimatedString, $elapsedString"
|
||||
estimatedString != null -> estimatedString
|
||||
elapsedString != null -> elapsedString
|
||||
else -> null
|
||||
}
|
||||
Row {
|
||||
if (text == null) {
|
||||
DisabledText(
|
||||
text = stringResource(id = R.string.TEA_timer_controls),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = 20.dp),
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(vertical = 20.dp),
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = {
|
||||
now = System.currentTimeMillis()
|
||||
timerClicked()
|
||||
},
|
||||
modifier = Modifier.padding(vertical = 8.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (started > 0) {
|
||||
Icons.Outlined.Pause
|
||||
} else {
|
||||
Icons.Outlined.PlayArrow
|
||||
},
|
||||
modifier = Modifier.alpha(ContentAlpha.medium),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(key1 = started) {
|
||||
while (started > 0) {
|
||||
delay(1.seconds)
|
||||
now = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
},
|
||||
onClick = onClick
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun NoTimer() {
|
||||
MdcTheme {
|
||||
TimerRow(started = 0, estimated = 0, elapsed = 0, timerClicked = {}, onClick = {})
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, widthDp = 320)
|
||||
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
|
||||
@Composable
|
||||
fun RunningTimer() {
|
||||
MdcTheme {
|
||||
TimerRow(started = System.currentTimeMillis(), estimated = 900, elapsed = 400, timerClicked = {}, onClick = {})
|
||||
}
|
||||
}
|
@ -1,50 +1,36 @@
|
||||
package org.tasks.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import com.google.android.material.composethemeadapter.MdcTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.tasks.R
|
||||
import org.tasks.preferences.Preferences
|
||||
import java.text.SimpleDateFormat
|
||||
import org.tasks.compose.edit.InfoRow
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class CreationDateControlSet : TaskEditControlComposeFragment() {
|
||||
class CreationDateControlSet : TaskEditControlFragment() {
|
||||
@Inject lateinit var locale: Locale
|
||||
|
||||
@Composable
|
||||
override fun Body() {
|
||||
Column(modifier = Modifier.padding(vertical = 20.dp)) {
|
||||
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm", locale)
|
||||
viewModel.creationDate?.let {
|
||||
Text(
|
||||
text = stringResource(id = R.string.sort_created_group, formatter.format(it))
|
||||
)
|
||||
}
|
||||
viewModel.modificationDate?.let {
|
||||
Text(
|
||||
text = stringResource(id = R.string.sort_modified_group, formatter.format(it))
|
||||
)
|
||||
}
|
||||
viewModel.completionDate?.takeIf { it > 0 }?.let {
|
||||
Text(
|
||||
text = stringResource(id = R.string.sort_completion_group, formatter.format(it))
|
||||
)
|
||||
override fun bind(parent: ViewGroup?): View =
|
||||
(parent as ComposeView).apply {
|
||||
setContent {
|
||||
MdcTheme {
|
||||
InfoRow(
|
||||
creationDate = viewModel.creationDate,
|
||||
modificationDate = viewModel.modificationDate,
|
||||
completionDate = viewModel.completionDate,
|
||||
locale = locale,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val icon = R.drawable.ic_outline_info_24px
|
||||
|
||||
override fun controlId() = TAG
|
||||
|
||||
companion object {
|
||||
const val TAG = R.string.TEA_ctrl_creation_date
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
package org.tasks.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.ContentAlpha
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.google.android.material.composethemeadapter.MdcTheme
|
||||
import org.tasks.compose.TaskEditIcon
|
||||
import org.tasks.compose.TaskEditRow
|
||||
|
||||
abstract class TaskEditControlComposeFragment : TaskEditControlFragment() {
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val composeView = ComposeView(requireActivity())
|
||||
viewModel = ViewModelProvider(requireParentFragment())[TaskEditViewModel::class.java]
|
||||
bind(composeView)
|
||||
createView(savedInstanceState)
|
||||
return composeView
|
||||
}
|
||||
|
||||
override fun bind(parent: ViewGroup?): View =
|
||||
(parent as ComposeView).apply {
|
||||
setContent {
|
||||
MdcTheme {
|
||||
TaskEditRow(
|
||||
icon = { Icon() },
|
||||
content = { Body() },
|
||||
onClick = if (this@TaskEditControlComposeFragment.isClickable)
|
||||
this@TaskEditControlComposeFragment::onRowClick
|
||||
else
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
protected open fun Icon() {
|
||||
TaskEditIcon(
|
||||
id = icon,
|
||||
modifier = Modifier
|
||||
.padding(start = 16.dp, top = 20.dp, end = 32.dp, bottom = 20.dp)
|
||||
.alpha(ContentAlpha.medium),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
protected open fun Body() {}
|
||||
}
|
Loading…
Reference in New Issue