Move more composable logic

pull/1952/head
Alex Baker 2 years ago
parent e7b6c96576
commit 00c80337de

@ -5,45 +5,33 @@
*/
package com.todoroo.astrid.files
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
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.Companion.CenterVertically
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 androidx.lifecycle.lifecycleScope
import com.google.android.material.composethemeadapter.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.tasks.R
import org.tasks.compose.DisabledText
import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.AttachmentRow
import org.tasks.data.TaskAttachment
import org.tasks.data.TaskAttachmentDao
import org.tasks.dialogs.AddAttachmentDialog
import org.tasks.dialogs.DialogBuilder
import org.tasks.files.FileHelper
import org.tasks.preferences.Preferences
import org.tasks.ui.TaskEditControlComposeFragment
import org.tasks.ui.TaskEditControlFragment
import javax.inject.Inject
@AndroidEntryPoint
class FilesControlSet : TaskEditControlComposeFragment() {
class FilesControlSet : TaskEditControlFragment() {
@Inject lateinit var taskAttachmentDao: TaskAttachmentDao
@Inject lateinit var dialogBuilder: DialogBuilder
@Inject lateinit var preferences: Preferences
@ -59,53 +47,25 @@ class FilesControlSet : TaskEditControlComposeFragment() {
}
}
private fun addAttachment() {
AddAttachmentDialog.newAddAttachmentDialog(this)
.show(parentFragmentManager, FRAG_TAG_ADD_ATTACHMENT_DIALOG)
}
@Composable
override fun Body() {
val attachments =
taskAttachmentDao.watchAttachments(viewModel.task.uuid)
.collectAsStateLifecycleAware(initial = emptyList()).value
Column(
modifier = Modifier.padding(top = if (attachments.isEmpty()) 0.dp else 8.dp),
) {
attachments.forEach {
Row(
modifier = Modifier
.clickable { showFile(it) },
verticalAlignment = CenterVertically,
) {
Text(
text = it.name!!,
modifier = Modifier.weight(1f),
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
MdcTheme {
AttachmentRow(
attachments = taskAttachmentDao.watchAttachments(viewModel.task.uuid)
.collectAsStateLifecycleAware(initial = emptyList()).value,
openAttachment = {
FileHelper.startActionView(requireActivity(), it.parseUri())
},
deleteAttachment = this@FilesControlSet::deleteAttachment,
addAttachment = {
AddAttachmentDialog.newAddAttachmentDialog(this@FilesControlSet)
.show(parentFragmentManager, FRAG_TAG_ADD_ATTACHMENT_DIALOG)
},
)
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,
)
)
}
}
override val icon = R.drawable.ic_outline_attachment_24px
override fun controlId() = TAG
@ -148,11 +108,6 @@ class FilesControlSet : TaskEditControlComposeFragment() {
.show()
}
@SuppressLint("NewApi")
private fun showFile(m: TaskAttachment) {
FileHelper.startActionView(requireActivity(), m.parseUri())
}
private fun copyToAttachmentDirectory(input: Uri?) {
newAttachment(FileHelper.copyToUri(requireContext(), preferences.attachmentsDirectory!!, input!!))
}
@ -171,4 +126,4 @@ class FilesControlSet : TaskEditControlComposeFragment() {
const val TAG = R.string.TEA_ctrl_files_pref
private const val FRAG_TAG_ADD_ATTACHMENT_DIALOG = "frag_tag_add_attachment_dialog"
}
}
}

@ -24,11 +24,11 @@ import org.tasks.repeats.RecurrenceUtils.newRecur
import org.tasks.repeats.RepeatRuleToString
import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils.currentTimeMillis
import org.tasks.ui.TaskEditControlComposeFragment
import org.tasks.ui.TaskEditControlFragment
import javax.inject.Inject
@AndroidEntryPoint
class RepeatControlSet : TaskEditControlComposeFragment() {
class RepeatControlSet : TaskEditControlFragment() {
@Inject lateinit var repeatRuleToString: RepeatRuleToString
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

@ -12,11 +12,11 @@ import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.TagsRow
import org.tasks.tags.TagPickerActivity
import org.tasks.ui.ChipProvider
import org.tasks.ui.TaskEditControlComposeFragment
import org.tasks.ui.TaskEditControlFragment
import javax.inject.Inject
@AndroidEntryPoint
class TagsControlSet : TaskEditControlComposeFragment() {
class TagsControlSet : TaskEditControlFragment() {
@Inject lateinit var chipProvider: ChipProvider
override fun onRowClick() {

@ -7,38 +7,23 @@ package com.todoroo.astrid.timers
import android.app.Activity
import android.os.Bundle
import android.text.format.DateUtils
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
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.unit.dp
import androidx.compose.ui.platform.ComposeView
import androidx.lifecycle.lifecycleScope
import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.astrid.data.Task
import com.todoroo.astrid.ui.TimeDurationControlSet
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.tasks.R
import org.tasks.compose.DisabledText
import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.TimerRow
import org.tasks.dialogs.DialogBuilder
import org.tasks.themes.Theme
import org.tasks.ui.TaskEditControlComposeFragment
import java.lang.System.currentTimeMillis
import org.tasks.ui.TaskEditControlFragment
import javax.inject.Inject
import kotlin.time.Duration.Companion.seconds
/**
* Control Set for managing repeats
@ -46,7 +31,7 @@ import kotlin.time.Duration.Companion.seconds
* @author Tim Su <tim></tim>@todoroo.com>
*/
@AndroidEntryPoint
class TimerControlSet : TaskEditControlComposeFragment() {
class TimerControlSet : TaskEditControlFragment() {
@Inject lateinit var activity: Activity
@Inject lateinit var dialogBuilder: DialogBuilder
@Inject lateinit var theme: Theme
@ -103,76 +88,23 @@ class TimerControlSet : TaskEditControlComposeFragment() {
}
}
@Composable
override fun Body() {
var now by remember { mutableStateOf(currentTimeMillis()) }
val started = viewModel.timerStarted.collectAsStateLifecycleAware().value
val newElapsed = if (started > 0) (now - started) / 1000L else 0
val estimated =
viewModel.estimatedSeconds.collectAsStateLifecycleAware().value.takeIf { it > 0 }
?.let {
stringResource(id = R.string.TEA_timer_est, DateUtils.formatElapsedTime(it.toLong()))
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
MdcTheme {
TimerRow(
started = viewModel.timerStarted.collectAsStateLifecycleAware().value,
estimated = viewModel.estimatedSeconds.collectAsStateLifecycleAware().value,
elapsed = viewModel.elapsedSeconds.collectAsStateLifecycleAware().value,
timerClicked = this@TimerControlSet::timerClicked,
onClick = this@TimerControlSet::onRowClick,
)
}
val elapsed =
(newElapsed + viewModel.elapsedSeconds.collectAsStateLifecycleAware().value)
.takeIf { it > 0 }
?.let {
stringResource(id = R.string.TEA_timer_elap, DateUtils.formatElapsedTime(it))
}
val text = when {
estimated != null && elapsed != null -> "$estimated, $elapsed"
estimated != null -> estimated
elapsed != null -> elapsed
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 = 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 = currentTimeMillis()
}
}
}
override val icon = R.drawable.ic_outline_timer_24px
override fun controlId() = TAG
override val isClickable = true
private fun timerActive() = viewModel.timerStarted.value > 0
interface TimerControlSetCallback {
@ -183,4 +115,4 @@ class TimerControlSet : TaskEditControlComposeFragment() {
companion object {
const val TAG = R.string.TEA_ctrl_timer_pref
}
}
}

@ -43,12 +43,12 @@ import org.tasks.date.DateTimeUtils
import org.tasks.dialogs.DialogBuilder
import org.tasks.dialogs.MyTimePickerDialog
import org.tasks.reminders.AlarmToString
import org.tasks.ui.TaskEditControlComposeFragment
import org.tasks.ui.TaskEditControlFragment
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@AndroidEntryPoint
class ReminderControlSet : TaskEditControlComposeFragment() {
class ReminderControlSet : TaskEditControlFragment() {
@Inject lateinit var activity: Activity
@Inject lateinit var dialogBuilder: DialogBuilder
@Inject lateinit var alarmToString: AlarmToString

@ -22,13 +22,13 @@ import org.tasks.dialogs.StartDatePicker.Companion.EXTRA_TIME
import org.tasks.dialogs.StartDatePicker.Companion.NO_DAY
import org.tasks.dialogs.StartDatePicker.Companion.NO_TIME
import org.tasks.preferences.Preferences
import org.tasks.ui.TaskEditControlComposeFragment
import org.tasks.ui.TaskEditControlFragment
import java.time.format.FormatStyle
import java.util.*
import javax.inject.Inject
@AndroidEntryPoint
class StartDateControlSet : TaskEditControlComposeFragment() {
class StartDateControlSet : TaskEditControlFragment() {
@Inject lateinit var preferences: Preferences
@Inject lateinit var locale: Locale

@ -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,
)
}
}

@ -1,9 +1,14 @@
package org.tasks.compose.edit
import android.content.res.Configuration
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.andlib.sql.QueryTemplate
import com.todoroo.astrid.api.Filter
import org.tasks.R
import org.tasks.compose.ChipGroup
@ -34,4 +39,18 @@ fun ListRow(
},
onClick = onClick
)
}
@ExperimentalComposeUiApi
@Preview(showBackground = true, widthDp = 320)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
@Composable
fun ListPreview() {
MdcTheme {
ListRow(
list = Filter("Default list", QueryTemplate()),
colorProvider = { -769226 },
onClick = {},
)
}
}

@ -1,16 +1,20 @@
package org.tasks.compose.edit
import android.content.res.Configuration
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
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.Chip
import org.tasks.compose.ChipGroup
import org.tasks.compose.DisabledText
import org.tasks.compose.TaskEditRow
import org.tasks.data.TagData
import org.tasks.themes.ColorProvider
import org.tasks.themes.CustomIcons
@Composable
@ -46,3 +50,69 @@ fun TagsRow(
onClick = onClick
)
}
@Preview(showBackground = true, widthDp = 320)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
@Composable
fun NoTags() {
MdcTheme {
TagsRow(
tags = emptyList(),
colorProvider = { 0 },
onClick = {},
)
}
}
@Preview(showBackground = true, widthDp = 320)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
@Composable
fun SingleTag() {
MdcTheme {
TagsRow(
tags = listOf(
TagData("Home").apply {
setIcon(1062)
setColor(ColorProvider.BLUE_500)
}
),
colorProvider = { it },
onClick = {},
)
}
}
@Preview(showBackground = true, widthDp = 320)
@Composable
fun BunchOfTags() {
MdcTheme {
TagsRow(
tags = listOf(
TagData("One"),
TagData("Two"),
TagData("Three"),
TagData("Four"),
TagData("Five"),
),
colorProvider = { it },
onClick = {}
)
}
}
@Preview(showBackground = true, widthDp = 320)
@Composable
fun TagWithReallyLongName() {
MdcTheme {
TagsRow(
tags = listOf(
TagData("This is a tag with a really really long name").apply {
setIcon(1062)
setColor(ColorProvider.BLUE_500)
}
),
colorProvider = { it },
onClick = {},
)
}
}

@ -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 = {})
}
}

@ -4,34 +4,25 @@ import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.provider.CalendarContract
import android.view.View
import android.view.ViewGroup
import android.widget.Toast.LENGTH_SHORT
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.unit.dp
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.Strings.isNullOrEmpty
import org.tasks.calendars.CalendarPicker
import org.tasks.calendars.CalendarProvider
import org.tasks.compose.DisabledText
import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.CalendarRow
import org.tasks.extensions.Context.toast
import org.tasks.preferences.PermissionChecker
import timber.log.Timber
import javax.inject.Inject
@AndroidEntryPoint
class CalendarControlSet : TaskEditControlComposeFragment() {
class CalendarControlSet : TaskEditControlFragment() {
@Inject lateinit var activity: Activity
@Inject lateinit var calendarProvider: CalendarProvider
@Inject lateinit var permissionChecker: PermissionChecker
@ -50,65 +41,34 @@ class CalendarControlSet : TaskEditControlComposeFragment() {
}
}
@Composable
override fun Body() {
val eventUri = viewModel.eventUri.collectAsStateLifecycleAware().value
val selectedCalendar =
viewModel.selectedCalendar.collectAsStateLifecycleAware().value?.let {
calendarProvider.getCalendar(it)?.name
}
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),
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
MdcTheme {
CalendarRow(
eventUri = viewModel.eventUri.collectAsStateLifecycleAware().value,
selectedCalendar = viewModel.selectedCalendar.collectAsStateLifecycleAware().value?.let {
calendarProvider.getCalendar(it)?.name
},
onClick = {
if (viewModel.eventUri.value.isNullOrBlank()) {
CalendarPicker.newCalendarPicker(this@CalendarControlSet, REQUEST_CODE_PICK_CALENDAR, calendarName)
.show(parentFragmentManager, FRAG_TAG_CALENDAR_PICKER)
} else {
openCalendarEvent()
}
},
clear = {
viewModel.selectedCalendar.value = null
viewModel.eventUri.value = null
}
)
}
}
} 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),
)
}
}
override val icon = R.drawable.ic_outline_event_24px
override fun controlId() = TAG
override val isClickable = true
private fun clear() {
viewModel.selectedCalendar.value = null
viewModel.eventUri.value = null
}
override fun onRowClick() {
if (viewModel.eventUri.value.isNullOrBlank()) {
CalendarPicker.newCalendarPicker(this, REQUEST_CODE_PICK_CALENDAR, calendarName)
.show(parentFragmentManager, FRAG_TAG_CALENDAR_PICKER)
} else {
openCalendarEvent()
}
}
private fun openCalendarEvent() {
val cr = activity.contentResolver
val uri = Uri.parse(viewModel.eventUri.value)
@ -172,4 +132,4 @@ class CalendarControlSet : TaskEditControlComposeFragment() {
private const val FRAG_TAG_CALENDAR_PICKER = "frag_tag_calendar_picker"
private const val REQUEST_CODE_PICK_CALENDAR = 70
}
}
}

@ -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
}
}
}

@ -20,7 +20,7 @@ import java.util.*
import javax.inject.Inject
@AndroidEntryPoint
class DeadlineControlSet : TaskEditControlComposeFragment() {
class DeadlineControlSet : TaskEditControlFragment() {
@Inject lateinit var locale: Locale
@Inject lateinit var preferences: Preferences

@ -14,7 +14,7 @@ import org.tasks.ui.TaskEditViewModel.Companion.stripCarriageReturns
import javax.inject.Inject
@AndroidEntryPoint
class DescriptionControlSet : TaskEditControlComposeFragment() {
class DescriptionControlSet : TaskEditControlFragment() {
@Inject lateinit var linkify: Linkify
@Inject lateinit var preferences: Preferences
@Inject lateinit var markdownProvider: MarkdownProvider

@ -17,7 +17,7 @@ import org.tasks.compose.edit.ListRow
import javax.inject.Inject
@AndroidEntryPoint
class ListFragment : TaskEditControlComposeFragment() {
class ListFragment : TaskEditControlFragment() {
@Inject lateinit var chipProvider: ChipProvider
override fun bind(parent: ViewGroup?): View =

@ -30,7 +30,7 @@ import org.tasks.preferences.Preferences
import javax.inject.Inject
@AndroidEntryPoint
class LocationControlSet : TaskEditControlComposeFragment() {
class LocationControlSet : TaskEditControlFragment() {
@Inject lateinit var preferences: Preferences
@Inject lateinit var dialogBuilder: DialogBuilder
@Inject lateinit var permissionChecker: PermissionChecker

@ -12,7 +12,7 @@ import org.tasks.preferences.Preferences
import javax.inject.Inject
@AndroidEntryPoint
class PriorityControlSet : TaskEditControlComposeFragment() {
class PriorityControlSet : TaskEditControlFragment() {
@Inject lateinit var preferences: Preferences
override fun bind(parent: ViewGroup?): View =

@ -54,7 +54,7 @@ import org.tasks.themes.ColorProvider
import javax.inject.Inject
@AndroidEntryPoint
class SubtaskControlSet : TaskEditControlComposeFragment() {
class SubtaskControlSet : TaskEditControlFragment() {
@Inject lateinit var activity: Activity
@Inject lateinit var taskCompleter: TaskCompleter
@Inject lateinit var localBroadcastManager: LocalBroadcastManager

@ -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() {}
}

@ -1,9 +1,21 @@
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.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.composethemeadapter.MdcTheme
import org.tasks.compose.TaskEditIcon
import org.tasks.compose.TaskEditRow
abstract class TaskEditControlFragment : Fragment() {
lateinit var viewModel: TaskEditViewModel
@ -17,5 +29,45 @@ abstract class TaskEditControlFragment : Fragment() {
protected open val icon = 0
abstract fun controlId(): Int
protected abstract fun bind(parent: ViewGroup?): View
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
}
open fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
MdcTheme {
TaskEditRow(
icon = { Icon() },
content = { Body() },
onClick = if (this@TaskEditControlFragment.isClickable)
this@TaskEditControlFragment::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…
Cancel
Save