Compare commits

...

6 Commits

Author SHA1 Message Date
50556dec31 Update button styling
All checks were successful
Build / build (push) Successful in 4m43s
2025-05-29 14:09:24 +02:00
a08e4a9355 Add readme info
All checks were successful
Build / build (push) Successful in 5m17s
2025-05-29 13:28:08 +02:00
e9f6e2c7a3 Fix BASE_URL
Some checks failed
Build / build (push) Has been cancelled
2025-05-29 13:24:42 +02:00
373c5be7fc Enable pipeline for all branches
Some checks failed
Build / build (push) Has been cancelled
2025-05-29 13:21:26 +02:00
8a055b7fcb Update changelog 2025-05-29 13:20:01 +02:00
bf500596fb Add profile selection dialog 2025-05-29 13:15:24 +02:00
9 changed files with 158 additions and 45 deletions

View File

@ -3,10 +3,10 @@ name: Build
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: [ "master" ] branches: [ "**" ]
tags: [ "*" ] tags: [ "*" ]
pull_request: pull_request:
branches: [ "master" ] branches: [ "**" ]
jobs: jobs:
build: build:
@ -26,7 +26,7 @@ jobs:
echo "KEYSTORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}" >> $GITHUB_ENV echo "KEYSTORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}" >> $GITHUB_ENV
echo "KEYSTORE_BASE64=${{ secrets.KEYSTORE_BASE64 }}" >> $GITHUB_ENV echo "KEYSTORE_BASE64=${{ secrets.KEYSTORE_BASE64 }}" >> $GITHUB_ENV
echo "BUILD_NUMBER=${{ github.run_number }}" >> $GITHUB_ENV echo "BUILD_NUMBER=${{ github.run_number }}" >> $GITHUB_ENV
echo "BASE_URL=${{ secrets.BASE_URL || '"https://github.com/timklge/karoo-powerbar/releases/latest/download"' }}" >> $GITHUB_ENV echo "BASE_URL=${{ secrets.BASE_URL || 'https://github.com/timklge/karoo-reminder/releases/latest/download' }}" >> $GITHUB_ENV
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: set up JDK 17 - name: set up JDK 17
uses: actions/setup-java@v4 uses: actions/setup-java@v4
@ -61,3 +61,4 @@ jobs:
list.png list.png
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -6,6 +6,8 @@
Karoo extension that displays in-ride alerts based on custom triggers. Reminders can be set to activate after a specific time interval, distance traveled, energy output or when a sensor value is outside a defined range (e.g., heart rate exceeds zone 2). Karoo extension that displays in-ride alerts based on custom triggers. Reminders can be set to activate after a specific time interval, distance traveled, energy output or when a sensor value is outside a defined range (e.g., heart rate exceeds zone 2).
By default, created reminders are active for all ride profiles. If you want to limit reminders to specific ride profiles (e. g. your "Gravel" profile), you can do so in the reminder settings.
Compatible with Karoo 2 and Karoo 3 devices. Compatible with Karoo 2 and Karoo 3 devices.
![Reminder List](list.png) ![Reminder List](list.png)

View File

@ -73,7 +73,7 @@ tasks.register("generateManifest") {
"latestVersionCode" to android.defaultConfig.versionCode, "latestVersionCode" to android.defaultConfig.versionCode,
"developer" to "github.com/timklge", "developer" to "github.com/timklge",
"description" to "Open-source extension that shows in-ride alerts after a given time interval has passed, distance has been traveled or HR / power / speed / cadence range is exceeded", "description" to "Open-source extension that shows in-ride alerts after a given time interval has passed, distance has been traveled or HR / power / speed / cadence range is exceeded",
"releaseNotes" to "* Add rolling average setting for power triggers", "releaseNotes" to "* Add profile selection dialog per reminder\n* Add rolling average setting for power triggers",
"screenshotUrls" to listOf( "screenshotUrls" to listOf(
"$baseUrl/reminder.png", "$baseUrl/reminder.png",
"$baseUrl/list.png", "$baseUrl/list.png",

View File

@ -7,6 +7,7 @@ import de.timklge.karooreminder.screens.Reminder
import de.timklge.karooreminder.screens.defaultReminders import de.timklge.karooreminder.screens.defaultReminders
import de.timklge.karooreminder.screens.preferencesKey import de.timklge.karooreminder.screens.preferencesKey
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.ActiveRideProfile
import io.hammerhead.karooext.models.OnStreamState import io.hammerhead.karooext.models.OnStreamState
import io.hammerhead.karooext.models.RideState import io.hammerhead.karooext.models.RideState
import io.hammerhead.karooext.models.StreamState import io.hammerhead.karooext.models.StreamState
@ -40,6 +41,17 @@ fun KarooSystemService.streamRideState(): Flow<RideState> {
} }
} }
fun KarooSystemService.streamActiveRideProfile(): Flow<ActiveRideProfile> {
return callbackFlow {
val listenerId = addConsumer { activeProfile: ActiveRideProfile ->
trySendBlocking(activeProfile)
}
awaitClose {
removeConsumer(listenerId)
}
}
}
fun KarooSystemService.streamUserProfile(): Flow<UserProfile> { fun KarooSystemService.streamUserProfile(): Flow<UserProfile> {
return callbackFlow { return callbackFlow {
val listenerId = addConsumer { userProfile: UserProfile -> val listenerId = addConsumer { userProfile: UserProfile ->

View File

@ -5,8 +5,10 @@ import android.media.MediaPlayer
import android.util.Log import android.util.Log
import de.timklge.karooreminder.screens.Reminder import de.timklge.karooreminder.screens.Reminder
import de.timklge.karooreminder.screens.ReminderBeepPattern import de.timklge.karooreminder.screens.ReminderBeepPattern
import de.timklge.karooreminder.screens.reminderIsActive
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.extension.KarooExtension import io.hammerhead.karooext.extension.KarooExtension
import io.hammerhead.karooext.models.ActiveRideProfile
import io.hammerhead.karooext.models.DataType import io.hammerhead.karooext.models.DataType
import io.hammerhead.karooext.models.HardwareType import io.hammerhead.karooext.models.HardwareType
import io.hammerhead.karooext.models.InRideAlert import io.hammerhead.karooext.models.InRideAlert
@ -231,13 +233,19 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
val triggerJobs = mutableSetOf<Job>() val triggerJobs = mutableSetOf<Job>()
data class StreamData(val preferences: MutableList<Reminder>, val activeProfile: ActiveRideProfile)
val streamDataFlow = combine(streamPreferences(), karooSystem.streamActiveRideProfile()) { reminders, activeProfile ->
StreamData(preferences = reminders, activeProfile = activeProfile)
}
triggerStreamJob = CoroutineScope(Dispatchers.IO).launch { triggerStreamJob = CoroutineScope(Dispatchers.IO).launch {
streamPreferences().collect { reminders -> streamDataFlow.collect { (reminders, activeRideProfile) ->
triggerJobs.forEach { it.cancel() } triggerJobs.forEach { it.cancel() }
triggerJobs.clear() triggerJobs.clear()
if (reminders.any { it.trigger == ReminderTrigger.DISTANCE }){ if (reminders.any { it.trigger == ReminderTrigger.DISTANCE }){
val distanceJob = startIntervalJob(ReminderTrigger.DISTANCE) { val distanceJob = startIntervalJob(reminders, activeRideProfile, ReminderTrigger.DISTANCE) {
karooSystem.streamDataFlow(DataType.Type.DISTANCE) karooSystem.streamDataFlow(DataType.Type.DISTANCE)
.mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue }
.combine(karooSystem.streamUserProfile()) { distance, profile -> distance to profile } .combine(karooSystem.streamUserProfile()) { distance, profile -> distance to profile }
@ -255,7 +263,7 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
} }
if (reminders.any { it.trigger == ReminderTrigger.ELAPSED_TIME }){ if (reminders.any { it.trigger == ReminderTrigger.ELAPSED_TIME }){
val elapsedTimeJob = startIntervalJob(ReminderTrigger.ELAPSED_TIME) { val elapsedTimeJob = startIntervalJob(reminders, activeRideProfile, ReminderTrigger.ELAPSED_TIME) {
karooSystem.streamDataFlow(DataType.Type.ELAPSED_TIME) karooSystem.streamDataFlow(DataType.Type.ELAPSED_TIME)
.mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue }
.map { (it / 1000 / 60).toInt() } .map { (it / 1000 / 60).toInt() }
@ -266,7 +274,7 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
} }
if (reminders.any { it.trigger == ReminderTrigger.ENERGY_OUTPUT }){ if (reminders.any { it.trigger == ReminderTrigger.ENERGY_OUTPUT }){
val energyOutputJob = startIntervalJob(ReminderTrigger.ENERGY_OUTPUT) { val energyOutputJob = startIntervalJob(reminders, activeRideProfile, ReminderTrigger.ENERGY_OUTPUT) {
karooSystem.streamDataFlow(DataType.Type.ENERGY_OUTPUT) karooSystem.streamDataFlow(DataType.Type.ENERGY_OUTPUT)
.mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue } .mapNotNull { (it as? StreamState.Streaming)?.dataPoint?.singleValue }
.map { it.toInt() } .map { it.toInt() }
@ -301,7 +309,7 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
intervalTriggers.forEach { trigger -> intervalTriggers.forEach { trigger ->
SmoothSetting.entries.forEach { smoothSetting -> SmoothSetting.entries.forEach { smoothSetting ->
if (reminders.any { it.trigger == trigger && it.smoothSetting == smoothSetting }){ if (reminders.any { it.trigger == trigger && it.smoothSetting == smoothSetting }){
val job = startRangeExceededJob(trigger, smoothSetting) val job = startRangeExceededJob(reminders, activeRideProfile, trigger, smoothSetting)
triggerJobs.add(job) triggerJobs.add(job)
} }
} }
@ -310,41 +318,43 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
} }
} }
private fun startIntervalJob(trigger: ReminderTrigger, flow: () -> Flow<Int>): Job { private fun startIntervalJob(preferences: List<Reminder>, activeRideProfile: ActiveRideProfile, trigger: ReminderTrigger, flow: () -> Flow<Int>): Job {
return CoroutineScope(Dispatchers.IO).launch { return CoroutineScope(Dispatchers.IO).launch {
val preferences = streamPreferences()
flow() flow()
.filterNot { it == 0 } .filterNot { it == 0 }
.combine(preferences) { elapsedMinutes, reminders -> elapsedMinutes to reminders} .distinctUntilChanged()
.distinctUntilChanged { old, new -> old.first == new.first } .collectLatest { elapsedMinutes ->
.collectLatest { (elapsedMinutes, reminders) -> val rs = preferences
val rs = reminders
.filter { reminder -> .filter { reminder ->
val interval = reminder.interval val interval = reminder.interval
reminder.trigger == trigger && reminder.isActive && interval != null && elapsedMinutes % interval == 0 reminder.trigger == trigger && reminderIsActive(reminder, activeRideProfile.profile) && interval != null && elapsedMinutes % interval == 0
} }
for (reminder in rs){ for (reminder in rs) {
Log.i(TAG, "$trigger reminder: ${reminder.name}") Log.i(TAG, "$trigger reminder: ${reminder.name}")
reminderChannel.send(DisplayedReminder(reminder.tone, trigger, InRideAlert( reminderChannel.send(
id = "reminder-${reminder.id}-${elapsedMinutes}", DisplayedReminder(
detail = reminder.text, reminder.tone, trigger, InRideAlert(
title = reminder.name, id = "reminder-${reminder.id}-${elapsedMinutes}",
autoDismissMs = if(reminder.isAutoDismiss) reminder.autoDismissSeconds * 1000L else null, detail = reminder.text,
icon = R.drawable.timer, title = reminder.name,
textColor = reminder.displayForegroundColor?.getTextColor() ?: R.color.black, autoDismissMs = if (reminder.isAutoDismiss) reminder.autoDismissSeconds * 1000L else null,
backgroundColor = reminder.displayForegroundColor?.colorRes ?: R.color.hRed icon = R.drawable.timer,
))) textColor = reminder.displayForegroundColor?.getTextColor()
?: R.color.black,
backgroundColor = reminder.displayForegroundColor?.colorRes
?: R.color.hRed
)
)
)
} }
} }
} }
} }
private fun startRangeExceededJob(triggerType: ReminderTrigger, smoothSetting: SmoothSetting): Job { private fun startRangeExceededJob(preferences: MutableList<Reminder>, activeRideProfile: ActiveRideProfile, triggerType: ReminderTrigger, smoothSetting: SmoothSetting): Job {
return CoroutineScope(Dispatchers.IO).launch { return CoroutineScope(Dispatchers.IO).launch {
val preferences = streamPreferences()
Log.i(TAG, "Starting range exceeded job for trigger $triggerType with smooth setting $smoothSetting") Log.i(TAG, "Starting range exceeded job for trigger $triggerType with smooth setting $smoothSetting")
val valueStream = karooSystem.streamDataFlow(triggerType.getSmoothedDataType(smoothSetting)) val valueStream = karooSystem.streamDataFlow(triggerType.getSmoothedDataType(smoothSetting))
@ -370,9 +380,9 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
data class StreamData(val value: Double, val reminders: MutableList<Reminder>, val distanceImperial: Boolean, val temperatureImperial: Boolean, val rideState: RideState) data class StreamData(val value: Double, val reminders: MutableList<Reminder>, val distanceImperial: Boolean, val temperatureImperial: Boolean, val rideState: RideState)
combine(valueStream, preferences, karooSystem.streamUserProfile(), karooSystem.streamRideState()) { value, reminders, profile, rideState -> combine(valueStream, karooSystem.streamUserProfile(), karooSystem.streamRideState()) { value, profile, rideState ->
StreamData(distanceImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL, temperatureImperial = profile.preferredUnit.temperature == UserProfile.PreferredUnit.UnitType.IMPERIAL, StreamData(distanceImperial = profile.preferredUnit.distance == UserProfile.PreferredUnit.UnitType.IMPERIAL, temperatureImperial = profile.preferredUnit.temperature == UserProfile.PreferredUnit.UnitType.IMPERIAL,
value = value, reminders = reminders, rideState = rideState) value = value, reminders = preferences, rideState = rideState)
}.filter { }.filter {
it.rideState is RideState.Recording it.rideState is RideState.Recording
}.let { }.let {
@ -432,7 +442,7 @@ class KarooReminderExtension : KarooExtension("karoo-reminder", BuildConfig.VERS
ReminderTrigger.ELAPSED_TIME, ReminderTrigger.DISTANCE, ReminderTrigger.ENERGY_OUTPUT -> error("Unsupported trigger type: $triggerType") ReminderTrigger.ELAPSED_TIME, ReminderTrigger.DISTANCE, ReminderTrigger.ENERGY_OUTPUT -> error("Unsupported trigger type: $triggerType")
} }
reminder.isActive && reminder.trigger == triggerType && triggerIsMet reminderIsActive(reminder, activeRideProfile.profile) && reminder.trigger == triggerType && triggerIsMet
} }
triggered triggered

View File

@ -103,6 +103,8 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
var selectedTone by remember { mutableStateOf(reminder.tone) } var selectedTone by remember { mutableStateOf(reminder.tone) }
var autoDismissSeconds by remember { mutableStateOf(reminder.autoDismissSeconds.toString()) } var autoDismissSeconds by remember { mutableStateOf(reminder.autoDismissSeconds.toString()) }
var selectedTrigger by remember { mutableStateOf(reminder.trigger) } var selectedTrigger by remember { mutableStateOf(reminder.trigger) }
var rideProfileDialogVisible by remember { mutableStateOf(false) }
var enabledRideProfiles by remember { mutableStateOf(reminder.enabledRideProfiles.toMutableSet()) }
val profile by karooSystem.streamUserProfile().collectAsStateWithLifecycle(null) val profile by karooSystem.streamUserProfile().collectAsStateWithLifecycle(null)
@ -116,7 +118,8 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
isActive = isActive, isActive = isActive,
smoothSetting = smoothSetting, smoothSetting = smoothSetting,
trigger = selectedTrigger, trigger = selectedTrigger,
isAutoDismiss = autoDismiss, tone = selectedTone, autoDismissSeconds = autoDismissSeconds.toIntOrNull() ?: 15) isAutoDismiss = autoDismiss, tone = selectedTone, autoDismissSeconds = autoDismissSeconds.toIntOrNull() ?: 15,
enabledRideProfiles = enabledRideProfiles.toSet())
} }
Column(modifier = Modifier Column(modifier = Modifier
@ -265,6 +268,16 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
Text("Is Active") Text("Is Active")
} }
FilledTonalButton(modifier = Modifier
.fillMaxWidth()
.height(60.dp), onClick = {
rideProfileDialogVisible = true
}) {
Icon(Icons.Default.Build, contentDescription = "Change Ride Profiles", modifier = Modifier.size(20.dp))
Spacer(modifier = Modifier.width(5.dp))
Text("Ride Profiles: ${if (enabledRideProfiles.isEmpty()) "All" else enabledRideProfiles.joinToString(", ")}")
}
FilledTonalButton(modifier = Modifier FilledTonalButton(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(50.dp), onClick = { .height(50.dp), onClick = {
@ -310,6 +323,75 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
title = { Text("Delete reminder") }, text = { Text("Really delete this reminder?") }) title = { Text("Delete reminder") }, text = { Text("Really delete this reminder?") })
} }
if (rideProfileDialogVisible) {
var newProfileName by remember { mutableStateOf("") }
Dialog(onDismissRequest = { rideProfileDialogVisible = false }) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
shape = RoundedCornerShape(10.dp),
) {
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
if (enabledRideProfiles.isEmpty()) {
Text("All profiles enabled")
} else {
Column(modifier = Modifier.fillMaxWidth()) {
enabledRideProfiles.forEach { profileName ->
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(profileName)
FilledTonalButton(onClick = {
enabledRideProfiles = enabledRideProfiles.toMutableSet().apply { remove(profileName) }
}) {
Icon(Icons.Default.Delete, contentDescription = "Delete profile")
}
}
}
}
}
OutlinedTextField(
value = newProfileName,
onValueChange = { newProfileName = it },
label = { Text("New profile name") },
modifier = Modifier.fillMaxWidth(),
singleLine = true
)
FilledTonalButton(
onClick = {
if (newProfileName.isNotBlank()) {
enabledRideProfiles = enabledRideProfiles.toMutableSet().apply { add(newProfileName) }
newProfileName = "" // Clear the text field
}
},
modifier = Modifier.fillMaxWidth()
) {
Text("Add Profile")
}
FilledTonalButton(
onClick = { rideProfileDialogVisible = false },
modifier = Modifier.fillMaxWidth()
) {
Text("Close")
}
}
}
}
}
if (triggerDialogVisible){ if (triggerDialogVisible){
Dialog(onDismissRequest = { triggerDialogVisible = false }) { Dialog(onDismissRequest = { triggerDialogVisible = false }) {
Card( Card(
@ -430,3 +512,4 @@ fun DetailScreen(isCreating: Boolean, reminder: Reminder, onSubmit: (updatedRemi
} }
} }
} }

View File

@ -20,13 +20,8 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Add
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FabPosition
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@ -63,6 +58,7 @@ import androidx.navigation.navArgument
import de.timklge.karooreminder.KarooReminderExtension import de.timklge.karooreminder.KarooReminderExtension
import de.timklge.karooreminder.R import de.timklge.karooreminder.R
import de.timklge.karooreminder.dataStore import de.timklge.karooreminder.dataStore
import de.timklge.karooreminder.streamActiveRideProfile
import de.timklge.karooreminder.streamUserProfile import de.timklge.karooreminder.streamUserProfile
import io.hammerhead.karooext.KarooSystemService import io.hammerhead.karooext.KarooSystemService
import io.hammerhead.karooext.models.UserProfile import io.hammerhead.karooext.models.UserProfile
@ -72,7 +68,6 @@ import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
val preferencesKey = stringPreferencesKey("reminders") val preferencesKey = stringPreferencesKey("reminders")
suspend fun saveReminders(context: Context, reminders: MutableList<Reminder>) { suspend fun saveReminders(context: Context, reminders: MutableList<Reminder>) {
@ -165,6 +160,7 @@ fun MainScreen(reminders: MutableList<Reminder>, onNavigateToReminder: (r: Remin
var showWarnings by remember { mutableStateOf(false) } var showWarnings by remember { mutableStateOf(false) }
val profile by karooSystem.streamUserProfile().collectAsStateWithLifecycle(null) val profile by karooSystem.streamUserProfile().collectAsStateWithLifecycle(null)
val currentRideProfile by karooSystem.streamActiveRideProfile().collectAsStateWithLifecycle(null)
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
delay(1000L) delay(1000L)
@ -197,7 +193,7 @@ fun MainScreen(reminders: MutableList<Reminder>, onNavigateToReminder: (r: Remin
Card(Modifier Card(Modifier
.fillMaxWidth() .fillMaxWidth()
.height(60.dp) .height(60.dp)
.alpha(if (reminder.isActive) 1f else 0.6f) .alpha(if (reminderIsActive(reminder, currentRideProfile?.profile)) 1f else 0.6f)
.clickable { onNavigateToReminder(reminder) } .clickable { onNavigateToReminder(reminder) }
.padding(5.dp), shape = RoundedCornerShape(corner = CornerSize(10.dp)) .padding(5.dp), shape = RoundedCornerShape(corner = CornerSize(10.dp))
) { ) {

View File

@ -7,6 +7,7 @@ import de.timklge.karooreminder.R
import de.timklge.karooreminder.ReminderTrigger import de.timklge.karooreminder.ReminderTrigger
import de.timklge.karooreminder.SmoothSetting import de.timklge.karooreminder.SmoothSetting
import io.hammerhead.karooext.models.PlayBeepPattern import io.hammerhead.karooext.models.PlayBeepPattern
import io.hammerhead.karooext.models.RideProfile
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -141,6 +142,14 @@ class Reminder(val id: Int, var name: String,
val isActive: Boolean = true, val isAutoDismiss: Boolean = true, val isActive: Boolean = true, val isAutoDismiss: Boolean = true,
val tone: ReminderBeepPattern = ReminderBeepPattern.THREE_TONES_UP, val tone: ReminderBeepPattern = ReminderBeepPattern.THREE_TONES_UP,
var trigger: ReminderTrigger = ReminderTrigger.ELAPSED_TIME, var trigger: ReminderTrigger = ReminderTrigger.ELAPSED_TIME,
val autoDismissSeconds: Int = 15) val autoDismissSeconds: Int = 15,
val enabledRideProfiles: Set<String> = emptySet())
val defaultReminders = Json.encodeToString(listOf(Reminder(0, "Drink", 30, text = "Take a sip!"))) val defaultReminders = Json.encodeToString(listOf(Reminder(0, "Drink", 30, text = "Take a sip!")))
fun reminderIsActive(reminder: Reminder, currentRideProfile: RideProfile?): Boolean {
val enabledRideProfiles = reminder.enabledRideProfiles.map { it.lowercase().trim() }
val currentProfileName = currentRideProfile?.name?.lowercase()?.trim()
return reminder.isActive && (currentRideProfile == null || enabledRideProfiles.isEmpty() || enabledRideProfiles.contains(currentProfileName))
}

View File

@ -22,7 +22,7 @@ compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "
[libraries] [libraries]
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
color = { module = "com.maxkeppeler.sheets-compose-dialogs:color", version.ref = "color" } color = { module = "com.maxkeppeler.sheets-compose-dialogs:color", version.ref = "color" }
hammerhead-karoo-ext = { group = "io.hammerhead", name = "karoo-ext", version = "1.1.1" } hammerhead-karoo-ext = { group = "io.hammerhead", name = "karoo-ext", version = "1.1.5" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCore" } androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCore" }