Commit 88363fa
authored
Accessibility audit, release signing config, and Play Store beta listing prep (#18)
* Phase 11 Task 1: defensive profile parsing and tap test patient advocate carryovers
ProfileSetupScreen now uses toIntOrNull and toDoubleOrNull with return@Button
sentinels around dob year and height parsing, hoisted out of the scope.launch
coroutine builder so the sentinel resolves at the onClick lambda level. Closes
the Phase 2 Code Reviewer minor finding on toInt and toDouble crash exposure.
BilateralTapTest absorbs four Patient Advocate Phase 2 findings: pre instructions
copy replaced with "tap with the hand you usually write or text with first, then
the other hand" framing (Finding 1); a new RestingBetweenRounds state with a 5
second countdown sits between the two rounds (Finding 2); the spacer between the
two 120 dp tap circles widens from 16 dp to 32 dp (Finding 4); the Done state
now blocks on an explicit Continue button rather than auto routing back to the
battery (Finding 5).
Mid test "Dominant hand" and "Non dominant hand" role labels left unchanged
because they identify which round is currently active and serve a different
function from the introductory framing; flagged for the Patient Advocate's
Phase 11 phase close ratification at Task 17.
Test suite 428 to 435: 2 new ProfileSetupScreen tests, 3 new BilateralTapTestResting
tests using mainClock.autoAdvance = false plus advanceTimeBy, 2 new BilateralTapTestDone
tests. 0 failures, 0 errors, 4 pre existing skipped.
* Phase 11 Task 3: chart content description, sparkline, and pdf paint hoists
TrendLineChart wraps the per recomposition content description build in a
remember keyed on (series, displayLabel, context); the join over points and the
unit suffix resource lookup now run only when an input changes. Closes Phase 9
Performance Engineer finding P9-M1.
ReportsScreen TestSummaryCardView wraps the FeatureSeries constructor in a
remember keyed on (primaryFeatureSparkline, displayName) so the sparkline
points and Path are not rebuilt on every recomposition. Closes Phase 9
Performance Engineer finding P9-M2.
PdfChartRenderer hoists four Paint instances (linePaint, markerPaint,
markerNotchPaint, axisPaint) to instance fields, eliminating the per render and
per marker and per axis label allocation churn flagged at Phase 10 Performance
Engineer finding P10-M1. drawMarker resets strokeWidth to 0 on FILL paths so a
prior LOW band marker stroke does not leak into a subsequent HIGH or MEDIUM
band marker.
CsvExporter.toCsv drops the unused sessionIdByResultId parameter; the single
production caller in ReportExporter and the two test sites collapse to match.
FaceDistanceAnalyzer.processFaces drops the unused frameWidth parameter; the
one internal call site in analyze and the three test sites collapse to match.
Closes two pre existing kotlinc unused parameter warnings.
A handful of brief items were already closed in prior PRs and required no edits
this pass: PE M5 (RawSensorWriter rowBuffer landed in PR 7), PE M4 (transient
Quaternion in quaternionFromAndroidRotationVector already returns a single
allocation in PR 7), Code Reviewer Phase 3 L2 L3 L4 (kotlin.math.sin removed,
Math.PI replaced, double support time docstring rewritten in PR 8), and
Phase 10 PE M3 (CsvExporter SimpleDateFormat is already hoisted out of the per
row loop). The Phase 10 PE M2 bodyPaint was reported as dead but is in use
inside the chartsForPage branch in renderTestSection.
Test suite 435, 0 failures. assembleDebug BUILD SUCCESSFUL with zero new
warnings.
* Phase 11 Task 4: sensor trace writer interface, cancel drain, and Madgwick first sample hold
SensorTraceWriter interface extracts the public surface (appendRow, close) from
RawSensorWriter so the test path can swap in an in memory FakeSensorTraceWriter
without subclassing the file backed writer. RawSensorWriter stays final and
implements the interface; the convenience write(Flow) entry point is preserved
for the existing RawSensorWriterTest. Closes SPE Phase 4 finding I3.
GaitTestViewModel onCancel now drains the in flight sample buffer via
cancelAndJoin before the writer closes; the writer's finally block guarantees
close happens once whether the path ends normally or by cancellation. A new
SensorCancelTailEdgeTest emits 10 samples, cancels mid stream, and asserts the
writer received every sample before close. Closes SPE Phase 4 finding I1.
AndroidImuSource adds a first sample hold on the Madgwick fallback path. When
the rotation vector sensor is absent and lastAccel has not arrived, the source
sets firstSampleHoldStartedAtMs on the very first emission and skips Madgwick
updates until either the accelerometer arrives within 50 ms (the filter steps
with the real gravity reading) or 50 ms elapses (the filter falls back to the
linear surrogate so the pipeline does not stall). The source accepts an
injectable nowMs supplier so tests can drive virtual time; the default uses
System.currentTimeMillis. Closes SPE Phase 4 finding I2.
Test suite grows by 2 net (1 new SensorCancelTailEdgeTest plus 1 net from
splitting the prior Madgwick fallback test into two falsifiable tests of the
hold contract). Robolectric reporter intermittently fails XML emission under
parallel workers; --max-workers=1 produces a clean BUILD SUCCESSFUL.
* Phase 11 Task 5: atomic rename, render failure cleanup, and opportunistic stale tmp cleanup
ReportExporter swaps File.renameTo for Files.move with StandardCopyOption
ATOMIC_MOVE on both the PDF and CSV write paths. On AtomicMoveNotSupportedException
the path falls back to Files.move with REPLACE_EXISTING and increments a process
local AtomicLong exposed via ReportExporter.atomicMoveFallbackCount so future
diagnostics can detect a fallback path without the report package logging
(K.7 no log rule is preserved by using a counter rather than Log or println).
Both write blocks are now wrapped in try/finally and the .tmp file is deleted
in the finally block before the canonical file rename, so a render exception
no longer leaves stranded .tmp files in cacheDir/exports/. ReportPdfRenderer
opens its class and render method so the new ReportExporterRenderFailureTest
can inject a subclass that throws IOException("simulated render failure") from
render and assert that no .tmp file remains after the exception propagates.
At the top of every export call, cleanupStaleTempFiles globs cacheDir/exports
for .tmp files older than STALE_TMP_AGE_MS (60 seconds) and deletes each. The
new ReportExporterTmpCleanupTest pre creates two .tmp files, one 120 s old and
one 30 s old, runs export, and asserts the older file is gone and the newer
one stays. The test wraps the export call in a try/catch for the Robolectric
FileProvider SimplePathStrategy static cache bleed that surfaces only in full
suite test ordering; the cleanup runs first so the assertions hold regardless
of how the export call exits.
docs/security/hardening-checklist.md Section K.6 rewritten and marked verified
closed 2026-05-18 with the four implementing and verifying file paths cited.
Test suite at 439 passing, 0 failures, 4 ignored. assembleDebug BUILD SUCCESSFUL.
* Phase 11 Task 2: phase 4 patient advocate gait carryovers and abandoned session lifecycle
GaitInstructionsScreen now carries a mobility aid messaging paragraph hoisted
to strings.xml as gait_test_instructions_mobility_aid_body. The copy was
narrowed at Clinical Validator ratification: the original "aided walking is
still measurable" claim was rewritten to a narrower cadence rhythm claim, the
list expanded to include crutches (clinically relevant for MS EDSS 6 to 6.5
ambulation), the "same aid each time" constraint added to make the within
device trend honesty load bearing, and the ZUPT calibration bias disclosed
explicitly ("Absolute stride length values are calibrated for unaided walking
and may read low when an aid is used"). Closes PA Phase 4 Finding 3.
GaitCaptureScreen subscribes to a process scoped GaitVolumeCancelBus while the
capture is in Running state. MainActivity intercepts KEYCODE_VOLUME_DOWN and
forwards key down and key up events to the bus, which fires a cancelRequested
counter only when the hold exceeds 1500 ms. While the hold is in progress the
capture screen surfaces an in screen hint plus a live progress fill so the
user can see the gesture working when they pull the phone out of the pocket.
The on screen Cancel button stays in place for pre walk and post walk. No
sensor or audio focus path is touched; the bus is gated to the gait capture
state machine so volume controls work normally everywhere else. Closes PA
Phase 4 Finding 1.
GaitCancelledScreen joins the project's Phase 6 and Phase 8 cancelled screen
pattern with a single "Test cancelled. Nothing was saved." line and a Back to
home button. The state machine routes Cancel paths to it rather than to the
zero quality Done screen so a cancelled session never presents per feature
values. Closes PA Phase 4 Finding 2.
GaitDoneScreen confidence line for high quality completions reads "Walk
recorded. Trends are most reliable across several weeks." matching the warmth
register of the Voice and SDMT Done screens. Closes PA Phase 4 Finding 5.
SessionEntity grows a wasCancelled Boolean column via Room migration v2 to v3.
SessionRunnerScreen runs a DisposableEffect that on dispose, if the orchestrator
is still in Running, calls BatteryOrchestrator.cancelSession() which stamps the
in flight SessionEntity with completedAtEpochMs equal to now and wasCancelled
equal to true. SessionDao.reclaimStrandedSessions(nowEpochMs, strandedBeforeEpochMs)
runs from MainActivity onCreate to repair any session left at
completedAtEpochMs IS NULL more than 60 seconds in the past. HomeScreen
SessionRow renders "Cancelled", "Completed", or "In progress" per state. The
existing explicit BatteryOrchestrator.cancel() that deletes the row stays
intact so the "Stop and discard" dialog promise survives.
Test suite 439 to 453 (plus 14): GaitTestLifecycleTest 6 covers cancelSession
and reclaim semantics; GaitVolumeCancelBusTest 5 covers gating and threshold;
GaitCancelledScreenTest 1 smoke; Migration2To3Test 1 verifies the new column;
SessionDaoTest 1 verifies observeCompletedSessionCount excludes cancelled.
assembleDebug BUILD SUCCESSFUL.
* Phase 11 Task 9: release signing config wired with keystore.properties externalization
app/build.gradle.kts gains a signingConfigs.release block that reads
storeFile, storePassword, keyAlias, keyPassword from app/keystore.properties
when the file exists. If any required key is null in a present file the
config block throws a GradleException naming the missing property. A
gradle.taskGraph.whenReady guard inside android {} checks the task graph for
assembleRelease, bundleRelease, or packageRelease and throws a GradleException
with a clear "Missing app/keystore.properties for the release signing config.
Copy app/keystore.properties.example to app/keystore.properties and fill in
the four values. See docs/observability/signing-runbook.md for the keystore
generation steps." when the file is absent. Debug builds are unaffected.
app/keystore.properties.example committed as the template: four placeholder
lines with set this guidance for both passwords and the baselinems alias.
The real app/keystore.properties is gitignored alongside *.jks and *.keystore.
The keystore itself is NOT generated by this commit. Generation is a user
driven step per the docs/observability/signing-runbook.md runbook because
password handling is a personal decision. The release path becomes available
to the user as soon as they run the keytool command in the runbook and
populate keystore.properties from a password manager entry.
assembleDebug BUILD SUCCESSFUL. assembleRelease BUILD FAILED with the expected
clear error message rather than a NullPointerException.
* Phase 11 Task 8: accessibility specialist must fix items absorbed
VoiceRunnerScreen mic icon now decorative (contentDescription = null); the
adjacent visible Text already carries the recording announcement, so TalkBack
now reads "Recording. N s remaining." once rather than twice. Closes MF1.
VoiceAudioQualityCheckScreen band copy carries a Modifier.semantics live
region with LiveRegionMode.Polite so TalkBack announces band transitions
(Pending to Green or Yellow or Red, Yellow to Green and back) as they happen
within the screen rather than only on initial focus. Closes MF2.
TrendLineChart Canvas DrawScope replaces raw pixel constants 4f 8f 3f with
density independent values via Dp.toPx() inside the DrawScope (which itself
implements Density). 2 dp line width, 4 dp marker radius, 1.5 dp marker
stroke. The chart now scales correctly across display densities and at 200
percent Dynamic Type. Closes MF3.
TrendLineChart sparkline branch attaches Modifier.semantics with
invisibleToUser when no human readable content description is provided, so
TalkBack does not announce raw point data when the sparkline sits inside a
Card whose content description already carries the per series summary. The
@OptIn(ExperimentalComposeUiApi::class) annotation gates the experimental
invisibleToUser API. Closes MF4.
PdfPageLayout FONT_SIZE_CHART_AXIS raised from 10f to 11f. The 49 char axis
label at 11 pt SANS_SERIF stays under the 504 pt content width; the 1 pt
chart plot area offset is absorbed within CHART_HEIGHT_POINTS = 180f. The
12 pt body floor still sits clearly above the axis annotation. Closes MF5.
Test suite 453, 0 failures. assembleDebug BUILD SUCCESSFUL. Zero new
warnings.
* Phase 11 Task 11 polish: citation MISMATCH absorption
NONAN GaitPrint first author corrected from Likens to Wiles in README,
web/public/validation-summary.json, and web/app/methodology/page.tsx.
MAREA DOI corrected from 10.1016/j.gaitpost.2016.09.027 (which resolves
to an unrelated paper on tibial shaft fractures) to the correct value
10.1016/j.gaitpost.2016.09.023 in web/public/validation-summary.json.
Oh 2024 title in README replaced with the published verbatim title.
Madgwick.kt KDoc corrected to acknowledge the deliberate default
beta = 0.1 deviation from the paper's IMU optimal beta = 0.033
(Madgwick 2010 Section 5) with the project's load bearing empirical
evidence cited (Phase 3 fixture accuracy plus Phase 5 NONAN result).
Citation Auditor Task 11 verdict APPROVED WITH MINOR FINDINGS. No
runtime behavior changes; documentation only edit on Madgwick.kt
plus public artifact corrections.1 parent 0891f80 commit 88363fa
57 files changed
Lines changed: 1968 additions & 211 deletions
File tree
- app
- schemas/com.mustafanazeer.baselinems.data.AppDatabase
- src
- main
- java/com/mustafanazeer/baselinems
- battery
- gait
- tap
- voice
- data
- dsp
- report
- csv
- pdf
- signals
- camera
- ui
- home
- onboarding
- reports
- res/values
- test/java/com/mustafanazeer/baselinems
- battery
- gait
- tap
- data
- report
- csv
- signals
- camera
- ui
- home
- onboarding
- web
- app/methodology
- public
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
67 | | - | |
| 67 | + | |
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
92 | | - | |
| 92 | + | |
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
1 | 4 | | |
2 | 5 | | |
3 | 6 | | |
| |||
19 | 22 | | |
20 | 23 | | |
21 | 24 | | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
22 | 48 | | |
23 | 49 | | |
24 | 50 | | |
25 | 51 | | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
26 | 76 | | |
27 | 77 | | |
28 | 78 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
Lines changed: 198 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
Lines changed: 2 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| |||
25 | 26 | | |
26 | 27 | | |
27 | 28 | | |
28 | | - | |
| 29 | + | |
29 | 30 | | |
30 | 31 | | |
Lines changed: 32 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
| 9 | + | |
8 | 10 | | |
| 11 | + | |
9 | 12 | | |
10 | 13 | | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
11 | 17 | | |
12 | 18 | | |
| 19 | + | |
13 | 20 | | |
14 | 21 | | |
15 | 22 | | |
16 | 23 | | |
17 | 24 | | |
18 | 25 | | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
19 | 51 | | |
Lines changed: 17 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
88 | 88 | | |
89 | 89 | | |
90 | 90 | | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
91 | 108 | | |
92 | 109 | | |
93 | 110 | | |
| |||
0 commit comments