Conversation
The in-app update checkers (macOS + Android), AltStore/SideStore source metadata, bug-report links, and install/README docs all still pointed at ParthJadhav/noop. Retarget them at MatB57/noop so future releases published here are the ones this fork's users (and their sideloaded devices) actually discover and auto-update to. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
French was ~15% complete on the main catalog (458/2988 keys) and partial elsewhere. Fills in all remaining keys across the four Xcode string catalogs (Strand, NOOPWatch, NOOPWatchComplications, StrandDesign) and adds the missing android/values-fr/strings.xml, matching the established "vous"-register terminology already used in the existing French strings (Récupération, Effort, VFC, Bracelet, Sources de données, etc.). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…897f7b Claude/whoop app fork improvements 897f7b
Version bump only, to trigger a clean CI build/publish of the update- redirect and French-localization changes for MatB57/noop, and to give the family's devices a distinct version to update to. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TestReportLinkTest.kt and TestReportLinkTests.swift still asserted the old ParthJadhav/noop issue URL, which broke Android CI's unit tests after the source was retargeted to MatB57/noop. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The first real release built and published from this fork. Prepends the 8.2.1 entry (keeps 8.2.0 for AltStore's version history) and fixes the remaining ParthJadhav source-attribution string. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Version-only bump to verify the in-app "Check for updates" flow works end-to-end for both a currently-installed app and a freshly-checking one. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
jaedonvs
pushed a commit
to jaedonvs/noop
that referenced
this pull request
Aug 15, 2026
…+ Android single-slot arbiter (ParthJadhav#5) ParthJadhav#4/ParthJadhav#6 (iOS): the iOS smart alarm only armed the strap firmware buzz and posted a local notification AFTER the strap reported it fired, so a failed strap buzz or a suspended phone past day one meant NO wake at all. applySmartAlarm() now also schedules a REPEATING daily UNCalendarNotificationTrigger (the WindDownNudge idiom: lives in the notification center, survives relaunch) at the wake time per selected weekday, gated on the wrist-alerts master + notification auth, replaced on every re-arm and cancelled on disarm. HONEST: it is a best-effort backup, not a guaranteed loud alarm (a sideloaded build has no critical-alert entitlement, so Focus/silent can still mute it) , the honesty card now says so and still tells users to keep their Clock alarm. ParthJadhav#5 (Android): "Buzz WHOOP 4/5" and the "Strap wake-alarm" both drove the single firmware-alarm slot with no coordination, so toggling one off ran disableStrapAlarm() and killed the other. Introduced one reconcileStrapAlarm() as the SOLE caller of arm/disable: it computes both requested times and arms the slot to the EARLIEST when either is on, else disarms. All six call sites routed through it. Test: both on, turn one off, slot stays armed to the other.
jaedonvs
pushed a commit
to jaedonvs/noop
that referenced
this pull request
Aug 15, 2026
iOS Backup & Sync: mirror Android adjustable keep-count (default 7)
jaedonvs
pushed a commit
to jaedonvs/noop
that referenced
this pull request
Aug 15, 2026
fitnessReadyLead() returns an already-localized String (via String(localized:)), but ReadinessChecklistCard.lead was typed LocalizedStringKey?, which SwiftUI treats as a lookup key rather than literal text. Retype to String? — Text(_:) accepts both, and this is the correct type for pre-resolved copy. Pre-existing break from commit 75a096a (PR #81), unrelated to this branch's localization change; fixing here so PR ParthJadhav#5's CI build passes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AndroidDiagnostics already flags "NOT exempt from battery optimisation" as the #1 cause of missed overnight background BLE, but nothing ever asked the user to fix it. Without the exemption Android freezes the foreground service during Doze: the strap link drops overnight, no R-R/HRV is banked, and the recovery baseline never leaves "calibrating" — on a WHOOP 4 or 5. - Add REQUEST_IGNORE_BATTERY_OPTIMIZATIONS to the manifest (Play permits it for a continuous companion-device link). - New BatteryOptimization helper: isExempt() + the request / settings intents. - Settings → "Keep connected in the background": an amber warning row with an "Allow" button, shown only while background connection is on and the exemption isn't granted. Android-only; no iOS/macOS counterpart. Fixes the likely root cause of "calibrating for weeks" on Android. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The sideloaded APK had no real update path — "Check for updates" only opened the GitHub release page in a browser, which a non-collaborator (e.g. a parent on a shared build) can't act on easily. - UpdateCheck now also returns the release's .apk asset URL + size. - InAppUpdate: download the APK into cache/updates/, hand it to the system package installer via the existing FileProvider; routes through the per-app "install unknown apps" grant on first use; browser fallback on any failure. - UpdateGate: a throttled (<=1/6h) launch check from NoopRoot that pops a dialog when a newer version is available, with one-tap "Update" (download + install) or "Later" (snoozes that version). Onboarded users only. - Settings "Check for updates" now installs in-app too instead of opening a browser. - Manifest: REQUEST_INSTALL_PACKAGES. file_paths: cache/updates/. Android-only; iOS gets one-tap updates from its AltStore/SideStore source. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This reverts commit 2f708bc.
This reverts commit 5fe0e9b.
Two fixes for the sideloaded Android build (redo of the reverted pair — the first attempt used the unimported M3 `Button`; this uses `NoopButton`). In-app update (so a non-collaborator parent doesn't have to hunt a GitHub asset in a browser): - UpdateCheck also returns the release's .apk asset URL + size. - InAppUpdate: download the APK into cache/updates/, hand it to the system package installer via the existing FileProvider; routes through the per-app "install unknown apps" grant on first use; browser fallback on any error. - UpdateGate: a throttled (<=1/6h) launch check from NoopRoot that pops a dialog when a newer version is available — one-tap "Update" (download + install) or "Later" (snoozes that version). Onboarded users only. - Settings "Check for updates" now installs in-app instead of opening a browser. - Manifest: REQUEST_INSTALL_PACKAGES. file_paths: cache/updates/. Battery-optimisation exemption (likely root cause of "calibrating for weeks"): - Manifest: REQUEST_IGNORE_BATTERY_OPTIMIZATIONS. - BatteryOptimization helper: isExempt() + the request / settings intents. - Settings → background connection: an amber warning row with an "Allow" button, shown only while background connection is on and the exemption isn't granted. Android-only; iOS gets updates from its AltStore/SideStore source and needs no power-exemption for CoreBluetooth background delivery. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Ships the Android in-app update prompt + the Doze battery-optimisation exemption. Cut as its own release so installs on 8.2.2 detect it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Prepend the 8.2.3 entry (real IPA size from the published release) and point the legacy top-level fields at it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Each release run signed with a fresh ephemeral debug key, so Android refused to update in place and forced an uninstall (wiping onboarding + profile + local DB). release.yml now decodes a keystore from the ANDROID_KEYSTORE_B64 secret (+ password/alias secrets) and signs with it, so every release shares one key and in-app updates install over the top. Falls back to the debug key with a loud warning when the secret is unset. Keystore never enters git. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Android: a backup restore / data import used to leave the app showing stale "calibrating" / "No Data" until the user force-quit and reopened. It now relaunches the process automatically once the DB is swapped (AppRestart). - Support / About: the contact row (macOS/iOS SupportView + Android Settings) now opens this fork's GitHub — code, releases, issues — instead of the upstream project's email. The "Built on" attribution credits are unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Groundwork for translating the Android UI without a mass strings.xml
extraction: Fr.load() parses the bundled EN->FR dictionary (the same pairs
the iOS String Catalog ships) once at startup when the device language is
French, and tr("English literal") swaps in the French rendering at the call
site — falling through to English for anything unknown or format-bearing, so
it is always safe. No call sites yet; wrapping the screens follows.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Source had newer UI copy than the last catalog extraction (Live Sessions, Smart Alarm explainers, Scoring Guide, Trends report legend, sleep-move warnings, a few labels/buttons), so those showed in English on a French device. Added as manual entries with fr translations. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SUPPORT.md and the issue-template config linked the upstream project's Discord / subreddit / wiki / contact email, none of which this family fork runs. Now they point at this repo's issues + README. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Mechanical pass over ~50 Compose screens: every user-facing English string
literal (Text, contentDescription, titles/subtitles, button labels, helper
copy, concatenation fragments) is now tr("…"), which returns the string
unchanged unless the device language is French. Behaviour-neutral on an
English device. French renderings for ~1000 of these already come from the
shared iOS dictionary; the rest are being translated next.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Translated the 885 tr() strings not already covered by the shared iOS dictionary (settings, device pairing, data sources, sleep/health/vitals, trends/insights, workouts, live sessions, onboarding). Every distinct tr() call site on the ~50 wrapped screens now resolves to French on a French device; English devices are unaffected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
French localisation: iOS/macOS catalog gap-filled, and the ~50 Android Compose screens now render in French on a French device. Plus the stable release-signing key wiring, in-app-update polish, auto-reload after a restore, and Support pointing at this fork. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
An update that turns into a forced uninstall (signature mismatch, or any other install hiccup) wipes the app-private database. AutoBackup.beforeUpdate now writes a fresh .noopbak to the public Downloads folder (survives an uninstall, unlike app-private storage) right before the APK download starts, from both the launch-time UpdateGate prompt and the Settings "Update" button. Best-effort and silent: any failure is swallowed and never blocks or delays the update. API 29+ only (no extra permission needed there); skipped on older devices rather than requesting one just for this. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds the pre-update auto-backup to Downloads. This is the first release signed with the stable keystore (repo secrets), so a device on 8.2.4 or earlier needs one last manual uninstall/reinstall; every release after this one installs in place. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Pass 1 wrapped literal Text("English") call-site arguments. It systematically
missed English returned from a when/if-based computed property or function —
DashboardCard.title/subtitle, ScoreState/RecordingState title+detail, sport
display names, sync-state labels, and (a much bigger class) full sentences
generated in com.noop.analytics (WeeklyDigest, ReadinessEngine,
RecoveryDrivers "What shaped Charge", IllnessSignalEngine, FitnessAgeEngine,
CyclePhaseEngine, VitalityEngine) that the UI only displays verbatim.
- TodayScreen.kt: Charge/Effort/Rest dashboard-card labels, the Synthesis
sheet (Steady/Depleted/Primed/Peak, "Charge is low/steady/strong"),
calibrating notes, sync-state chip, "Start session", HR chart subtitle,
"Synced from:" footer, Key Metrics editor. Onboarding gets a "Skip" button
(top bar) so re-onboarding after a reinstall doesn't force the full flow.
- WorkoutsScreen/WorkoutEditing/DevicesScreen/AddDeviceWizard/LiveWorkoutScreen:
sport display names (fixes the "Walking, Walking, Walking" workout list),
device pairing/sync labels.
- SleepScreen: remaining labels (duration subtitle, ledger paragraph, time
pickers) + a layout fix for French labels overflowing/wrapping onto their
own line in fixed-width stat tiles (SparkTile, StageBreakdownRow,
NapSummaryCell, ChartFooter).
- TrendsScreen/TrendsExploreScreen/WeeklyDigestCard/HealthScreen/
SkinTempCardsScreen/StressScreen: chart-legend words (Avg/Peak/Low/Days),
"Trailing N days", vitals detail stats, illness/cycle confidence lines.
- TrendsReport.kt: the whole file was missed by pass 1 entirely (raw
Canvas.drawText PDF renderer, not Compose Text()) — title, section
headers, AVG/MIN/MAX/DAYS legend, footer legend, empty state, the export
sheet, share-sheet strings.
- analytics/*.kt (7 files): added `Fr.tr(...)` (the i18n bridge from a
different package) around every static text fragment in the insight/
explanation sentence generators, preserving all interpolated numbers/
formatting exactly. Two `const val`s had to become `val` getters since
Fr.tr() isn't a compile-time constant.
fr_strings.json: 3805 -> 4034 (+229), 100% coverage of all tr()/Fr.tr() call
sites app-wide (2233 distinct strings). Verified no double-wraps, every
tr(/Fr.tr( call takes a string literal.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes the remaining English left after the first FR pass: dashboard-card labels, the Synthesis sheet, sync/calibration states, sport display names, device labels, sleep-screen layout overflow, chart legends, the Trends Report PDF (entirely missed by pass 1), and full sentences generated in the analytics layer (weekly digest, readiness, recovery drivers, illness, fitness age, cycle phase, vitality). Onboarding also gets a "Skip" button. This installs in place over 8.2.5 (stable signing key already active). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does
Type of change
How it was tested
Checklist
swift testinPackages/<name>)android/(./gradlew testFullDebugUnitTest)StrandDesigntokens — no hardcoded colors, fonts, or spacingdocs/CONTRIBUTING.mdStrand.xcodeproj/) or any secrets/keystoresRelated issues