mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
feat: record the source app/device name from Health Connect
Instead of a generic "health_connect" source, capture the app/device that wrote the record (the Health Connect data origin) and use it as the workout source — so the card shows e.g. "SAMSUNG HEALTH" / "GOOGLE FIT" instead of "HEALTH CONNECT". resolveSourceName resolves the data-origin package to the installed app's label, falling back to a known-package map (Samsung Health, Google Fit, Fitbit, Garmin, Strava, Nike Run Club), then the raw package, then "Health Connect". DetectedWorkout carries this as `source` and it flows into the published kind 1301 source tag. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015qgqQKHSewRHVM8vSCLt9P
This commit is contained in:
+2
@@ -45,4 +45,6 @@ data class DetectedWorkout(
|
||||
val maxHeartRate: Int?,
|
||||
val steps: Int?,
|
||||
val elevationGainMeters: Double?,
|
||||
/** Human-readable name of the app/device that wrote the record (e.g. "Samsung Health"). */
|
||||
val source: String,
|
||||
)
|
||||
|
||||
+30
@@ -60,6 +60,20 @@ class HealthConnectManager(
|
||||
/** How far back the New Workout carousel looks for workouts to offer. */
|
||||
const val LOOKBACK_DAYS = 7L
|
||||
|
||||
private const val DEFAULT_SOURCE = "Health Connect"
|
||||
|
||||
/** Friendly names for well-known writers when their app isn't installed to read a label from. */
|
||||
private val KNOWN_SOURCES =
|
||||
mapOf(
|
||||
"com.sec.android.app.shealth" to "Samsung Health",
|
||||
"com.google.android.apps.fitness" to "Google Fit",
|
||||
"com.google.android.apps.healthdata" to "Health Connect",
|
||||
"com.fitbit.FitbitMobile" to "Fitbit",
|
||||
"com.garmin.android.apps.connectmobile" to "Garmin Connect",
|
||||
"com.strava" to "Strava",
|
||||
"com.nike.plusgps" to "Nike Run Club",
|
||||
)
|
||||
|
||||
/** Read permissions needed to map a workout. */
|
||||
val PERMISSIONS =
|
||||
setOf(
|
||||
@@ -168,9 +182,25 @@ class HealthConnectManager(
|
||||
maxHeartRate = totals?.get(HeartRateRecord.BPM_MAX)?.toInt(),
|
||||
steps = totals?.get(StepsRecord.COUNT_TOTAL)?.toInt(),
|
||||
elevationGainMeters = totals?.get(ElevationGainedRecord.ELEVATION_GAINED_TOTAL)?.inMeters,
|
||||
source = resolveSourceName(session.metadata.dataOrigin.packageName),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Friendly name of the app/device that wrote the record. Resolves the
|
||||
* Health Connect data-origin package to the installed app's label
|
||||
* ("Samsung Health", "Google Fit", …); falls back to a known-package map,
|
||||
* then the raw package, then "Health Connect" when nothing is available.
|
||||
*/
|
||||
private fun resolveSourceName(packageName: String): String {
|
||||
if (packageName.isBlank()) return DEFAULT_SOURCE
|
||||
runCatching {
|
||||
val pm = context.packageManager
|
||||
return pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0)).toString()
|
||||
}
|
||||
return KNOWN_SOURCES[packageName] ?: packageName
|
||||
}
|
||||
|
||||
/** Aggregates the optional metrics over the session window. Null if aggregation fails. */
|
||||
private suspend fun aggregate(session: ExerciseSessionRecord): AggregationResult? =
|
||||
try {
|
||||
|
||||
+1
-2
@@ -23,7 +23,6 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.workouts.suggestion
|
||||
import android.text.format.DateUtils
|
||||
import com.vitorpamplona.amethyst.service.workouts.health.DetectedWorkout
|
||||
import com.vitorpamplona.amethyst.ui.navigation.routes.Route
|
||||
import com.vitorpamplona.quartz.experimental.fitness.workout.tags.SourceTag
|
||||
|
||||
/** Builds the pre-filled composer route for a detected workout. Shared by the
|
||||
* feed suggestion banner and the New Workout carousel so they never drift. */
|
||||
@@ -39,7 +38,7 @@ internal fun DetectedWorkout.toNewWorkoutRoute(title: String) =
|
||||
steps = steps ?: 0,
|
||||
elevationGainMeters = elevationGainMeters ?: 0.0,
|
||||
startTime = startTimeEpochSeconds,
|
||||
source = SourceTag.HEALTH_CONNECT,
|
||||
source = source,
|
||||
)
|
||||
|
||||
internal fun formatWorkoutDuration(totalSeconds: Long): String {
|
||||
|
||||
Reference in New Issue
Block a user