Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/app/src/main/java/com/noop/ai/AiCoach.kt
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ class AiCoach(private val repo: WhoopRepository) {

// Local servers (notably Ollama) stop with finish_reason "length" at the context-window edge
// and give NO error, keep the partial text and append the actionable notice so it isn't silent.
val truncated = firstChoice?.optString("finish_reason")?.lowercase() == "length"
val truncated = firstChoice.optString("finish_reason").lowercase() == "length"
return if (truncated) content + TRUNCATION_NOTE else content
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ data class CaffeineActiveEstimate(
mgSum += CaffeineDecay.remainingMg(it, hours, halfLifeHours)
anyMg = true
}
if (mostRecentActiveHours == null || hours < mostRecentActiveHours!!) {
if (mostRecentActiveHours == null || hours < mostRecentActiveHours) {
mostRecentActiveHours = hours
}
}
Expand Down
4 changes: 2 additions & 2 deletions android/app/src/main/java/com/noop/analytics/WatchRecovery.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ object WatchRecovery {
): Result {
// Build both baselines through the production model (Winsorized EWMA + cold-start gating), exactly
// as the strap path does. HRV feeds the HRV config; resting HR feeds the RHR config.
val hrvBase = Baselines.foldHistory(hrvHistory.map { it as Double? }, Baselines.hrvCfg)
val rhrBase = Baselines.foldHistory(rhrHistory.map { it as Double? }, Baselines.restingHRCfg)
val hrvBase = Baselines.foldHistory(hrvHistory, Baselines.hrvCfg)
val rhrBase = Baselines.foldHistory(rhrHistory, Baselines.restingHRCfg)

// Confidence is the SAME helper the strap Charge uses, so the calibrating -> building -> solid arc
// matches. It reads CALIBRATING whenever recovery would be null (no usable HRV baseline).
Expand Down
4 changes: 2 additions & 2 deletions android/app/src/main/java/com/noop/ble/Backfiller.kt
Original file line number Diff line number Diff line change
Expand Up @@ -362,12 +362,12 @@ class Backfiller(
for (f in frames) {
if (spo2Dumped >= com.noop.analytics.Spo2ReTrace.MAX_SAMPLES) break
val d = decodeHistorical(f, family) ?: continue
val unix = d["unix"] as? Int ?: continue
val recUnix = d["unix"] as? Int ?: continue
connectionLog(
com.noop.analytics.Spo2ReTrace.recordLine(
frame = f,
version = d["hist_version"] as? Int,
unix = unix,
unix = recUnix,
red = d["spo2_red"] as? Int,
ir = d["spo2_ir"] as? Int,
skinRaw = d["skin_temp_raw"] as? Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ class SourceCoordinator(
// The ring generation is carried on the row's model ("Oura Ring 3/4/5"); recover it so the
// transport clamps the MTU + picks the gen-appropriate live-HR enable command set. Defaults to
// gen3 if the model is missing/unrecognised (OuraRingGen.from).
val ringGen = OuraRingGen.from(row?.model ?: "")
val ringGen = OuraRingGen.from(row.model ?: "")
val source = OuraLiveSource(
context = ctx,
deviceId = id,
Expand Down
4 changes: 2 additions & 2 deletions android/app/src/main/java/com/noop/ble/StandardHrSource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,9 @@ class StandardHrSource(
// exposes — a footpod / bike speed-cadence sensor / power meter read ALONGSIDE HR. Separate
// services from HR; a device without them yields no characteristic and the HR path is untouched.
for ((svcUuid, charUuid) in FITNESS_SENSOR_CHARS) {
val ch = g.getService(svcUuid)?.getCharacteristic(charUuid) ?: continue
val sensorCh = g.getService(svcUuid)?.getCharacteristic(charUuid) ?: continue
log("HR-strap: fitness-sensor characteristic $charUuid found — enabling notifications")
enableFitnessNotify(g, ch)
enableFitnessNotify(g, sensorCh)
}
}

Expand Down
1 change: 0 additions & 1 deletion android/app/src/main/java/com/noop/data/DataBackup.kt
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,6 @@ object DataBackup {
StageResult.NOT_A_BACKUP -> return ImportResult.Failed(
"That file is not a NOOP backup - it doesn't look like a .noopbak archive or a SQLite database."
)
else -> error("unreachable stage result $staged")
}
} catch (e: IOException) {
tempSqlite.delete()
Expand Down
2 changes: 1 addition & 1 deletion android/app/src/main/java/com/noop/ingest/CsvParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ internal object WhoopTime {
else if (s.startsWith("-")) { sign = -1; s = s.substring(1) }

// Accept HH:MM or HHMM.
var hours = 0
var hours: Int
var minutes = 0
val colonIdx = s.indexOf(':')
if (colonIdx >= 0) {
Expand Down
10 changes: 5 additions & 5 deletions android/app/src/main/java/com/noop/ui/BreatheScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Air
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.filled.Bolt
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.GraphicEq
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Remove
import androidx.compose.material.icons.filled.Stop
import androidx.compose.material.icons.filled.VolumeOff
import androidx.compose.material.icons.filled.VolumeUp
import androidx.compose.material.icons.automirrored.filled.VolumeOff
import androidx.compose.material.icons.automirrored.filled.VolumeUp
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
Expand Down Expand Up @@ -570,7 +570,7 @@ fun BreatheScreen(viewModel: AppViewModel) {
private fun AudioCueToggle(checked: Boolean, onChange: (Boolean) -> Unit) {
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Icon(
if (checked) Icons.Filled.VolumeUp else Icons.Filled.VolumeOff,
if (checked) Icons.AutoMirrored.Filled.VolumeUp else Icons.AutoMirrored.Filled.VolumeOff,
contentDescription = null,
tint = if (checked) Palette.restBright else Palette.textTertiary,
modifier = Modifier.size(16.dp).padding(end = 10.dp),
Expand Down Expand Up @@ -1113,7 +1113,7 @@ private fun CalmMode(viewModel: AppViewModel, live: com.noop.ble.LiveState, bpm:
Row(verticalAlignment = Alignment.Bottom,
horizontalArrangement = Arrangement.spacedBy(10.dp)) {
Text(bpm?.toString() ?: "—", style = NoopType.number(48f), color = Palette.metricRose)
Icon(Icons.Filled.ArrowForward, contentDescription = null,
Icon(Icons.AutoMirrored.Filled.ArrowForward, contentDescription = null,
tint = Palette.textTertiary, modifier = Modifier.padding(bottom = 8.dp))
Column {
Text("target", style = NoopType.footnote, color = Palette.textTertiary)
Expand Down
4 changes: 2 additions & 2 deletions android/app/src/main/java/com/noop/ui/CoachScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Send
import androidx.compose.material.icons.automirrored.filled.Send
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu
Expand Down Expand Up @@ -747,7 +747,7 @@ private fun SendButton(enabled: Boolean, sending: Boolean, onClick: () -> Unit)
CircularProgressIndicator(modifier = Modifier.size(20.dp), strokeWidth = 2.dp, color = Palette.accent)
} else {
Icon(
Icons.Filled.Send,
Icons.AutoMirrored.Filled.Send,
contentDescription = null,
tint = if (enabled) Palette.surfaceBase else Palette.textTertiary,
modifier = Modifier.size(20.dp),
Expand Down
8 changes: 4 additions & 4 deletions android/app/src/main/java/com/noop/ui/DashboardCards.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import androidx.compose.material.icons.filled.Air
import androidx.compose.material.icons.filled.AutoAwesome
import androidx.compose.material.icons.filled.Bedtime
import androidx.compose.material.icons.filled.Bolt
import androidx.compose.material.icons.filled.DirectionsRun
import androidx.compose.material.icons.filled.DirectionsWalk
import androidx.compose.material.icons.automirrored.filled.DirectionsRun
import androidx.compose.material.icons.automirrored.filled.DirectionsWalk
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Hexagon
import androidx.compose.material.icons.filled.LocalFireDepartment
Expand Down Expand Up @@ -46,9 +46,9 @@ enum class DashboardCard(
HRV("hrv", "HRV", "Heart-rate variability", "ms", Icons.Filled.MonitorHeart),
RESTING_HR("restingHr", "Resting HR", "Resting heart rate", "bpm", Icons.Filled.Favorite),
RESPIRATORY("respiratory", "Respiratory", "Breaths per minute", "rpm", Icons.Filled.Air),
STEPS("steps", "Steps", "Today", "", Icons.Filled.DirectionsWalk),
STEPS("steps", "Steps", "Today", "", Icons.AutoMirrored.Filled.DirectionsWalk),
STRESS("stress", "Stress", "Autonomic load", "", Icons.Filled.Bolt),
FITNESS_AGE("fitnessAge", "Fitness Age", "Updated weekly", "yrs", Icons.Filled.DirectionsRun),
FITNESS_AGE("fitnessAge", "Fitness Age", "Updated weekly", "yrs", Icons.AutoMirrored.Filled.DirectionsRun),
VITALITY("vitality", "Vitality", "Wellness score", "", Icons.Filled.AutoAwesome),
BLOOD_OXYGEN("bloodOxygen", "Blood Oxygen", "Blood oxygen", "", Icons.Filled.WaterDrop),
SKIN_TEMP("skinTemp", "Skin Temp", "Skin temperature", "", Icons.Filled.Thermostat),
Expand Down
6 changes: 3 additions & 3 deletions android/app/src/main/java/com/noop/ui/HealthScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.CompareArrows
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.MenuBook
import androidx.compose.material.icons.automirrored.filled.MenuBook
import androidx.compose.material.icons.filled.Sync
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
Expand Down Expand Up @@ -329,7 +329,7 @@ private fun RecordsAndSourcesSection(
Column(verticalArrangement = Arrangement.spacedBy(Metrics.gap)) {
SectionHeader("Records & sources", overline = "On this phone")
RecordRow(
icon = Icons.Filled.MenuBook,
icon = Icons.AutoMirrored.Filled.MenuBook,
tint = Palette.metricCyan,
title = "Lab Book",
subtitle = "Your bloods, BP and body numbers. Kept private here.",
Expand Down Expand Up @@ -475,7 +475,7 @@ private fun HealthContributorsSection(day: DailyMetric?) {
if (hrv == null && rhr == null && sleepMin == null && resp == null) return

// SOLID once recovery has been scored from these signals; CALIBRATING while the baseline seeds.
val solid = day?.recovery != null
val solid = day.recovery != null
Column(verticalArrangement = Arrangement.spacedBy(Metrics.gap)) {
Row(verticalAlignment = Alignment.CenterVertically) {
Box(modifier = Modifier.weight(1f)) {
Expand Down
2 changes: 1 addition & 1 deletion android/app/src/main/java/com/noop/ui/HrvSnapshotScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ fun HrvSnapshotScreen(
if (phase == HrvPhase.Done && r != null && r.rmssd != null) {
OutlinedButton(
onClick = {
val rmssd = r.rmssd ?: return@OutlinedButton
val rmssd = r.rmssd
val day = hrvDayKey(Date())
val row = MetricSeriesRow(
deviceId = HRV_SNAPSHOT_SOURCE_ID,
Expand Down
4 changes: 2 additions & 2 deletions android/app/src/main/java/com/noop/ui/LabBookScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.FileUpload
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.MenuBook
import androidx.compose.material.icons.automirrored.filled.MenuBook
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
Expand Down Expand Up @@ -175,7 +175,7 @@ fun LabBookScreen(vm: AppViewModel) {
.background(Palette.metricCyan.copy(alpha = 0.14f)),
contentAlignment = Alignment.Center,
) {
Icon(Icons.Filled.MenuBook, contentDescription = null, tint = Palette.metricCyan, modifier = Modifier.size(16.dp))
Icon(Icons.AutoMirrored.Filled.MenuBook, contentDescription = null, tint = Palette.metricCyan, modifier = Modifier.size(16.dp))
}
Column(modifier = Modifier.weight(1f)) {
Text(countLine(markers), style = NoopType.headline, color = Palette.textPrimary)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CalendarMonth
import androidx.compose.material.icons.filled.Call
import androidx.compose.material.icons.filled.Chat
import androidx.compose.material.icons.automirrored.filled.Chat
import androidx.compose.material.icons.filled.Email
import androidx.compose.material.icons.filled.GraphicEq
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.NotificationsActive
import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material.icons.automirrored.filled.OpenInNew
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Tune
import androidx.compose.material.icons.filled.Videocam
Expand Down Expand Up @@ -97,7 +97,7 @@ internal enum class NotifCategory(
val defaultPattern: BuzzPattern,
) {
Email("Email", Icons.Filled.Email, BuzzPattern.Double),
Messaging("Messaging", Icons.Filled.Chat, BuzzPattern.Single),
Messaging("Messaging", Icons.AutoMirrored.Filled.Chat, BuzzPattern.Single),
Meetings("Meetings", Icons.Filled.Videocam, BuzzPattern.Triple),
Calendar("Calendar & Reminders", Icons.Filled.CalendarMonth, BuzzPattern.Double),
}
Expand All @@ -118,14 +118,14 @@ internal data class NotifApp(
private val notifCatalog: List<NotifApp> = listOf(
NotifApp("com.google.android.gm", "Gmail", NotifCategory.Email, Icons.Filled.Email),
NotifApp("com.microsoft.office.outlook", "Outlook", NotifCategory.Email, Icons.Filled.Email),
NotifApp("com.whatsapp", "WhatsApp", NotifCategory.Messaging, Icons.Filled.Chat),
NotifApp("com.google.android.apps.messaging", "Messages", NotifCategory.Messaging, Icons.Filled.Chat),
NotifApp("com.Slack", "Slack", NotifCategory.Messaging, Icons.Filled.Chat),
NotifApp("org.telegram.messenger", "Telegram", NotifCategory.Messaging, Icons.Filled.Chat),
NotifApp("com.whatsapp", "WhatsApp", NotifCategory.Messaging, Icons.AutoMirrored.Filled.Chat),
NotifApp("com.google.android.apps.messaging", "Messages", NotifCategory.Messaging, Icons.AutoMirrored.Filled.Chat),
NotifApp("com.Slack", "Slack", NotifCategory.Messaging, Icons.AutoMirrored.Filled.Chat),
NotifApp("org.telegram.messenger", "Telegram", NotifCategory.Messaging, Icons.AutoMirrored.Filled.Chat),
// Teams' ringing-call notifications are handled by the Calls card below (VoIP path). This
// per-app row covers everything else Teams sends to the shade (chats, @-mentions, channel
// posts), which read as messages, so it lives under Messaging with the chat glyph.
NotifApp("com.microsoft.teams", "Microsoft Teams", NotifCategory.Messaging, Icons.Filled.Chat),
NotifApp("com.microsoft.teams", "Microsoft Teams", NotifCategory.Messaging, Icons.AutoMirrored.Filled.Chat),
NotifApp("us.zoom.videomeetings", "Zoom", NotifCategory.Meetings, Icons.Filled.Videocam),
NotifApp("com.google.android.calendar", "Calendar", NotifCategory.Calendar, Icons.Filled.CalendarMonth),
)
Expand Down Expand Up @@ -614,7 +614,7 @@ private fun DeliveryNote() {
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
Icon(
Icons.Filled.OpenInNew,
Icons.AutoMirrored.Filled.OpenInNew,
contentDescription = null,
tint = Palette.accent,
modifier = Modifier.size(14.dp),
Expand Down
4 changes: 2 additions & 2 deletions android/app/src/main/java/com/noop/ui/SettingsScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.IosShare
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.MenuBook
import androidx.compose.material.icons.automirrored.filled.MenuBook
import androidx.compose.material.icons.filled.Palette
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.SaveAlt
Expand Down Expand Up @@ -2276,7 +2276,7 @@ fun SettingsScreen(
horizontalArrangement = Arrangement.spacedBy(10.dp),
) {
Icon(
Icons.Filled.MenuBook,
Icons.AutoMirrored.Filled.MenuBook,
contentDescription = null,
tint = Palette.accent,
modifier = Modifier.size(18.dp),
Expand Down
6 changes: 3 additions & 3 deletions android/app/src/main/java/com/noop/ui/SleepScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -499,15 +499,15 @@ fun SleepScreen(
val now = System.currentTimeMillis() / 1000L
// Same active∪canonical union as the main loader (#814/#1008), so the
// post-nap reload can't snap the browse back to a canonical-only night set.
val imported = vm.repo.sleepSessionsUnion(vm.activeStrapId, 0L, now)
val importedSessions = vm.repo.sleepSessionsUnion(vm.activeStrapId, 0L, now)
val computed = vm.repo.computedSleepSessionsUnion(vm.activeStrapId, 0L, now)
fun localEndDay(ts: Long): String {
val offsetSec = (java.util.TimeZone.getDefault().getOffset(ts * 1000) / 1000).toLong()
return AnalyticsEngine.dayString(ts, offsetSec)
}
val importedDays = imported.map { localEndDay(it.endTs) }.toHashSet()
val importedDays = importedSessions.map { localEndDay(it.endTs) }.toHashSet()
val computedOnly = computed.filter { localEndDay(it.endTs) !in importedDays }
(imported + computedOnly).sortedBy { it.effectiveStartTs }
(importedSessions + computedOnly).sortedBy { it.effectiveStartTs }
}.getOrDefault(sleeps)
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import androidx.compose.foundation.verticalScroll
import com.noop.data.WhoopRepository
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.DirectionsWalk
import androidx.compose.material.icons.automirrored.filled.DirectionsWalk
import androidx.compose.material.icons.filled.SyncProblem
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
Expand Down Expand Up @@ -241,7 +241,7 @@ private fun ExplainerCard() {
NoopCard(padding = 20.dp) {
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Icon(Icons.Filled.DirectionsWalk, contentDescription = null, tint = Palette.accent, modifier = Modifier.size(20.dp))
Icon(Icons.AutoMirrored.Filled.DirectionsWalk, contentDescription = null, tint = Palette.accent, modifier = Modifier.size(20.dp))
Text("How this works", style = NoopType.headline, color = Palette.textPrimary)
}
Text(
Expand Down
Loading
Loading