Etapp 5: stegsynk via Health Connect
All checks were successful
release / build-release (push) Successful in 4m30s
All checks were successful
release / build-release (push) Successful in 4m30s
- StepsSync: läser dagens + gårdagens stegsumma (StepsRecord.COUNT_TOTAL) och upsertar till gym-API:t; synkas automatiskt (max var 10:e min) när hemskärmen visas - Inställningar → Stegsynk: status, aktivera-flöde (HC-permission), Play-länk om Health Connect saknas, manuell synk - Kcal-mätarens telefonrad visar nu riktiga steg + kcal (serverns beräkning med dubbelräkningsskydd); stegmål får riktig data - connect-client 1.1.0-alpha07 (nyare kräver AGP 8.9/compileSdk 36 — toolchain-bump tas separat); HC-manifest (READ_STEPS, rationale-intents) - Version 0.10.0 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01C2GA94E1f3cmrvdQLQhYdg
This commit is contained in:
80
app/src/main/java/eu/brassepc/fitnessdroid/data/StepsSync.kt
Normal file
80
app/src/main/java/eu/brassepc/fitnessdroid/data/StepsSync.kt
Normal file
@@ -0,0 +1,80 @@
|
||||
package eu.brassepc.fitnessdroid.data
|
||||
|
||||
import android.content.Context
|
||||
import androidx.health.connect.client.HealthConnectClient
|
||||
import androidx.health.connect.client.permission.HealthPermission
|
||||
import androidx.health.connect.client.records.StepsRecord
|
||||
import androidx.health.connect.client.request.AggregateRequest
|
||||
import androidx.health.connect.client.time.TimeRangeFilter
|
||||
import java.time.Instant
|
||||
import java.time.LocalDate
|
||||
import java.time.ZoneId
|
||||
|
||||
/**
|
||||
* Steg via Health Connect: läser dagens + gårdagens stegsumma (som andra
|
||||
* appar/telefonen loggat) och synkar upp till gym-API:t (upsertDailySteps).
|
||||
* Servern räknar sedan in stegen i kcal-mätarens telefondel och stegmålen.
|
||||
*/
|
||||
class StepsSync(
|
||||
private val context: Context,
|
||||
private val gymApi: GymApi,
|
||||
) {
|
||||
val stepsPermission: String = HealthPermission.getReadPermission(StepsRecord::class)
|
||||
|
||||
private var lastSyncMs = 0L
|
||||
|
||||
/** SDK_UNAVAILABLE / SDK_UNAVAILABLE_PROVIDER_UPDATE_REQUIRED / SDK_AVAILABLE */
|
||||
fun sdkStatus(): Int = HealthConnectClient.getSdkStatus(context)
|
||||
|
||||
fun isAvailable(): Boolean = sdkStatus() == HealthConnectClient.SDK_AVAILABLE
|
||||
|
||||
suspend fun hasPermission(): Boolean {
|
||||
if (!isAvailable()) return false
|
||||
return runCatching {
|
||||
HealthConnectClient.getOrCreate(context)
|
||||
.permissionController.getGrantedPermissions()
|
||||
.contains(stepsPermission)
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Läs och synka. Returnerar dagens steg, eller null om HC saknas /
|
||||
* behörighet inte getts. Fel sväljs (nästa synk försöker igen).
|
||||
*/
|
||||
suspend fun syncNow(): Int? {
|
||||
if (!hasPermission()) return null
|
||||
val client = HealthConnectClient.getOrCreate(context)
|
||||
val zone = ZoneId.systemDefault()
|
||||
val today = LocalDate.now()
|
||||
var todaySteps: Int? = null
|
||||
|
||||
for (day in listOf(today.minusDays(1), today)) {
|
||||
val startInstant = day.atStartOfDay(zone).toInstant()
|
||||
val endInstant = if (day == today) Instant.now()
|
||||
else day.plusDays(1).atStartOfDay(zone).toInstant()
|
||||
|
||||
val steps = runCatching {
|
||||
val result = client.aggregate(
|
||||
AggregateRequest(
|
||||
metrics = setOf(StepsRecord.COUNT_TOTAL),
|
||||
timeRangeFilter = TimeRangeFilter.between(startInstant, endInstant),
|
||||
)
|
||||
)
|
||||
(result[StepsRecord.COUNT_TOTAL] ?: 0L).toInt()
|
||||
}.getOrNull() ?: continue
|
||||
|
||||
if (day == today) todaySteps = steps
|
||||
runCatching {
|
||||
gymApi.upsertDailySteps(startInstant.toString(), steps)
|
||||
}
|
||||
}
|
||||
lastSyncMs = System.currentTimeMillis()
|
||||
return todaySteps
|
||||
}
|
||||
|
||||
/** Synka högst var 10:e minut — anropas t.ex. när hemskärmen visas. */
|
||||
suspend fun syncThrottled() {
|
||||
if (System.currentTimeMillis() - lastSyncMs < 10 * 60_000) return
|
||||
syncNow()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user