Add beep tone select, auto dismiss option
This commit is contained in:
parent
769737c483
commit
eeaf40d98a
@ -1,6 +1,8 @@
|
||||
package de.timklge.karooreminder
|
||||
|
||||
import android.util.Log
|
||||
import de.timklge.karooreminder.screens.Reminder
|
||||
import de.timklge.karooreminder.screens.ReminderBeepPattern
|
||||
import de.timklge.karooreminder.screens.defaultReminders
|
||||
import de.timklge.karooreminder.screens.preferencesKey
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
@ -41,7 +43,14 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", "1.0") {
|
||||
}
|
||||
|
||||
val preferences = applicationContext.dataStore.data.map { remindersJson ->
|
||||
Json.decodeFromString<MutableList<Reminder>>(remindersJson[preferencesKey] ?: defaultReminders)
|
||||
try {
|
||||
Json.decodeFromString<MutableList<Reminder>>(
|
||||
remindersJson[preferencesKey] ?: defaultReminders
|
||||
)
|
||||
} catch(e: Throwable){
|
||||
Log.e("karoo-reminder","Failed to read preferences", e)
|
||||
mutableListOf()
|
||||
}
|
||||
}
|
||||
|
||||
karooSystem.streamDataFlow(DataType.Type.ELAPSED_TIME)
|
||||
@ -50,19 +59,20 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", "1.0") {
|
||||
.distinctUntilChanged()
|
||||
.drop(1)
|
||||
.combine(preferences) { elapsedMinutes, reminders -> elapsedMinutes to reminders}
|
||||
.distinctUntilChanged { old, new -> old.first == new.first }
|
||||
.collect { (elapsedMinutes, reminders) ->
|
||||
reminders
|
||||
.filter { reminder -> reminder.isActive && elapsedMinutes % reminder.interval == 0 }
|
||||
.forEach { reminder ->
|
||||
karooSystem.dispatch(TurnScreenOn)
|
||||
delay(1_000)
|
||||
karooSystem.dispatch(PlayBeepPattern(listOf(PlayBeepPattern.Tone(300, 200), PlayBeepPattern.Tone(500, 200), PlayBeepPattern.Tone(700, 200))))
|
||||
if (reminder.tone != ReminderBeepPattern.NO_TONES) karooSystem.dispatch(PlayBeepPattern(reminder.tone.tones))
|
||||
karooSystem.dispatch(
|
||||
InRideAlert(
|
||||
id = "reminder-${reminder.id}-${elapsedMinutes}",
|
||||
detail = reminder.text,
|
||||
title = reminder.name,
|
||||
autoDismissMs = 15_000,
|
||||
autoDismissMs = if(reminder.isAutoDismiss) 15_000 else null,
|
||||
icon = R.drawable.ic_launcher,
|
||||
textColor = R.color.white,
|
||||
backgroundColor = reminder.getResourceColor(applicationContext)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package de.timklge.karooreminder.screens
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@ -12,28 +13,34 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Build
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Done
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@ -42,6 +49,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.maxkeppeker.sheets.core.models.base.UseCaseState
|
||||
import com.maxkeppeler.sheets.color.ColorDialog
|
||||
@ -51,19 +59,31 @@ import com.maxkeppeler.sheets.color.models.ColorSelectionMode
|
||||
import com.maxkeppeler.sheets.color.models.MultipleColors
|
||||
import com.maxkeppeler.sheets.color.models.SingleColor
|
||||
import de.timklge.karooreminder.R
|
||||
import io.hammerhead.karooext.KarooSystemService
|
||||
import io.hammerhead.karooext.models.PlayBeepPattern
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedReminder: Reminder?) -> Unit, onCancel: () -> Unit){
|
||||
val ctx = LocalContext.current
|
||||
val karooSystem = remember { KarooSystemService(ctx) }
|
||||
LaunchedEffect(Unit) {
|
||||
karooSystem.connect{}
|
||||
}
|
||||
var title by remember { mutableStateOf(reminder.name) }
|
||||
var text by remember { mutableStateOf(reminder.text) }
|
||||
var selectedColor by remember { mutableIntStateOf(reminder.foregroundColor) }
|
||||
val colorDialogState by remember { mutableStateOf(UseCaseState()) }
|
||||
var duration by remember { mutableStateOf(reminder.interval.toString()) }
|
||||
var isActive by remember { mutableStateOf(reminder.isActive) }
|
||||
var autoDismiss by remember { mutableStateOf(reminder.isAutoDismiss) }
|
||||
var deleteDialogVisible by remember { mutableStateOf(false) }
|
||||
var toneDialogVisible by remember { mutableStateOf(false) }
|
||||
var selectedTone by remember { mutableStateOf(reminder.tone) }
|
||||
|
||||
fun getUpdatedReminder(): Reminder = Reminder(reminder.id, title, duration.toIntOrNull() ?: 0, text, selectedColor, isActive)
|
||||
fun getUpdatedReminder(): Reminder = Reminder(reminder.id, title, duration.toIntOrNull() ?: 0,
|
||||
text, selectedColor, isActive, isAutoDismiss = autoDismiss, tone = selectedTone)
|
||||
|
||||
Column(modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@ -116,11 +136,28 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
|
||||
.shadow(5.dp, CircleShape)
|
||||
.width(40.dp), content = {})
|
||||
|
||||
Spacer(modifier = Modifier.width(20.dp))
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
|
||||
Text("Change Color")
|
||||
}
|
||||
|
||||
FilledTonalButton(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(60.dp),
|
||||
onClick = {
|
||||
toneDialogVisible = true
|
||||
}) {
|
||||
Icon(Icons.Default.Build, contentDescription = "Change Tone")
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
Text("Change Tone")
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Switch(checked = autoDismiss, onCheckedChange = { autoDismiss = it})
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Text("Auto-Dismiss")
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Switch(checked = isActive, onCheckedChange = { isActive = it})
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
@ -133,6 +170,7 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
|
||||
onSubmit(getUpdatedReminder())
|
||||
}) {
|
||||
Icon(Icons.Default.Done, contentDescription = "Save Reminder")
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
Text("Save")
|
||||
}
|
||||
|
||||
@ -142,6 +180,7 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
|
||||
onCancel()
|
||||
}) {
|
||||
Icon(Icons.Default.Close, contentDescription = "Cancel Editing")
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
Text("Cancel")
|
||||
}
|
||||
|
||||
@ -169,6 +208,64 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
|
||||
},
|
||||
title = { Text("Delete reminder") }, text = { Text("Really delete this reminder?") })
|
||||
}
|
||||
|
||||
if (toneDialogVisible){
|
||||
Dialog(onDismissRequest = { toneDialogVisible = false }) {
|
||||
var dialogSelectedTone by remember { mutableStateOf(selectedTone) }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
) {
|
||||
Column(modifier = Modifier
|
||||
.padding(5.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
|
||||
ReminderBeepPattern.entries.forEach { pattern ->
|
||||
Row(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
dialogSelectedTone = pattern
|
||||
karooSystem.dispatch(PlayBeepPattern(pattern.tones))
|
||||
}, verticalAlignment = Alignment.CenterVertically) {
|
||||
RadioButton(selected = dialogSelectedTone == pattern, onClick = {
|
||||
dialogSelectedTone = pattern
|
||||
karooSystem.dispatch(PlayBeepPattern(pattern.tones))
|
||||
})
|
||||
Text(
|
||||
text = pattern.displayName,
|
||||
modifier = Modifier.padding(start = 10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
FilledTonalButton(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp), onClick = {
|
||||
selectedTone = dialogSelectedTone
|
||||
toneDialogVisible = false
|
||||
}) {
|
||||
Icon(Icons.Default.Done, contentDescription = "Save")
|
||||
Text("Save")
|
||||
}
|
||||
|
||||
FilledTonalButton(modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp), onClick = {
|
||||
toneDialogVisible = false
|
||||
}) {
|
||||
Icon(Icons.Default.Close, contentDescription = "Cancel")
|
||||
Text("Cancel")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,7 @@
|
||||
package de.timklge.karooreminder.screens
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -86,9 +87,15 @@ fun ReminderAppNavHost(modifier: Modifier = Modifier, navController: NavHostCont
|
||||
val ctx = LocalContext.current
|
||||
LaunchedEffect(Unit) {
|
||||
ctx.dataStore.data.distinctUntilChanged().collect { t ->
|
||||
val entries = Json.decodeFromString<MutableList<Reminder>>(t[preferencesKey] ?: defaultReminders)
|
||||
reminders.clear()
|
||||
try {
|
||||
val entries = Json.decodeFromString<MutableList<Reminder>>(
|
||||
t[preferencesKey] ?: defaultReminders
|
||||
)
|
||||
reminders.addAll(entries)
|
||||
} catch(e: Throwable){
|
||||
Log.e("karoo-reminder","Failed to read preferences", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,14 +3,32 @@ package de.timklge.karooreminder.screens
|
||||
import android.content.Context
|
||||
import androidx.core.content.ContextCompat
|
||||
import de.timklge.karooreminder.R
|
||||
import io.hammerhead.karooext.models.PlayBeepPattern
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Serializable
|
||||
enum class ReminderBeepPattern(val displayName: String, val tones: List<PlayBeepPattern.Tone>) {
|
||||
NO_TONES("No tones", emptyList()),
|
||||
THREE_TONES_UP("Three tones up", listOf(PlayBeepPattern.Tone(500, 200), PlayBeepPattern.Tone(700, 200), PlayBeepPattern.Tone(900, 200))),
|
||||
THREE_TONES_DOWN("Three tones down", listOf(PlayBeepPattern.Tone(900, 200), PlayBeepPattern.Tone(700, 200), PlayBeepPattern.Tone(400, 200))),
|
||||
DOUBLE_HIGH("Double high", listOf(
|
||||
PlayBeepPattern.Tone(400, 400),
|
||||
PlayBeepPattern.Tone(0, 200),
|
||||
PlayBeepPattern.Tone(600, 200),
|
||||
PlayBeepPattern.Tone(0, 200),
|
||||
PlayBeepPattern.Tone(600, 200),
|
||||
PlayBeepPattern.Tone(0, 200),
|
||||
PlayBeepPattern.Tone(400, 400))
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class Reminder(val id: Int, var name: String, var interval: Int, var text: String,
|
||||
var foregroundColor: Int = android.graphics.Color.parseColor("#700000"),
|
||||
val isActive: Boolean = true){
|
||||
val isActive: Boolean = true, val isAutoDismiss: Boolean = true,
|
||||
val tone: ReminderBeepPattern = ReminderBeepPattern.THREE_TONES_UP){
|
||||
|
||||
fun getResourceColor(context: Context): Int {
|
||||
return when(foregroundColor){
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user