New relative reminder picker

Only supports 'before due' for now
pull/1773/head
Alex Baker 2 years ago
parent d062bbb574
commit bfe0af5500

@ -15,9 +15,18 @@ import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.compose.material.AlertDialog
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.ExperimentalComposeUiApi
import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.andlib.utility.AndroidUtilities
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R
import org.tasks.activities.DateAndTimePickerActivity
import org.tasks.compose.AddReminderDialog
import org.tasks.compose.Constants
import org.tasks.data.Alarm
import org.tasks.data.Alarm.Companion.TYPE_DATE_TIME
import org.tasks.data.Alarm.Companion.TYPE_RANDOM
@ -35,6 +44,7 @@ import org.tasks.ui.TaskEditControlFragment
import java.util.concurrent.TimeUnit
import javax.inject.Inject
/**
* Control set dealing with reminder settings
*
@ -50,8 +60,10 @@ class ReminderControlSet : TaskEditControlFragment() {
private lateinit var mode: TextView
private var randomControlSet: RandomReminderControlSet? = null
private val showDialog = mutableStateOf(false)
override fun createView(savedInstanceState: Bundle?) {
showDialog.value = savedInstanceState?.getBoolean(DIALOG_VISIBLE) ?: false
mode.paintFlags = mode.paintFlags or Paint.UNDERLINE_TEXT_FLAG
when {
viewModel.ringNonstop!! -> setRingMode(2)
@ -61,6 +73,12 @@ class ReminderControlSet : TaskEditControlFragment() {
viewModel.selectedAlarms?.forEach(this::addAlarmRow)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(DIALOG_VISIBLE, showDialog.value)
}
private fun onClickRingType() {
val modes = resources.getStringArray(R.array.reminder_ring_modes)
val ringMode = when {
@ -105,6 +123,8 @@ class ReminderControlSet : TaskEditControlFragment() {
addAlarmRow(Alarm(id, TimeUnit.DAYS.toMillis(14), TYPE_RANDOM))
getString(R.string.pick_a_date_and_time) ->
addNewAlarm()
getString(R.string.repeat_option_custom) ->
addCustomAlarm()
}
}
@ -123,6 +143,7 @@ class ReminderControlSet : TaskEditControlFragment() {
}
}
@OptIn(ExperimentalComposeUiApi::class)
override fun bind(parent: ViewGroup?) =
ControlSetRemindersBinding.inflate(layoutInflater, parent, true).let {
alertContainer = it.alertContainer
@ -130,6 +151,61 @@ class ReminderControlSet : TaskEditControlFragment() {
setOnClickListener { onClickRingType() }
}
it.alarmsAdd.setOnClickListener { addAlarm() }
it.dialogView.setContent {
MdcTheme {
val openDialog = remember { showDialog }
val selectedInterval = rememberSaveable { mutableStateOf(15L as Long?) }
val selectedMultiplier = rememberSaveable { mutableStateOf(0) }
if (openDialog.value) {
AlertDialog(
onDismissRequest = {
openDialog.value = false
AndroidUtilities.hideKeyboard(activity)
},
text = {
AddReminderDialog.AddReminderDialog(
openDialog,
selectedInterval,
selectedMultiplier,
)
},
confirmButton = {
Constants.TextButton(text = R.string.ok, onClick = {
val multiplier = -1 * when (selectedMultiplier.value) {
1 -> TimeUnit.HOURS.toMillis(1)
2 -> TimeUnit.DAYS.toMillis(1)
3 -> TimeUnit.DAYS.toMillis(7)
else -> TimeUnit.MINUTES.toMillis(1)
}
selectedInterval.value?.let { i ->
addAlarmRow(
Alarm(
viewModel.task?.id ?: 0L,
i * multiplier,
TYPE_REL_END
)
)
openDialog.value = false
AndroidUtilities.hideKeyboard(activity)
}
})
},
dismissButton = {
Constants.TextButton(
text = R.string.cancel,
onClick = {
openDialog.value = false
AndroidUtilities.hideKeyboard(activity)
})
},
)
} else {
selectedInterval.value = 15
selectedMultiplier.value = 0
}
}
}
it.root
}
@ -178,6 +254,10 @@ class ReminderControlSet : TaskEditControlFragment() {
startActivityForResult(intent, REQUEST_NEW_ALARM)
}
private fun addCustomAlarm() {
showDialog.value = true
}
private fun addAlarmRow(alarm: Alarm, onRemove: View.OnClickListener): View {
val alertItem = requireActivity().layoutInflater.inflate(R.layout.alarm_edit_row, null)
alertContainer.addView(alertItem)
@ -213,11 +293,14 @@ class ReminderControlSet : TaskEditControlFragment() {
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
}
companion object {
const val TAG = R.string.TEA_ctrl_reminders_pref
private const val REQUEST_NEW_ALARM = 12152
private const val DIALOG_VISIBLE = "dialog_visible"
}
}
}

@ -0,0 +1,186 @@
package org.tasks.compose
import android.content.res.Configuration
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedTextField
import androidx.compose.material.RadioButton
import androidx.compose.material.Text
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextRange
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.android.awaitFrame
import org.tasks.R
@ExperimentalComposeUiApi
object AddReminderDialog {
@Composable
fun AddReminderDialog(
visible: MutableState<Boolean> = mutableStateOf(true),
interval: MutableState<Long?>,
selected: MutableState<Int>,
) {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(scrollState)
) {
CenteredH6(resId = R.string.custom_notification)
val focusRequester = remember { FocusRequester() }
OutlinedLongInput(interval, focusRequester)
Spacer(modifier = Modifier.height(16.dp))
val options = listOf(
R.plurals.reminder_minutes,
R.plurals.reminder_hours,
R.plurals.reminder_days,
R.plurals.reminder_week,
)
options.forEachIndexed { index, option ->
RadioRow(index, option, interval, selected)
}
ShowKeyboard(visible, focusRequester)
}
}
}
@ExperimentalComposeUiApi
@Composable
fun ShowKeyboard(visible: MutableState<Boolean>, focusRequester: FocusRequester) {
val keyboardController = LocalSoftwareKeyboardController.current
LaunchedEffect(visible) {
focusRequester.freeFocus()
awaitFrame()
focusRequester.requestFocus()
keyboardController?.show()
}
}
@Composable
fun OutlinedLongInput(
interval: MutableState<Long?>,
focusRequester: FocusRequester
) {
val value = rememberSaveable(stateSaver = TextFieldValue.Saver) {
val text = interval.value.toString()
mutableStateOf(TextFieldValue(text = text, selection = TextRange(0, text.length)))
}
OutlinedTextField(
value = value.value,
onValueChange = {
value.value = it.copy(text = it.text.filter { t -> t.isDigit() })
interval.value = value.value.text.toLongOrNull()
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.focusRequester(focusRequester),
colors = TextFieldDefaults.outlinedTextFieldColors(
textColor = MaterialTheme.colors.onSurface,
focusedBorderColor = MaterialTheme.colors.onSurface
),
isError = value.value.text.toLongOrNull() == null,
)
}
@Composable
fun CenteredH6(@StringRes resId: Int) {
Text(
text = stringResource(id = resId),
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
color = MaterialTheme.colors.onSurface,
style = MaterialTheme.typography.h6
)
}
@Composable
fun RadioRow(index: Int, option: Int, interval: MutableState<Long?>, selected: MutableState<Int>) {
val number = interval.value?.toInt() ?: 1
val optionString = LocalContext.current.resources.getQuantityString(option, number)
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
selected.value = index
}
) {
RadioButton(
selected = index == selected.value,
onClick = {
selected.value = index
},
modifier = Modifier
.padding(16.dp, 8.dp)
.align(CenterVertically)
)
Text(
text = if (index == selected.value) {
stringResource(id = R.string.alarm_before_due, optionString)
} else {
optionString
},
modifier = Modifier.align(CenterVertically),
color = MaterialTheme.colors.onSurface,
style = MaterialTheme.typography.body1,
)
}
}
@ExperimentalComposeUiApi
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun AddReminderOne() =
MaterialTheme(if (isSystemInDarkTheme()) darkColors() else lightColors()) {
AddReminderDialog.AddReminderDialog(
interval = mutableStateOf(1L),
selected = mutableStateOf(0)
)
}
@ExperimentalComposeUiApi
@Preview(showBackground = true)
@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun AddReminderMultiple() =
MaterialTheme(if (isSystemInDarkTheme()) darkColors() else lightColors()) {
AddReminderDialog.AddReminderDialog(
interval = mutableStateOf(15L),
selected = mutableStateOf(1)
)
}

@ -1,5 +1,6 @@
package org.tasks.compose
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@ -15,7 +16,6 @@ import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.MaterialTheme.colors
import androidx.compose.material.Text
import androidx.compose.material.darkColors
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -49,21 +49,17 @@ private val principals = listOf(
)
)
@Preview(showBackground = true, backgroundColor = 0xFFFFFF)
@Preview(showBackground = true)
@Preview(showBackground = true, backgroundColor = 0x202124, uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun Owner() = MaterialTheme {
ListSettingsComposables.PrincipalList(principals) {}
}
@Preview(showBackground = true, backgroundColor = 0x202124)
@Preview(showBackground = true)
@Preview(showBackground = true, backgroundColor = 0x202124, uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun OwnerDark() = MaterialTheme(darkColors()) {
ListSettingsComposables.PrincipalList(principals) {}
}
@Preview(showBackground = true, backgroundColor = 0xFFFFFF)
@Composable
private fun NotOwner() = MaterialTheme {
private fun NotOwner() = MaterialTheme() {
ListSettingsComposables.PrincipalList(principals, null)
}

@ -62,13 +62,24 @@ class AlarmToString @Inject constructor(
private fun getDurationString(duration: Long): String {
val seconds = duration.absoluteValue
val day = TimeUnit.MILLISECONDS.toDays(seconds)
val hours = TimeUnit.MILLISECONDS.toHours(seconds) - day * 24
val days = TimeUnit.MILLISECONDS.toDays(seconds)
val weeks = days / 7
val hours = TimeUnit.MILLISECONDS.toHours(seconds) - days * 24
val minute =
TimeUnit.MILLISECONDS.toMinutes(seconds) - TimeUnit.MILLISECONDS.toHours(seconds) * 60
val result = ArrayList<String>()
if (day > 0) {
result.add(resources.getQuantityString(R.plurals.repeat_n_days, day.toInt(), day.toInt()))
if (weeks > 0) {
result.add(resources.getQuantityString(R.plurals.repeat_n_weeks, weeks.toInt(), weeks.toInt()))
}
val leftoverDays = days - weeks * 7
if (leftoverDays > 0) {
result.add(
resources.getQuantityString(
R.plurals.repeat_n_days,
leftoverDays.toInt(),
leftoverDays.toInt()
)
)
}
if (hours > 0) {
result.add(resources.getQuantityString(R.plurals.repeat_n_hours, hours.toInt(), hours.toInt()))

@ -31,4 +31,9 @@
android:layout_height="wrap_content"
android:hint="@string/add_reminder" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/dialog_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>

@ -209,6 +209,10 @@ File %1$s contained %2$s.\n\n
<item quantity="one">minute</item>
<item quantity="other">minutes</item>
</plurals>
<plurals name="reminder_minutes">
<item quantity="one">Minute</item>
<item quantity="other">Minutes</item>
</plurals>
<plurals name="repeat_n_minutes">
<item quantity="one">%d minute</item>
<item quantity="other">%d minutes</item>
@ -217,6 +221,10 @@ File %1$s contained %2$s.\n\n
<item quantity="one">hour</item>
<item quantity="other">hours</item>
</plurals>
<plurals name="reminder_hours">
<item quantity="one">Hour</item>
<item quantity="other">Hours</item>
</plurals>
<plurals name="repeat_n_hours">
<item quantity="one">%d hour</item>
<item quantity="other">%d hours</item>
@ -225,6 +233,10 @@ File %1$s contained %2$s.\n\n
<item quantity="one">day</item>
<item quantity="other">days</item>
</plurals>
<plurals name="reminder_days">
<item quantity="one">Day</item>
<item quantity="other">Days</item>
</plurals>
<plurals name="repeat_n_days">
<item quantity="one">%d day</item>
<item quantity="other">%d days</item>
@ -233,6 +245,10 @@ File %1$s contained %2$s.\n\n
<item quantity="one">week</item>
<item quantity="other">weeks</item>
</plurals>
<plurals name="reminder_week">
<item quantity="one">Week</item>
<item quantity="other">Weeks</item>
</plurals>
<plurals name="repeat_n_weeks">
<item quantity="one">%d week</item>
<item quantity="other">%d weeks</item>
@ -722,4 +738,5 @@ File %1$s contained %2$s.\n\n
<string name="alarm_before_due">%s before due</string>
<string name="alarm_after_due">%s after due</string>
<string name="snoozed_until">Snoozed until %s</string>
<string name="custom_notification">Custom notification</string>
</resources>

Loading…
Cancel
Save