diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettings.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettings.kt index fc453f5c29..a4bc260c99 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettings.kt @@ -53,6 +53,7 @@ data class UiSettings( val showProfileZapReceivedFeed: Boolean = true, val showProfileFollowersFeed: Boolean = true, val dontShowOnchainPublicWarning: Boolean = false, + val suggestWorkoutsFromHealthConnect: BooleanType = BooleanType.ALWAYS, ) enum class ThemeType( diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettingsFlow.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettingsFlow.kt index 146a6a8fc6..546cc50293 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettingsFlow.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/UiSettingsFlow.kt @@ -53,6 +53,7 @@ class UiSettingsFlow( val showProfileZapReceivedFeed: MutableStateFlow = MutableStateFlow(true), val showProfileFollowersFeed: MutableStateFlow = MutableStateFlow(true), val dontShowOnchainPublicWarning: MutableStateFlow = MutableStateFlow(false), + val suggestWorkoutsFromHealthConnect: MutableStateFlow = MutableStateFlow(BooleanType.ALWAYS), ) { val listOfFlows: List> = listOf>( @@ -80,6 +81,7 @@ class UiSettingsFlow( showProfileZapReceivedFeed, showProfileFollowersFeed, dontShowOnchainPublicWarning, + suggestWorkoutsFromHealthConnect, ) // emits at every change in any of the propertyes. @@ -111,6 +113,7 @@ class UiSettingsFlow( flows[21] as Boolean, flows[22] as Boolean, flows[23] as Boolean, + flows[24] as BooleanType, ) } @@ -140,6 +143,7 @@ class UiSettingsFlow( showProfileZapReceivedFeed.value, showProfileFollowersFeed.value, dontShowOnchainPublicWarning.value, + suggestWorkoutsFromHealthConnect.value, ) fun update(torSettings: UiSettings): Boolean { @@ -241,6 +245,10 @@ class UiSettingsFlow( dontShowOnchainPublicWarning.tryEmit(torSettings.dontShowOnchainPublicWarning) any = true } + if (suggestWorkoutsFromHealthConnect.value != torSettings.suggestWorkoutsFromHealthConnect) { + suggestWorkoutsFromHealthConnect.tryEmit(torSettings.suggestWorkoutsFromHealthConnect) + any = true + } return any } @@ -290,6 +298,7 @@ class UiSettingsFlow( MutableStateFlow(uiSettings.showProfileZapReceivedFeed), MutableStateFlow(uiSettings.showProfileFollowersFeed), MutableStateFlow(uiSettings.dontShowOnchainPublicWarning), + MutableStateFlow(uiSettings.suggestWorkoutsFromHealthConnect), ) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/preferences/UISharedPreferences.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/preferences/UISharedPreferences.kt index ebf0c03d44..77082c8411 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/preferences/UISharedPreferences.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/preferences/UISharedPreferences.kt @@ -117,6 +117,7 @@ class UiSharedPreferences( val UI_SHOW_PROFILE_ZAP_RECEIVED_FEED = booleanPreferencesKey("ui.show_profile_zap_received_feed") val UI_SHOW_PROFILE_FOLLOWERS_FEED = booleanPreferencesKey("ui.show_profile_followers_feed") val UI_DONT_SHOW_ONCHAIN_PUBLIC_WARNING = booleanPreferencesKey("ui.dont_show_onchain_public_warning") + val UI_SUGGEST_WORKOUTS_FROM_HEALTH_CONNECT = stringPreferencesKey("ui.suggest_workouts_from_health_connect") suspend fun uiPreferences(context: Context): UiSettings? = try { @@ -152,6 +153,8 @@ class UiSharedPreferences( showProfileZapReceivedFeed = preferences[UI_SHOW_PROFILE_ZAP_RECEIVED_FEED] ?: true, showProfileFollowersFeed = preferences[UI_SHOW_PROFILE_FOLLOWERS_FEED] ?: true, dontShowOnchainPublicWarning = preferences[UI_DONT_SHOW_ONCHAIN_PUBLIC_WARNING] ?: false, + suggestWorkoutsFromHealthConnect = + preferences[UI_SUGGEST_WORKOUTS_FROM_HEALTH_CONNECT]?.let { BooleanType.valueOf(it) } ?: BooleanType.ALWAYS, ) } catch (e: Exception) { if (e is CancellationException) throw e @@ -200,6 +203,7 @@ class UiSharedPreferences( preferences[UI_SHOW_PROFILE_ZAP_RECEIVED_FEED] = sharedSettings.showProfileZapReceivedFeed preferences[UI_SHOW_PROFILE_FOLLOWERS_FEED] = sharedSettings.showProfileFollowersFeed preferences[UI_DONT_SHOW_ONCHAIN_PUBLIC_WARNING] = sharedSettings.dontShowOnchainPublicWarning + preferences[UI_SUGGEST_WORKOUTS_FROM_HEALTH_CONNECT] = sharedSettings.suggestWorkoutsFromHealthConnect.name } } catch (e: Exception) { if (e is CancellationException) throw e diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/ComposeSettingsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/ComposeSettingsScreen.kt index 2e53282a81..aedab64c92 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/ComposeSettingsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/ComposeSettingsScreen.kt @@ -101,6 +101,13 @@ fun ComposeSettingsContent( description = R.string.tracked_broadcasts_setting_description, ) SettingsDivider() + BooleanSwitchTile( + flow = sharedPrefs.suggestWorkoutsFromHealthConnect, + icon = MaterialSymbols.DirectionsRun, + title = R.string.suggest_workouts_setting_title, + description = R.string.suggest_workouts_setting_description, + ) + SettingsDivider() AddClientTagTile(accountViewModel) } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/suggestion/WorkoutSuggestionCard.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/suggestion/WorkoutSuggestionCard.kt index 95b908da18..c26a3c02e3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/suggestion/WorkoutSuggestionCard.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/workouts/suggestion/WorkoutSuggestionCard.kt @@ -50,6 +50,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.commons.icons.symbols.Icon import com.vitorpamplona.amethyst.commons.icons.symbols.MaterialSymbols +import com.vitorpamplona.amethyst.model.BooleanType import com.vitorpamplona.amethyst.service.workouts.health.DetectedWorkout import com.vitorpamplona.amethyst.service.workouts.health.HealthConnectManager import com.vitorpamplona.amethyst.service.workouts.health.HealthConnectStore @@ -77,6 +78,10 @@ fun WorkoutSuggestions( val available = remember { HealthConnectManager.isAvailable(context) } if (!available) return + val suggestEnabled by accountViewModel.settings.uiSettingsFlow.suggestWorkoutsFromHealthConnect + .collectAsStateWithLifecycle() + if (suggestEnabled == BooleanType.NEVER) return + val pubkeyHex = accountViewModel.account.signer.pubKey val state = remember(pubkeyHex) { diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 87a6aa328a..57e336226d 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -3481,6 +3481,8 @@ Uses an on-device AI model to propose text corrections and tone changes. Tracked broadcasts Use the tracked broadcaster when sending events. Shows live progress and per-relay status while broadcasting. + Suggest workouts to share + Reads finished workouts from Health Connect (Samsung Health, Google Fit, Fitbit, Garmin…) and suggests sharing them as a post. Permission is requested only when you connect. Compose Settings Automatically create drafts Saves a draft event automatically when you type or leave a composer with unsent text and sends to your private outbox relays.