diff --git a/app/src/main/java/com/todoroo/astrid/timers/TimerControlSet.kt b/app/src/main/java/com/todoroo/astrid/timers/TimerControlSet.kt index 357f1a4e8..d741f4d34 100644 --- a/app/src/main/java/com/todoroo/astrid/timers/TimerControlSet.kt +++ b/app/src/main/java/com/todoroo/astrid/timers/TimerControlSet.kt @@ -7,29 +7,38 @@ package com.todoroo.astrid.timers import android.app.Activity import android.os.Bundle -import android.os.SystemClock -import android.text.format.DateFormat import android.text.format.DateUtils import android.view.View -import android.view.ViewGroup -import android.widget.Chronometer -import android.widget.Chronometer.OnChronometerTickListener -import android.widget.ImageView -import android.widget.TextView 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.lifecycle.lifecycleScope -import com.todoroo.andlib.utility.DateUtilities 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.Strings.isNullOrEmpty -import org.tasks.databinding.ControlSetTimersBinding +import org.tasks.compose.DisabledText +import org.tasks.compose.collectAsStateLifecycleAware import org.tasks.dialogs.DialogBuilder import org.tasks.themes.Theme -import org.tasks.ui.TaskEditControlFragment +import org.tasks.ui.TaskEditControlComposeFragment +import java.lang.System.currentTimeMillis import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds /** * Control Set for managing repeats @@ -37,15 +46,11 @@ import javax.inject.Inject * @author Tim Su @todoroo.com> */ @AndroidEntryPoint -class TimerControlSet : TaskEditControlFragment() { +class TimerControlSet : TaskEditControlComposeFragment() { @Inject lateinit var activity: Activity @Inject lateinit var dialogBuilder: DialogBuilder @Inject lateinit var theme: Theme - private lateinit var displayEdit: TextView - private lateinit var chronometer: Chronometer - private lateinit var timerButton: ImageView - private lateinit var estimated: TimeDurationControlSet private lateinit var elapsed: TimeDurationControlSet private var dialog: AlertDialog? = null @@ -56,9 +61,8 @@ class TimerControlSet : TaskEditControlFragment() { dialogView = activity.layoutInflater.inflate(R.layout.control_set_timers_dialog, null) estimated = TimeDurationControlSet(activity, dialogView, R.id.estimatedDuration, theme) elapsed = TimeDurationControlSet(activity, dialogView, R.id.elapsedDuration, theme) - estimated.setTimeDuration(viewModel.estimatedSeconds!!) - elapsed.setTimeDuration(viewModel.elapsedSeconds!!) - refresh() + estimated.setTimeDuration(viewModel.estimatedSeconds.value) + elapsed.setTimeDuration(viewModel.elapsedSeconds.value) } override fun onAttach(activity: Activity) { @@ -77,8 +81,11 @@ class TimerControlSet : TaskEditControlFragment() { return dialogBuilder .newDialog() .setView(dialogView) - .setPositiveButton(R.string.ok) { _, _ -> refreshDisplayView() } - .setOnCancelListener { refreshDisplayView() } + .setPositiveButton(R.string.ok) { _, _ -> + viewModel.estimatedSeconds.value = estimated.timeDurationInSeconds + viewModel.elapsedSeconds.value = elapsed.timeDurationInSeconds + } + .setOnCancelListener {} .create() } @@ -86,27 +93,78 @@ class TimerControlSet : TaskEditControlFragment() { lifecycleScope.launch { if (timerActive()) { val task = callback.stopTimer() + viewModel.elapsedSeconds.value = task.elapsedSeconds elapsed.setTimeDuration(task.elapsedSeconds) - viewModel.timerStarted = 0 - chronometer.stop() - refreshDisplayView() + viewModel.timerStarted.value = 0 } else { val task = callback.startTimer() - viewModel.timerStarted = task.timerStart - chronometer.start() + viewModel.timerStarted.value = task.timerStart } - updateChronometer() } } - override fun bind(parent: ViewGroup?) = - ControlSetTimersBinding.inflate(layoutInflater, parent, true).let { - displayEdit = it.displayRowEdit - chronometer = it.timer - timerButton = it.timerButton - it.timerContainer.setOnClickListener { timerClicked() } - it.root + @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())) + } + val elapsed = + viewModel.elapsedSeconds.collectAsStateLifecycleAware().value.takeIf { it > 0 } + ?.let { + stringResource(id = R.string.TEA_timer_elap, DateUtils.formatElapsedTime(it + newElapsed)) + } + 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 @@ -114,61 +172,7 @@ class TimerControlSet : TaskEditControlFragment() { override val isClickable = true - private fun refresh() { - refreshDisplayView() - updateChronometer() - } - - private fun refreshDisplayView() { - var est: String? = null - viewModel.estimatedSeconds = estimated.timeDurationInSeconds - if (viewModel.estimatedSeconds!! > 0) { - est = getString( - R.string.TEA_timer_est, - DateUtils.formatElapsedTime(viewModel.estimatedSeconds!!.toLong())) - } - var elap: String? = null - viewModel.elapsedSeconds = elapsed.timeDurationInSeconds - if (viewModel.elapsedSeconds!! > 0) { - elap = getString( - R.string.TEA_timer_elap, - DateUtils.formatElapsedTime(viewModel.elapsedSeconds!!.toLong())) - } - val toDisplay: String? - toDisplay = if (!isNullOrEmpty(est) && !isNullOrEmpty(elap)) { - "$est, $elap" // $NON-NLS-1$ - } else if (!isNullOrEmpty(est)) { - est - } else if (!isNullOrEmpty(elap)) { - elap - } else { - null - } - displayEdit.text = toDisplay - } - - private fun updateChronometer() { - timerButton.setImageResource( - if (timerActive()) R.drawable.ic_outline_pause_24px else R.drawable.ic_outline_play_arrow_24px) - var elapsed = elapsed.timeDurationInSeconds * 1000L - if (timerActive()) { - chronometer.visibility = View.VISIBLE - elapsed += DateUtilities.now() - viewModel.timerStarted - chronometer.base = SystemClock.elapsedRealtime() - elapsed - if (elapsed > DateUtilities.ONE_DAY) { - chronometer.onChronometerTickListener = OnChronometerTickListener { cArg: Chronometer -> - val t = SystemClock.elapsedRealtime() - cArg.base - cArg.text = DateFormat.format("d'd' h:mm", t) // $NON-NLS-1$ - } - } - chronometer.start() - } else { - chronometer.visibility = View.GONE - chronometer.stop() - } - } - - private fun timerActive() = viewModel.timerStarted > 0 + private fun timerActive() = viewModel.timerStarted.value > 0 interface TimerControlSetCallback { suspend fun stopTimer(): Task diff --git a/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt b/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt index 37c47a474..b987ab090 100644 --- a/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt +++ b/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt @@ -119,6 +119,9 @@ class TaskEditViewModel @Inject constructor( } eventUri.value = task.calendarURI priority.value = task.priority + elapsedSeconds.value = task.elapsedSeconds + estimatedSeconds.value = task.estimatedSeconds + timerStarted.value = task.timerStart } lateinit var task: Task @@ -231,17 +234,9 @@ class TaskEditViewModel @Inject constructor( var isNew: Boolean = false private set - var timerStarted: Long - get() = task.timerStart - set(value) { - task.timerStart = value - } - - var estimatedSeconds: Int? = null - get() = field ?: task.estimatedSeconds - - var elapsedSeconds: Int? = null - get() = field ?: task.elapsedSeconds + val timerStarted = MutableStateFlow(0L) + val estimatedSeconds = MutableStateFlow(0) + val elapsedSeconds = MutableStateFlow(0) private lateinit var originalList: Filter @@ -307,8 +302,8 @@ class TaskEditViewModel @Inject constructor( } else { task.calendarURI != eventUri.value } || - task.elapsedSeconds != elapsedSeconds || - task.estimatedSeconds != estimatedSeconds || + task.elapsedSeconds != elapsedSeconds.value || + task.estimatedSeconds != estimatedSeconds.value || originalList != selectedList.value || originalLocation != selectedLocation.value || originalTags.toHashSet() != selectedTags.value.toHashSet() || @@ -337,8 +332,8 @@ class TaskEditViewModel @Inject constructor( task.hideUntil = startDate.value task.recurrence = recurrence task.repeatUntil = repeatUntil!! - task.elapsedSeconds = elapsedSeconds!! - task.estimatedSeconds = estimatedSeconds!! + task.elapsedSeconds = elapsedSeconds.value + task.estimatedSeconds = estimatedSeconds.value task.ringFlags = getRingFlags() applyCalendarChanges() diff --git a/app/src/main/res/layout/control_set_timers.xml b/app/src/main/res/layout/control_set_timers.xml deleted file mode 100644 index 5521a84b3..000000000 --- a/app/src/main/res/layout/control_set_timers.xml +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - - - - - - -