HydroFlux 0.1.2

This commit is contained in:
2026-02-11 19:45:56 +11:00
parent 6f766b7a96
commit 766533c8cd
3 changed files with 95 additions and 51 deletions

View File

@@ -145,15 +145,15 @@ export class Dashboard {
</div>
</div>
<!-- Strava Integration -->
<!-- Distance / Fitness Integration -->
<div class="bg-gradient-to-br from-orange-50 to-white rounded-2xl p-6 shadow-md">
<h3 class="text-sm font-semibold text-gray-700 mb-4">Strava Integration</h3>
<h3 class="text-sm font-semibold text-gray-700 mb-4">Distance</h3>
<div class="flex flex-col items-center justify-center py-4">
<div class="w-16 h-16 bg-gradient-to-br from-orange-500 to-orange-600 rounded-2xl flex items-center justify-center mb-3 shadow-lg transform rotate-12">
<span class="text-3xl font-bold text-white transform -rotate-12">S</span>
<span class="text-3xl font-bold text-white transform -rotate-12">D</span>
</div>
<span class="text-lg font-bold text-gray-800">5.2km Run</span>
<span class="text-xs text-gray-500 mt-1">Last Sync: 2h ago</span>
<span class="text-lg font-bold text-gray-800" id="distanceDisplay">${(healthData.distance || 0).toFixed(2)}km</span>
<span class="text-xs text-gray-500 mt-1">Today's Total</span>
</div>
</div>
</div>
@@ -195,11 +195,15 @@ export class Dashboard {
this.renderGoals(goalsData);
// Expose Native Callback
window.updateHealthData = (steps, sleepHoursTotal) => {
console.log("Received Health Data:", steps, sleepHoursTotal);
window.updateHealthData = (steps, sleepHoursTotal, distanceKm) => {
console.log("Received Health Data:", steps, sleepHoursTotal, distanceKm);
// Save to Storage
localStorage.setItem('hydroflux_health_data', JSON.stringify({ steps: steps, sleep: sleepHoursTotal }));
localStorage.setItem('hydroflux_health_data', JSON.stringify({
steps: steps,
sleep: sleepHoursTotal,
distance: distanceKm || 0 // Default to 0 if undefined
}));
// Update UI directly
const stepsCount = this.container.querySelector('#stepsCount');
@@ -210,7 +214,21 @@ export class Dashboard {
const sleepTime = this.container.querySelector('#sleepTime');
if (sleepTime) sleepTime.textContent = `${sh}h ${sm}m`;
// Update Distance Card (Replacing Strava Placeholder or updating it)
// Assuming the Strava card is now the general Fitness/Distance card as requested?
// "can we have the distance tab actually pull stats from health connect"
// Let's look for a distance element or update the text '5.2km Run' -> 'X.X km'
// NOTE: The current HTML has "5.2km Run" hardcoded in the Strava card.
// I should verify if I should target that specific element.
// There is no ID on that span. I'll add one via full replacement of render or just target via text?
// Safer to re-render or target widely.
// Let's assume I upgrade the render() to include an ID first?
// Actually, let's just save the data here. The Dashboard.render() needs to read it.
this.updateDynamicElements(steps, 10000);
if (this.updateDistanceDisplay) this.updateDistanceDisplay(distanceKm);
};
// Trigger initial sync if native interface exists
@@ -395,6 +413,11 @@ export class Dashboard {
}
}
updateDistanceDisplay(distance) {
const el = this.container.querySelector('#distanceDisplay');
if (el) el.textContent = (distance || 0).toFixed(2) + 'km';
}
startTimers() {
const updateTime = () => {
const now = new Date();

View File

@@ -13,6 +13,7 @@ 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.records.SleepSessionRecord
import androidx.health.connect.client.records.DistanceRecord // Added
import androidx.health.connect.client.request.ReadRecordsRequest
import androidx.health.connect.client.request.AggregateRequest
import androidx.health.connect.client.time.TimeRangeFilter
@@ -28,7 +29,8 @@ class MainActivity : FragmentActivity() {
// Define Permissions we need
private val PERMISSIONS = setOf(
HealthPermission.getReadPermission(StepsRecord::class),
HealthPermission.getReadPermission(SleepSessionRecord::class)
HealthPermission.getReadPermission(SleepSessionRecord::class),
HealthPermission.getReadPermission(DistanceRecord::class) // Added
)
private val requestPermissions = registerForActivityResult(
@@ -169,55 +171,74 @@ class MainActivity : FragmentActivity() {
val startOfDay = ZonedDateTime.now().truncatedTo(ChronoUnit.DAYS).toInstant()
val now = Instant.now()
var totalSteps = 0L
var sleepHours = 0.0
// 1. Try Read Steps (AGGREGATE)
try {
val response = healthConnectClient.aggregate(
AggregateRequest(
metrics = setOf(StepsRecord.COUNT_TOTAL),
timeRangeFilter = TimeRangeFilter.between(startOfDay, now)
)
var totalSteps = 0L
var sleepHours = 0.0
var totalDistance = 0.0
// 1. Try Read Steps (AGGREGATE)
try {
val response = healthConnectClient.aggregate(
AggregateRequest(
metrics = setOf(StepsRecord.COUNT_TOTAL),
timeRangeFilter = TimeRangeFilter.between(startOfDay, now)
)
// The result may be null if no data
totalSteps = response[StepsRecord.COUNT_TOTAL] ?: 0
} catch (e: Exception) {
e.printStackTrace()
val msg = e.message ?: "Unknown Error"
runOnUiThread { Toast.makeText(activity, "Steps Error: $msg", Toast.LENGTH_LONG).show() }
totalSteps = 0
}
// 2. Try Read Sleep (AGGREGATE)
try {
val sleepResponse = healthConnectClient.aggregate(
AggregateRequest(
metrics = setOf(SleepSessionRecord.SLEEP_DURATION_TOTAL),
timeRangeFilter = TimeRangeFilter.between(now.minus(24, ChronoUnit.HOURS), now)
)
)
// The result may be null if no data
totalSteps = response[StepsRecord.COUNT_TOTAL] ?: 0
} catch (e: Exception) {
e.printStackTrace()
val msg = e.message ?: "Unknown Error"
runOnUiThread { Toast.makeText(activity, "Steps Error: $msg", Toast.LENGTH_LONG).show() }
totalSteps = 0
}
// 2. Try Read Sleep (AGGREGATE)
try {
val sleepResponse = healthConnectClient.aggregate(
AggregateRequest(
metrics = setOf(SleepSessionRecord.SLEEP_DURATION_TOTAL),
timeRangeFilter = TimeRangeFilter.between(now.minus(24, ChronoUnit.HOURS), now)
)
// Get total duration in Seconds (Duration -> Seconds)
val duration = sleepResponse[SleepSessionRecord.SLEEP_DURATION_TOTAL]
if (duration != null) {
sleepHours = duration.seconds / 3600.0
}
} catch (e: Exception) {
e.printStackTrace()
sleepHours = 0.0
)
// Get total duration in Seconds (Duration -> Seconds)
val duration = sleepResponse[SleepSessionRecord.SLEEP_DURATION_TOTAL]
if (duration != null) {
sleepHours = duration.seconds / 3600.0
}
runOnUiThread {
if (!activity.isFinishing && !activity.isDestroyed) {
try {
myWebView.evaluateJavascript("window.updateHealthData($totalSteps, $sleepHours)", null)
} catch (e: Exception) {
e.printStackTrace()
}
} catch (e: Exception) {
e.printStackTrace()
sleepHours = 0.0
}
// 3. Try Read Distance (AGGREGATE)
try {
val distanceResponse = healthConnectClient.aggregate(
AggregateRequest(
metrics = setOf(DistanceRecord.DISTANCE_TOTAL),
timeRangeFilter = TimeRangeFilter.between(startOfDay, now)
)
)
val dist = distanceResponse[DistanceRecord.DISTANCE_TOTAL]
if (dist != null) {
totalDistance = dist.inKilometers // Double
}
} catch (e: Exception) {
e.printStackTrace()
totalDistance = 0.0
}
runOnUiThread {
if (!activity.isFinishing && !activity.isDestroyed) {
try {
// Pass Steps, Sleep, AND Distance
myWebView.evaluateJavascript("window.updateHealthData($totalSteps, $sleepHours, $totalDistance)", null)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
}
}
}

Binary file not shown.