Click on alarm row to replace it

pull/1957/head
Alex Baker 3 years ago
parent 8ed2880953
commit 20ab106e12

@ -2,16 +2,18 @@ package com.todoroo.astrid.ui
import android.Manifest
import android.app.Activity
import android.app.Activity.RESULT_OK
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.viewModels
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionStatus
import com.google.accompanist.permissions.rememberPermissionState
@ -24,17 +26,11 @@ import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.AlarmRow
import org.tasks.data.Alarm
import org.tasks.data.Alarm.Companion.TYPE_DATE_TIME
import org.tasks.data.Alarm.Companion.TYPE_REL_END
import org.tasks.data.Alarm.Companion.TYPE_REL_START
import org.tasks.data.Alarm.Companion.whenDue
import org.tasks.data.Alarm.Companion.whenOverdue
import org.tasks.data.Alarm.Companion.whenStarted
import org.tasks.date.DateTimeUtils
import org.tasks.dialogs.DialogBuilder
import org.tasks.dialogs.MyTimePickerDialog
import org.tasks.ui.TaskEditControlFragment
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@AndroidEntryPoint
@ -44,7 +40,6 @@ class ReminderControlSet : TaskEditControlFragment() {
@Inject lateinit var locale: Locale
private val ringMode = mutableStateOf(0)
private val vm: ReminderControlSetViewModel by viewModels()
override fun createView(savedInstanceState: Bundle?) {
when {
@ -76,39 +71,6 @@ class ReminderControlSet : TaskEditControlFragment() {
this.ringMode.value = ringMode
}
private fun addAlarm(selected: String) {
val id = viewModel.task.id
when (selected) {
getString(R.string.when_started) ->
viewModel.addAlarm(whenStarted(id))
getString(R.string.when_due) ->
viewModel.addAlarm(whenDue(id))
getString(R.string.when_overdue) ->
viewModel.addAlarm(whenOverdue(id))
getString(R.string.randomly) ->
vm.showRandomDialog(visible = true)
getString(R.string.pick_a_date_and_time) ->
addNewAlarm()
getString(R.string.repeat_option_custom) ->
vm.showCustomDialog(visible = true)
}
}
private fun addAlarm() {
val options = options
if (options.size == 1) {
addNewAlarm()
} else {
dialogBuilder
.newDialog()
.setItems(options) { dialog: DialogInterface, which: Int ->
addAlarm(options[which])
dialog.dismiss()
}
.show()
}
}
@OptIn(ExperimentalPermissionsApi::class)
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
@ -122,6 +84,16 @@ class ReminderControlSet : TaskEditControlFragment() {
} else {
null
}
val pickDateAndTime =
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode != RESULT_OK) return@rememberLauncherForActivityResult
val data = result.data ?: return@rememberLauncherForActivityResult
val timestamp =
data.getLongExtra(MyTimePickerDialog.EXTRA_TIMESTAMP, 0L)
val replace: Alarm? = data.getParcelableExtra(EXTRA_REPLACE)
replace?.let { viewModel.removeAlarm(it) }
viewModel.addAlarm(Alarm(0, timestamp, TYPE_DATE_TIME))
}
AlarmRow(
locale = locale,
alarms = viewModel.selectedAlarms.collectAsStateLifecycleAware().value,
@ -131,59 +103,28 @@ class ReminderControlSet : TaskEditControlFragment() {
notificationPermissions?.launchPermissionRequest()
},
ringMode = ringMode,
newAlarm = this@ReminderControlSet::addAlarm,
addAlarm = viewModel::addAlarm,
openRingType = this@ReminderControlSet::onClickRingType,
deleteAlarm = {
viewModel.selectedAlarms.value = viewModel.selectedAlarms.value.minus(it)
}
)
}
}
}
override fun controlId() = TAG
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_NEW_ALARM) {
if (resultCode == Activity.RESULT_OK) {
val timestamp = data!!.getLongExtra(MyTimePickerDialog.EXTRA_TIMESTAMP, 0L)
viewModel.addAlarm(Alarm(0, timestamp, TYPE_DATE_TIME))
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
private fun addNewAlarm() {
val intent = Intent(activity, DateAndTimePickerActivity::class.java)
deleteAlarm = viewModel::removeAlarm,
pickDateAndTime = { replace ->
pickDateAndTime.launch(
Intent(activity, DateAndTimePickerActivity::class.java)
.putExtra(
DateAndTimePickerActivity.EXTRA_TIMESTAMP,
DateTimeUtils.newDateTime().noon().millis
)
startActivityForResult(intent, REQUEST_NEW_ALARM)
}
private val options: List<String>
get() {
val options: MutableList<String> = ArrayList()
if (viewModel.selectedAlarms.value.find { it.type == TYPE_REL_START && it.time == 0L } == null) {
options.add(getString(R.string.when_started))
.putExtra(EXTRA_REPLACE, replace)
)
}
if (viewModel.selectedAlarms.value.find { it.type == TYPE_REL_END && it.time == 0L } == null) {
options.add(getString(R.string.when_due))
)
}
if (viewModel.selectedAlarms.value.find { it.type == TYPE_REL_END && it.time == TimeUnit.HOURS.toMillis(24) } == null) {
options.add(getString(R.string.when_overdue))
}
options.add(getString(R.string.randomly))
options.add(getString(R.string.pick_a_date_and_time))
options.add(getString(R.string.repeat_option_custom))
return options
}
override fun controlId() = TAG
companion object {
const val TAG = R.string.TEA_ctrl_reminders_pref
private const val REQUEST_NEW_ALARM = 12152
private const val EXTRA_REPLACE = "extra_replace"
}
}

@ -4,12 +4,16 @@ import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import org.tasks.data.Alarm
class ReminderControlSetViewModel : ViewModel() {
data class ViewState(
val showAddAlarm: Boolean = false,
val showCustomDialog: Boolean = false,
val showRandomDialog: Boolean = false,
val replace: Alarm? = null,
)
private val _viewState = MutableStateFlow(ViewState())
@ -17,15 +21,26 @@ class ReminderControlSetViewModel : ViewModel() {
val viewState: StateFlow<ViewState>
get() = _viewState.asStateFlow()
fun showCustomDialog(visible: Boolean) {
_viewState.value = _viewState.value.copy(
showCustomDialog = visible
fun setReplace(alarm: Alarm?) {
_viewState.update { it.copy(replace = alarm) }
}
fun showAddAlarm(visible: Boolean) {
_viewState.update { state ->
state.copy(
showAddAlarm = visible,
replace = state.replace?.takeIf {
visible || state.showCustomDialog || state.showRandomDialog
},
)
}
}
fun showCustomDialog(visible: Boolean) {
_viewState.update { it.copy(showCustomDialog = visible) }
}
fun showRandomDialog(visible: Boolean) {
_viewState.value = _viewState.value.copy(
showRandomDialog = visible
)
_viewState.update { it.copy(showRandomDialog = visible) }
}
}

@ -79,6 +79,7 @@ class DateAndTimePickerActivity : InjectingAppCompatActivity() {
}
addOnPositiveButtonClickListener {
val data = Intent()
data.putExtras(intent)
data.putExtra(
MyTimePickerDialog.EXTRA_TIMESTAMP,
DateTime(

@ -32,6 +32,9 @@ import com.google.android.material.composethemeadapter.MdcTheme
import kotlinx.coroutines.android.awaitFrame
import org.tasks.R
import org.tasks.data.Alarm
import org.tasks.data.Alarm.Companion.TYPE_REL_END
import org.tasks.data.Alarm.Companion.TYPE_REL_START
import org.tasks.data.Alarm.Companion.whenStarted
import org.tasks.reminders.AlarmToString.Companion.getRepeatString
import java.util.concurrent.TimeUnit
@ -104,11 +107,11 @@ object AddReminderDialog {
time.value.takeIf { it >= 0 }?.let { i ->
addAlarm(
Alarm(
0,
-1 * i * units.millis,
Alarm.TYPE_REL_END,
repeat.value,
interval.value * recurringUnits.millis
task = 0,
time = -1 * i * units.millis,
type = TYPE_REL_END,
repeat = repeat.value,
interval = interval.value * recurringUnits.millis
)
)
closeDialog()
@ -460,6 +463,54 @@ fun BodyText(modifier: Modifier = Modifier, text: String) {
)
}
@Composable
fun AlarmDropDown(
visible: Boolean,
existingAlarms: List<Alarm>,
addAlarm: (Alarm) -> Unit,
addRandom: () -> Unit,
addCustom: () -> Unit,
pickDateAndTime: () -> Unit,
dismiss: () -> Unit,
) {
CustomDialog(visible = visible, onDismiss = dismiss) {
Column(modifier = Modifier.padding(vertical = 4.dp)) {
if (existingAlarms.none { it.type == TYPE_REL_START && it.time == 0L }) {
DialogRow(text = R.string.when_started) {
addAlarm(whenStarted(0))
dismiss()
}
}
if (existingAlarms.none { it.type == TYPE_REL_END && it.time == 0L }) {
DialogRow(text = R.string.when_due) {
addAlarm(Alarm.whenDue(0))
dismiss()
}
}
if (existingAlarms.none {
it.type == TYPE_REL_END && it.time == TimeUnit.HOURS.toMillis(24)
}) {
DialogRow(text = R.string.when_overdue) {
addAlarm(Alarm.whenOverdue(0))
dismiss()
}
}
DialogRow(text = R.string.randomly) {
addRandom()
dismiss()
}
DialogRow(text = R.string.pick_a_date_and_time) {
pickDateAndTime()
dismiss()
}
DialogRow(text = R.string.repeat_option_custom) {
addCustom()
dismiss()
}
}
}
}
@ExperimentalComposeUiApi
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@ -547,3 +598,19 @@ fun AddRandomReminder() =
units = remember { mutableStateOf(1) }
)
}
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun AddReminderDialog() =
MdcTheme {
AlarmDropDown(
visible = true,
existingAlarms = emptyList(),
addAlarm = {},
addRandom = {},
addCustom = {},
pickDateAndTime = {},
dismiss = {},
)
}

@ -0,0 +1,29 @@
package org.tasks.compose
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Card
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
@Composable
fun CustomDialog(
visible: Boolean,
onDismiss: () -> Unit,
content: @Composable () -> Unit,
) {
if (!visible) {
return
}
Dialog(onDismissRequest = onDismiss) {
Card(
shape = MaterialTheme.shapes.medium,
modifier = Modifier.padding(16.dp),
elevation = 8.dp
) {
content()
}
}
}

@ -0,0 +1,25 @@
package org.tasks.compose
import androidx.annotation.IntegerRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
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.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@Composable
fun DialogRow(@IntegerRes text: Int, onClick: () -> Unit) {
Text(
text = stringResource(id = text),
modifier = Modifier
.clickable { onClick() }
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 12.dp),
color = MaterialTheme.colors.onSurface,
style = MaterialTheme.typography.body1,
)
}

@ -37,10 +37,10 @@ fun AlarmRow(
alarms: List<Alarm>,
ringMode: Int,
locale: Locale,
newAlarm: () -> Unit,
addAlarm: (Alarm) -> Unit,
deleteAlarm: (Alarm) -> Unit,
openRingType: () -> Unit,
pickDateAndTime: (replace: Alarm?) -> Unit,
) {
TaskEditRow(
iconRes = R.drawable.ic_outline_notifications_24px,
@ -52,7 +52,11 @@ fun AlarmRow(
alarms = alarms,
ringMode = ringMode,
locale = locale,
addAlarm = newAlarm,
replaceAlarm = {
vm.setReplace(it)
vm.showAddAlarm(visible = true)
},
addAlarm = { vm.showAddAlarm(visible = true) },
deleteAlarm = deleteAlarm,
openRingType = openRingType,
)
@ -80,15 +84,34 @@ fun AlarmRow(
}
}
AlarmDropDown(
visible = viewState.showAddAlarm,
existingAlarms = alarms,
addAlarm = {
viewState.replace?.let(deleteAlarm)
addAlarm(it)
},
addRandom = { vm.showRandomDialog(visible = true) },
addCustom = { vm.showCustomDialog(visible = true) },
pickDateAndTime = { pickDateAndTime(viewState.replace) },
dismiss = { vm.showAddAlarm(visible = false) },
)
AddReminderDialog.AddCustomReminderDialog(
openDialog = viewState.showCustomDialog,
addAlarm = addAlarm,
addAlarm = {
viewState.replace?.let(deleteAlarm)
addAlarm(it)
},
closeDialog = { vm.showCustomDialog(visible = false) }
)
AddReminderDialog.AddRandomReminderDialog(
openDialog = viewState.showRandomDialog,
addAlarm = addAlarm,
addAlarm = {
viewState.replace?.let(deleteAlarm)
addAlarm(it)
},
closeDialog = { vm.showRandomDialog(visible = false) }
)
},
@ -100,6 +123,7 @@ fun Alarms(
alarms: List<Alarm>,
ringMode: Int,
locale: Locale,
replaceAlarm: (Alarm) -> Unit,
addAlarm: () -> Unit,
deleteAlarm: (Alarm) -> Unit,
openRingType: () -> Unit,
@ -107,9 +131,11 @@ fun Alarms(
Column {
Spacer(modifier = Modifier.height(8.dp))
alarms.forEach { alarm ->
AlarmRow(AlarmToString(LocalContext.current, locale).toString(alarm)) {
deleteAlarm(alarm)
}
AlarmRow(
text = AlarmToString(LocalContext.current, locale).toString(alarm),
onClick = { replaceAlarm(alarm) },
remove = { deleteAlarm(alarm) }
)
}
Row(modifier = Modifier.fillMaxWidth()) {
DisabledText(
@ -119,7 +145,7 @@ fun Alarms(
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = false),
onClick = { addAlarm() }
onClick = addAlarm,
)
)
Spacer(modifier = Modifier.weight(1f))
@ -150,10 +176,15 @@ fun Alarms(
}
@Composable
private fun AlarmRow(text: String, remove: () -> Unit = {}) {
private fun AlarmRow(
text: String,
onClick: () -> Unit,
remove: () -> Unit,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onClick() }
) {
Text(
text = text,
@ -175,12 +206,12 @@ fun NoAlarms() {
alarms = emptyList(),
ringMode = 0,
locale = Locale.getDefault(),
newAlarm = {},
addAlarm = {},
deleteAlarm = {},
openRingType = {},
permissionStatus = PermissionStatus.Granted,
launchPermissionRequest = {}
launchPermissionRequest = {},
pickDateAndTime = {},
)
}
}
@ -195,12 +226,12 @@ fun PermissionDenied() {
alarms = emptyList(),
ringMode = 0,
locale = Locale.getDefault(),
newAlarm = {},
addAlarm = {},
deleteAlarm = {},
openRingType = {},
permissionStatus = PermissionStatus.Denied(true),
launchPermissionRequest = {}
launchPermissionRequest = {},
pickDateAndTime = {},
)
}
}

@ -28,6 +28,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
@ -385,6 +386,10 @@ class TaskEditViewModel @Inject constructor(
}
}
fun removeAlarm(alarm: Alarm) {
selectedAlarms.update { it.minus(alarm) }
}
fun addAlarm(alarm: Alarm) {
with (selectedAlarms) {
if (value.none { it.same(alarm) }) {

Loading…
Cancel
Save