diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ca762b5..770fc5f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -71,7 +71,8 @@ tasks.register("generateManifest") { "latestVersionCode" to android.defaultConfig.versionCode, "developer" to "timklge", "description" to "Shows in-ride alerts after a given time interval, distance or HR / power / speed / cadence out of range", - "releaseNotes" to "* Add additional beep patterns for Karoo 3\n" + + "releaseNotes" to "* Add additional trigger types for ambient and CORE temperature, gradient and tire pressure\n" + + "* Add additional beep patterns for Karoo 3\n" + "* Add touchable back button", ) diff --git a/app/src/main/kotlin/de/timklge/karooreminder/KarooReminderExtension.kt b/app/src/main/kotlin/de/timklge/karooreminder/KarooReminderExtension.kt index 7a94829..877339f 100644 --- a/app/src/main/kotlin/de/timklge/karooreminder/KarooReminderExtension.kt +++ b/app/src/main/kotlin/de/timklge/karooreminder/KarooReminderExtension.kt @@ -47,14 +47,27 @@ enum class ReminderTrigger(val id: String, val label: String) { SPEED_LIMIT_MAXIMUM_EXCEEDED("speed_limit_max", "Speed above value"), SPEED_LIMIT_MINIMUM_EXCEEDED("speed_limit_min", "Speed below value"), CADENCE_LIMIT_MAXIMUM_EXCEEDED("cadence_limit_max", "Cadence above value"), - CADENCE_LIMIT_MINIMUM_EXCEEDED("cadence_limit_min", "Cadence below value"); + CADENCE_LIMIT_MINIMUM_EXCEEDED("cadence_limit_min", "Cadence below value"), + AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED("ambient_temperature_limit_max", "Ambient temperature above value"), + AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED("ambient_temperature_limit_min", "Ambient temperature below value"), + GRADIENT_LIMIT_MAXIMUM_EXCEEDED("gradient_limit_max", "Gradient above value"), + GRADIENT_LIMIT_MINIMUM_EXCEEDED("gradient_limit_min", "Gradient below value"), + CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED("core_temperature_limit_max", "Core temperature above value"), + CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED("core_temperature_limit_min", "Core temperature below value"), + FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED("front_tire_pressure_limit_max", "Front tire pressure above value"), + FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED("front_tire_pressure_limit_min", "Front tire pressure below value"), + REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED("rear_tire_pressure_limit_max", "Rear tire pressure above value"), + REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED("rear_tire_pressure_limit_min", "Rear tire pressure below value"); fun getPrefix(): String { return when (this) { - HR_LIMIT_MINIMUM_EXCEEDED, POWER_LIMIT_MINIMUM_EXCEEDED, SPEED_LIMIT_MINIMUM_EXCEEDED, CADENCE_LIMIT_MINIMUM_EXCEEDED -> "<" - HR_LIMIT_MAXIMUM_EXCEEDED, POWER_LIMIT_MAXIMUM_EXCEEDED, SPEED_LIMIT_MAXIMUM_EXCEEDED, CADENCE_LIMIT_MAXIMUM_EXCEEDED -> ">" - ELAPSED_TIME, DISTANCE -> "" - ENERGY_OUTPUT -> "" + HR_LIMIT_MINIMUM_EXCEEDED, POWER_LIMIT_MINIMUM_EXCEEDED, SPEED_LIMIT_MINIMUM_EXCEEDED, CADENCE_LIMIT_MINIMUM_EXCEEDED, CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED, REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED, + AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, GRADIENT_LIMIT_MINIMUM_EXCEEDED -> "<" + + HR_LIMIT_MAXIMUM_EXCEEDED, POWER_LIMIT_MAXIMUM_EXCEEDED, SPEED_LIMIT_MAXIMUM_EXCEEDED, CADENCE_LIMIT_MAXIMUM_EXCEEDED, CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, + AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, GRADIENT_LIMIT_MAXIMUM_EXCEEDED -> ">" + + ELAPSED_TIME, DISTANCE, ENERGY_OUTPUT -> "" } } @@ -66,9 +79,18 @@ enum class ReminderTrigger(val id: String, val label: String) { POWER_LIMIT_MAXIMUM_EXCEEDED, POWER_LIMIT_MINIMUM_EXCEEDED -> "W" SPEED_LIMIT_MAXIMUM_EXCEEDED, SPEED_LIMIT_MINIMUM_EXCEEDED -> if(imperial) "mph" else "km/h" CADENCE_LIMIT_MAXIMUM_EXCEEDED, CADENCE_LIMIT_MINIMUM_EXCEEDED -> "rpm" + CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED -> "°C" + FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED, REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED -> "bar" ENERGY_OUTPUT -> "kJ" + GRADIENT_LIMIT_MAXIMUM_EXCEEDED, GRADIENT_LIMIT_MINIMUM_EXCEEDED -> "%" } } + + fun isDecimalValue() = this == CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED || this == CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED || + this == FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED || this == FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED || + this == REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED || this == REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED || + this == AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED || this == AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED || + this == GRADIENT_LIMIT_MAXIMUM_EXCEEDED || this == GRADIENT_LIMIT_MINIMUM_EXCEEDED } fun Flow.allIntermediateInts(): Flow = flow { @@ -158,18 +180,7 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS } } - val distanceJob = CoroutineScope(Dispatchers.IO).launch { - val preferences = applicationContext.dataStore.data.map { remindersJson -> - try { - Json.decodeFromString>( - remindersJson[preferencesKey] ?: defaultReminders - ) - } catch(e: Throwable){ - Log.e(TAG,"Failed to read preferences", e) - mutableListOf() - } - } - + val distanceJob = startIntervalJob(ReminderTrigger.DISTANCE) { karooSystem.streamDataFlow(DataType.Type.DISTANCE) .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } .combine(karooSystem.streamUserProfile()) { distance, profile -> distance to profile } @@ -182,25 +193,6 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS } .distinctUntilChanged() .filterNot { it == 0 } - .combine(preferences) { distance, reminders -> distance to reminders} - .distinctUntilChanged { old, new -> old.first == new.first } - .collectLatest { (distance, reminders) -> - val rs = reminders - .filter { reminder -> reminder.trigger == ReminderTrigger.DISTANCE && reminder.isActive && distance % reminder.interval == 0 } - - for (reminder in rs){ - Log.i(TAG, "Distance reminder: ${reminder.name}") - reminderChannel.send(DisplayedReminder(reminder.tone, ReminderTrigger.DISTANCE, InRideAlert( - id = "reminder-${reminder.id}-${distance}", - detail = reminder.text, - title = reminder.name, - autoDismissMs = if(reminder.isAutoDismiss) reminder.autoDismissSeconds * 1000L else null, - icon = R.drawable.timer, - textColor = reminder.displayForegroundColor?.getTextColor() ?: R.color.black, - backgroundColor = reminder.displayForegroundColor?.colorRes ?: R.color.hRed - ))) - } - } } jobs.add(distanceJob) @@ -212,10 +204,41 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS startRangeExceededJob(ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED), startRangeExceededJob(ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED), startRangeExceededJob(ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED), - startRangeExceededJob(ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED) + startRangeExceededJob(ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED), + startRangeExceededJob(ReminderTrigger.CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED), + startRangeExceededJob(ReminderTrigger.CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED), + startRangeExceededJob(ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED), + startRangeExceededJob(ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED), + startRangeExceededJob(ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED), + startRangeExceededJob(ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED), + startRangeExceededJob(ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED), + startRangeExceededJob(ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED), + startRangeExceededJob(ReminderTrigger.GRADIENT_LIMIT_MAXIMUM_EXCEEDED), + startRangeExceededJob(ReminderTrigger.GRADIENT_LIMIT_MINIMUM_EXCEEDED) )) - val elapsedTimeJob = CoroutineScope(Dispatchers.IO).launch { + val elapsedTimeJob = startIntervalJob(ReminderTrigger.ELAPSED_TIME) { + karooSystem.streamDataFlow(DataType.Type.ELAPSED_TIME) + .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } + .map { (it / 1000 / 60).toInt() } + .distinctUntilChanged() + .filterNot { it == 0 } + } + jobs.add(elapsedTimeJob) + + val energyOutputJob = startIntervalJob(ReminderTrigger.ENERGY_OUTPUT) { + karooSystem.streamDataFlow(DataType.Type.ENERGY_OUTPUT) + .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } + .map { it.toInt() } + .distinctUntilChanged() + .filterNot { it == 0 } + .allIntermediateInts() + } + jobs.add(energyOutputJob) + } + + private fun startIntervalJob(trigger: ReminderTrigger, flow: () -> Flow): Job { + return CoroutineScope(Dispatchers.IO).launch { val preferences = applicationContext.dataStore.data.map { remindersJson -> try { Json.decodeFromString>( @@ -227,20 +250,20 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS } } - karooSystem.streamDataFlow(DataType.Type.ELAPSED_TIME) - .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } - .map { (it / 1000 / 60).toInt() } - .distinctUntilChanged() + flow() .filterNot { it == 0 } .combine(preferences) { elapsedMinutes, reminders -> elapsedMinutes to reminders} .distinctUntilChanged { old, new -> old.first == new.first } .collectLatest { (elapsedMinutes, reminders) -> val rs = reminders - .filter { reminder -> reminder.trigger == ReminderTrigger.ELAPSED_TIME && reminder.isActive && elapsedMinutes % reminder.interval == 0 } + .filter { reminder -> + val interval = reminder.interval + reminder.trigger == trigger && reminder.isActive && interval != null && elapsedMinutes % interval == 0 + } for (reminder in rs){ - Log.i(TAG, "Elapsed time reminder: ${reminder.name}") - reminderChannel.send(DisplayedReminder(reminder.tone, ReminderTrigger.ELAPSED_TIME, InRideAlert( + Log.i(TAG, "$trigger reminder: ${reminder.name}") + reminderChannel.send(DisplayedReminder(reminder.tone, trigger, InRideAlert( id = "reminder-${reminder.id}-${elapsedMinutes}", detail = reminder.text, title = reminder.name, @@ -252,47 +275,6 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS } } } - jobs.add(elapsedTimeJob) - - val energyOutputJob = CoroutineScope(Dispatchers.IO).launch { - val preferences = applicationContext.dataStore.data.map { remindersJson -> - try { - Json.decodeFromString>( - remindersJson[preferencesKey] ?: defaultReminders - ) - } catch(e: Throwable){ - Log.e(TAG,"Failed to read preferences", e) - mutableListOf() - } - } - - karooSystem.streamDataFlow(DataType.Type.ENERGY_OUTPUT) - .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } - .map { it.toInt() } - .distinctUntilChanged() - .filterNot { it == 0 } - .allIntermediateInts() - .combine(preferences) { energyOutput, reminders -> energyOutput to reminders} - .distinctUntilChanged { old, new -> old.first == new.first } - .collectLatest { (energyOutput, reminders) -> - val rs = reminders - .filter { reminder -> reminder.trigger == ReminderTrigger.ENERGY_OUTPUT && reminder.isActive && energyOutput % reminder.interval == 0 } - - for (reminder in rs){ - Log.i(TAG, "Energy output reminder: ${reminder.name}") - reminderChannel.send(DisplayedReminder(reminder.tone, ReminderTrigger.ENERGY_OUTPUT, InRideAlert( - id = "reminder-${reminder.id}-${energyOutput}", - detail = reminder.text, - title = reminder.name, - autoDismissMs = if(reminder.isAutoDismiss) reminder.autoDismissSeconds * 1000L else null, - icon = R.drawable.timer, - textColor = reminder.displayForegroundColor?.getTextColor() ?: R.color.black, - backgroundColor = reminder.displayForegroundColor?.colorRes ?: R.color.hRed - ))) - } - } - } - jobs.add(energyOutputJob) } data class StreamData(val value: Double, val reminders: MutableList? = null, val imperial: Boolean = false) @@ -315,33 +297,92 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS ReminderTrigger.POWER_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.POWER_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.SMOOTHED_3S_AVERAGE_POWER ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.SMOOTHED_3S_AVERAGE_SPEED ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.SMOOTHED_3S_AVERAGE_CADENCE + ReminderTrigger.CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.CORE_TEMP + ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.TIRE_PRESSURE_FRONT + ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.TIRE_PRESSURE_REAR + ReminderTrigger.GRADIENT_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.GRADIENT_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.ELEVATION_GRADE + ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED -> DataType.Type.TEMPERATURE + ReminderTrigger.DISTANCE, ReminderTrigger.ELAPSED_TIME, ReminderTrigger.ENERGY_OUTPUT -> error("Unsupported trigger type: $triggerType") } karooSystem.streamDataFlow(dataType) - .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } - .filter { it > 0.0 } + .mapNotNull { + val dataPoint = (it as? StreamState.Streaming)?.dataPoint + + @Suppress("KotlinConstantConditions") + when (triggerType) { + ReminderTrigger.CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED -> dataPoint?.values?.get(DataType.Field.CORE_TEMP) + ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED, + ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED -> dataPoint?.values?.get(DataType.Field.TIRE_PRESSURE) + + ReminderTrigger.ELAPSED_TIME, ReminderTrigger.DISTANCE, ReminderTrigger.ENERGY_OUTPUT, + ReminderTrigger.HR_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.HR_LIMIT_MINIMUM_EXCEEDED, + ReminderTrigger.POWER_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.POWER_LIMIT_MINIMUM_EXCEEDED, + ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED, + ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED, + ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, + ReminderTrigger.GRADIENT_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.GRADIENT_LIMIT_MINIMUM_EXCEEDED -> dataPoint?.singleValue + } + } + .filter { it != 0.0 } .combine(preferences) { value, reminders -> StreamData(value, reminders) } .combine(karooSystem.streamUserProfile()) { streamData, profile -> streamData.copy(imperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL) } - .onlyIfNValuesReceivedWithinTimeframe(5, 1000 * 10) // At least 5 values have been received over the last 10 seconds + .let { + @Suppress("KotlinConstantConditions") + when (triggerType){ + // Tire pressure, gradient and temperature triggers do not require ongoing measurements, as measurement rate is unknown + ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.GRADIENT_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.GRADIENT_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED -> it + + ReminderTrigger.HR_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.HR_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.POWER_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.POWER_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED -> it.onlyIfNValuesReceivedWithinTimeframe(5, 1000 * 10) // At least 5 values have been received over the last 10 seconds + + ReminderTrigger.ELAPSED_TIME, ReminderTrigger.DISTANCE, ReminderTrigger.ENERGY_OUTPUT -> error("Unsupported trigger type: $triggerType") + } + } .map { (value, reminders, imperial) -> val triggered = reminders?.filter { reminder -> - val isSpeedTrigger = triggerType == ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED || triggerType == ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED - val reminderValue = if (isSpeedTrigger){ - // Convert m/s speed to km/h or mph - if (imperial) reminder.interval * 0.44704 else reminder.interval * 0.277778 - } else { - reminder.interval.toDouble() + val reminderValue = when(triggerType) { + ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED -> { + if (imperial) reminder.interval?.times(0.44704) else reminder.interval?.times(0.277778) + } + ReminderTrigger.CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED -> { + reminder.intervalFloat + } + ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED -> { + reminder.intervalFloat?.times(1000.0) // bar to mbar + } + + ReminderTrigger.ELAPSED_TIME, ReminderTrigger.DISTANCE, + ReminderTrigger.ENERGY_OUTPUT, ReminderTrigger.HR_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.HR_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.POWER_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.POWER_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.GRADIENT_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.GRADIENT_LIMIT_MINIMUM_EXCEEDED -> reminder.interval?.toDouble() } val triggerIsMet = when (triggerType){ ReminderTrigger.HR_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.POWER_LIMIT_MAXIMUM_EXCEEDED, - ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED -> value > reminderValue + ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.SPEED_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED, ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED, + ReminderTrigger.GRADIENT_LIMIT_MAXIMUM_EXCEEDED -> reminderValue != null && value > reminderValue ReminderTrigger.HR_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.POWER_LIMIT_MINIMUM_EXCEEDED, - ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED -> value < reminderValue + ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.SPEED_LIMIT_MINIMUM_EXCEEDED, + ReminderTrigger.CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED, + ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED, ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED, + ReminderTrigger.GRADIENT_LIMIT_MINIMUM_EXCEEDED -> reminderValue != null && value < reminderValue - else -> error("Unsupported trigger type: $triggerType") + ReminderTrigger.ELAPSED_TIME, ReminderTrigger.DISTANCE, ReminderTrigger.ENERGY_OUTPUT -> error("Unsupported trigger type: $triggerType") } reminder.isActive && reminder.trigger == triggerType && triggerIsMet diff --git a/app/src/main/kotlin/de/timklge/karooreminder/screens/DetailScreen.kt b/app/src/main/kotlin/de/timklge/karooreminder/screens/DetailScreen.kt index 7d9ccff..b482667 100644 --- a/app/src/main/kotlin/de/timklge/karooreminder/screens/DetailScreen.kt +++ b/app/src/main/kotlin/de/timklge/karooreminder/screens/DetailScreen.kt @@ -79,7 +79,13 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi var text by remember { mutableStateOf(reminder.text) } var selectedColor by remember { mutableStateOf(reminder.displayForegroundColor) } val colorDialogState by remember { mutableStateOf(UseCaseState()) } - var duration by remember { mutableStateOf(reminder.interval.toString()) } + var duration by remember { + mutableStateOf(if (reminder.intervalFloat != null){ + java.text.DecimalFormat("#.##").format(reminder.intervalFloat) + } else { + reminder.interval.toString() + }) + } var isActive by remember { mutableStateOf(reminder.isActive) } var autoDismiss by remember { mutableStateOf(reminder.isAutoDismiss) } var deleteDialogVisible by remember { mutableStateOf(false) } @@ -91,12 +97,17 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi val profile by karooSystem.streamUserProfile().collectAsStateWithLifecycle(null) - fun getUpdatedReminder(): Reminder = Reminder(reminder.id, title, duration.toIntOrNull() ?: 1, - text = text, - displayForegroundColor = selectedColor, - isActive = isActive, - trigger = selectedTrigger, - isAutoDismiss = autoDismiss, tone = selectedTone, autoDismissSeconds = autoDismissSeconds.toIntOrNull() ?: 15) + fun getUpdatedReminder(): Reminder { + val durationString = duration.replace(",", ".") + + return Reminder(reminder.id, title, interval = durationString.toDoubleOrNull()?.toInt() ?: 1, + intervalFloat = if (selectedTrigger.isDecimalValue()) durationString.toDoubleOrNull() else null, + text = text, + displayForegroundColor = selectedColor, + isActive = isActive, + trigger = selectedTrigger, + isAutoDismiss = autoDismiss, tone = selectedTone, autoDismissSeconds = autoDismissSeconds.toIntOrNull() ?: 15) + } Column(modifier = Modifier .fillMaxSize() @@ -136,12 +147,22 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi ReminderTrigger.CADENCE_LIMIT_MAXIMUM_EXCEEDED -> Text("Maximum cadence") ReminderTrigger.CADENCE_LIMIT_MINIMUM_EXCEEDED -> Text("Minimum cadence") ReminderTrigger.ENERGY_OUTPUT -> Text("Energy Output") + ReminderTrigger.CORE_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED -> Text("Maximum core temp") + ReminderTrigger.CORE_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED -> Text("Minimum core temp") + ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED -> Text("Max front tire pressure") + ReminderTrigger.FRONT_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED -> Text("Min front tire pressure") + ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MAXIMUM_EXCEEDED -> Text("Max rear tire pressure") + ReminderTrigger.REAR_TIRE_PRESSURE_LIMIT_MINIMUM_EXCEEDED -> Text("Min rear tire pressure") + ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MAXIMUM_EXCEEDED -> Text("Maximum temp") + ReminderTrigger.AMBIENT_TEMPERATURE_LIMIT_MINIMUM_EXCEEDED -> Text("Minimum temp") + ReminderTrigger.GRADIENT_LIMIT_MAXIMUM_EXCEEDED -> Text("Maximum gradient") + ReminderTrigger.GRADIENT_LIMIT_MINIMUM_EXCEEDED -> Text("Minimum gradient") } }, suffix = { Text(selectedTrigger.getSuffix(profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL)) }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + keyboardOptions = KeyboardOptions(keyboardType = if (selectedTrigger.isDecimalValue()) KeyboardType.Decimal else KeyboardType.Number), singleLine = true ) diff --git a/app/src/main/kotlin/de/timklge/karooreminder/screens/MainScreen.kt b/app/src/main/kotlin/de/timklge/karooreminder/screens/MainScreen.kt index 25b560c..84ab6a2 100644 --- a/app/src/main/kotlin/de/timklge/karooreminder/screens/MainScreen.kt +++ b/app/src/main/kotlin/de/timklge/karooreminder/screens/MainScreen.kt @@ -134,7 +134,7 @@ fun ReminderAppNavHost(modifier: Modifier = Modifier, navController: NavHostCont } composable(route = "create") { val nextReminderId = reminders.maxOfOrNull { it.id + 1 } ?: 0 - val newReminder = Reminder(nextReminderId, "", 30, "") + val newReminder = Reminder(nextReminderId, "", 30, text = "") DetailScreen(true, newReminder, { updatedReminder -> updatedReminder?.let { r -> @@ -203,7 +203,8 @@ fun MainScreen(reminders: MutableList, onNavigateToReminder: (r: Remin Spacer(Modifier.weight(1.0f)) - Text("${reminder.trigger.getPrefix()}${reminder.interval}${reminder.trigger.getSuffix(profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL)}") + val value = if (reminder.trigger.isDecimalValue()) java.text.DecimalFormat("#.##").format(reminder.intervalFloat) else reminder.interval + Text("${reminder.trigger.getPrefix()}${value}${reminder.trigger.getSuffix(profile?.preferredUnit?.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL)}") } } } diff --git a/app/src/main/kotlin/de/timklge/karooreminder/screens/Reminder.kt b/app/src/main/kotlin/de/timklge/karooreminder/screens/Reminder.kt index 1677785..5db417b 100644 --- a/app/src/main/kotlin/de/timklge/karooreminder/screens/Reminder.kt +++ b/app/src/main/kotlin/de/timklge/karooreminder/screens/Reminder.kt @@ -126,7 +126,12 @@ enum class ReminderColor(@ColorRes val colorRes: Int, val whiteFont: Boolean, va } @Serializable -class Reminder(val id: Int, var name: String, var interval: Int, var text: String, +class Reminder(val id: Int, var name: String, + /** Trigger value used by all triggers except temperature, gradient, tire pressure */ + var interval: Int? = null, + /** Trigger value used by temperature, gradient, tire pressure triggers */ + var intervalFloat: Double? = null, + var text: String, var displayForegroundColor: ReminderColor? = null, @Deprecated("Use displayForegroundColor instead") var foregroundColor: Int = android.graphics.Color.parseColor("#FF6060"), @@ -135,4 +140,4 @@ class Reminder(val id: Int, var name: String, var interval: Int, var text: Strin var trigger: ReminderTrigger = ReminderTrigger.ELAPSED_TIME, val autoDismissSeconds: Int = 15) -val defaultReminders = Json.encodeToString(listOf(Reminder(0, "Drink", 30, "Take a sip!"))) \ No newline at end of file +val defaultReminders = Json.encodeToString(listOf(Reminder(0, "Drink", 30, text = "Take a sip!"))) \ No newline at end of file diff --git a/detail.png b/detail.png index 47cf111..fe2aa6e 100644 Binary files a/detail.png and b/detail.png differ diff --git a/list.png b/list.png index e576ef7..8911b25 100644 Binary files a/list.png and b/list.png differ diff --git a/reminder.png b/reminder.png index bec77f9..e14b17b 100644 Binary files a/reminder.png and b/reminder.png differ