Commit Graph

330 Commits

Author SHA1 Message Date
Trey T
ab0e5c450c Coverage: regenerate gallery — 40/40 Android surfaces rendering
Previous run left edit_document at 0/4 because the record task hadn't
recorded it; the other 39 surfaces' goldens were optimized in-place by
zopflipng (no visual change). Gallery HTML/markdown regenerated to
reflect 160 Android goldens (40 surfaces × 4 variants).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 02:28:59 -05:00
Trey T
b24469bf38 Coverage: Android gallery expansion 23 → 39 surfaces + regenerate gallery
Android Roborazzi re-recorded end-to-end. Coverage expanded from 23
surfaces × 4 variants (92 goldens) to 39 surfaces × 4 variants (156
goldens). Only edit_document still silent-fails — flagged for follow-up
PR requiring fixture DocumentResponse + a non-network Edit flow.

docs/parity-gallery.html + docs/parity-gallery-grid.md regenerated:
47 screens, 156 Android + 88 iOS = 244 PNGs. Compared to the prior
gallery commit (3944223) this doubles total coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 01:45:14 -05:00
Trey T
6c3c9d3e0c Coverage: iOS ViewModel DI seam + populated-state snapshots
6 user-facing ViewModels now accept optional `dataManager: DataManagerObservable = .shared`
init param — production call-sites unchanged; tests inject fixture-backed
observables. Refactored: ResidenceViewModel, TaskViewModel, ContractorViewModel,
DocumentViewModel, ProfileViewModel, LoginViewModel.

DataManagerObservable gains test-only init(observeSharedDataManager:) + convenience
init(kotlin: IDataManager).

SnapshotGalleryTests.setUp() resets .shared to FixtureDataManager.empty() per test;
populated tests call seedPopulated() to copy every StateFlow from
FixtureDataManager.populated() onto .shared synchronously. 15 populated surfaces ×
2 modes = 30 new PNGs.

iOS goldens: 58 → 88. 44 SnapshotGalleryTests green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 01:45:04 -05:00
Trey T
3944223a5e P4: gitea-renderable parity-gallery-grid.md (markdown with inline images)
Gitea serves raw .html with Content-Type: text/plain for security, so the
HTML gallery only renders via `open` locally or external static hosting.
Add a parallel markdown version that gitea's /src/ view renders natively
with inline images.

View: https://gitea.treytartt.com/admin/honeyDueKMP/src/branch/rc/android-ios-parity/docs/parity-gallery-grid.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:59:06 -05:00
Trey T
031d61157f docs: regenerate parity gallery after tasks_empty_dark straggler 2026-04-18 23:53:53 -05:00
Trey T
f77c41f07a P2 addendum: tasks_empty_dark.png straggler 2026-04-18 23:53:53 -05:00
Trey T
fec0c4384a docs: regenerate parity gallery HTML (37 screens, 89 Android + 58 iOS) 2026-04-18 23:50:37 -05:00
Trey T
7a04ad4ff2 P2 addendum: 18 additional Android goldens (add/edit residence, join, manage users)
Late writes from the previous recordRoborazziDebug pass. Brings Android
coverage from 17 → 21 surfaces.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:50:33 -05:00
Trey T
707a90e5f1 P4: HTML parity gallery generator + comprehensive docs
scripts/build_parity_gallery.py walks both golden directories and pairs
Android↔iOS PNGs by filename convention into docs/parity-gallery.html —
a self-contained HTML file with relative <img> paths that renders
directly from gitea's raw-file view (no server needed).

Current output: 34 screens × 71 Android + 58 iOS images, grouped per
screen with sticky headers and per-screen anchor nav.

docs/parity-gallery.md: full workflow guide — verify vs record, adding
screens to both platforms, approving intentional drift, tool install,
size budget, known limitations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:45:20 -05:00
Trey T
6cc5295db8 P2: Android parity gallery — real-screen captures (partial, 17/40 surfaces)
Replaces the synthetic theme-showcase ScreenshotTests with real screens
rendered against FixtureDataManager.empty() / .populated() via
LocalDataManager. GallerySurfaces.kt manifest declares 40 screens.

Landed: 68 goldens covering 17 surfaces (login, register, password-reset
chain, 10 onboarding screens, home, residences-list).

Missing: 23 detail/edit screens that need a specific fixture model passed
via GallerySurfaces.kt — tracked as follow-up in docs/parity-gallery.md.
Non-blocking: these render silently as blank and don't fail the suite.

Android total: 2.5 MB, avg 41 KB, max 113 KB — well under the 150 KB
per-file budget enforced by the CI size gate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:45:12 -05:00
Trey T
3bac38449c P3.1: iOS goldens @2x + PNG optimizer + Makefile record/verify targets
- SnapshotGalleryTests rendered at displayScale: 2.0 (was native 3.0)
  → 49MB → 15MB (~69% reduction)
- Records via SNAPSHOT_TESTING_RECORD=1 env var (no code edits needed)
- scripts/optimize_goldens.sh runs zopflipng (or pngcrush fallback)
  over both iOS and Android golden dirs
- scripts/{record,verify}_snapshots.sh one-command wrappers
- Makefile targets: make {record,verify,optimize}-snapshots

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:45:02 -05:00
Trey T
6f2fb629c9 P3: iOS parity gallery (swift-snapshot-testing, 1.17.0+)
Records 58 baseline PNGs across 29 primary SwiftUI screens × {light, dark}
for the honeyDue iOS app. Covers auth, password reset, onboarding,
residences, tasks, contractors, documents, profile, and subscription
surfaces — everything that's instantiable without complex runtime context.

State coverage is empty-only for this first pass: views currently spin up
their own ViewModels which read DataManagerObservable.shared directly, and
the test host has no login → all flows render their empty states. A
follow-up PR adds an optional `dataManager:` init param to each
*ViewModel.swift so populated-state snapshots (backed by P1's
FixtureDataManager) can land.

Tolerance knobs: pixelPrecision 0.97 / perceptualPrecision 0.95 — tuned to
absorb animation-frame drift (gradient blobs, focus rings) while catching
structural regressions.

Tooling: swift-snapshot-testing SPM dep added to the HoneyDueTests target
only (not the app target) via scripts/add_snapshot_testing.rb, which is an
idempotent xcodeproj-gem script so the edit is reproducible rather than a
hand-crafted pbxproj diff. Pins resolve to 1.19.2 (up-to-next-major from
the 1.17.0 plan floor).

Blocks regressions at PR time via `xcodebuild test
-only-testing:HoneyDueTests/SnapshotGalleryTests`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:37:09 -05:00
Trey T
47eaf5a0c0 P1: Shared FixtureDataManager (empty + populated) for cross-platform snapshots
InMemoryDataManager + Fixtures with deterministic data (fixed clock 2026-04-15,
2 residences, 8 tasks, 3 contractors, 5 documents). FixtureDataManager.empty()
and .populated() factories. Exposed to Swift via SKIE.

Expanded IDataManager surface (5 -> 22 members) so fixtures cover every
StateFlow and lookup helper screens read: myResidences, allTasks,
tasksByResidence, documents, documentsByResidence, contractors, residenceTypes,
taskFrequencies, taskPriorities, taskCategories, contractorSpecialties,
taskTemplates, taskTemplatesGrouped, residenceSummaries, upgradeTriggers,
promotions, plus get{ResidenceType,TaskFrequency,TaskPriority,TaskCategory,
ContractorSpecialty}(id) lookup helpers. DataManager implementation is a pure
override-keyword addition — no behavior change.

Enables P2 (Android gallery) + P3 (iOS gallery) to render real screens against
identical inputs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:22:41 -05:00
Trey T
c57743dca0 Fix: expect/actual enableTestTagsAsResourceId() for iOS compile
testTagsAsResourceId is Android-only; its use in commonMain broke
compileKotlinIosSimulatorArm64. Wrap behind expect fun — Android impl
sets the semantic, other platforms return Modifier unchanged. Blocks
P3 iOS parity gallery otherwise.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:20:48 -05:00
Trey T
f56d854acc P0.3: add iOS @Environment(\.dataManager) key
Introduces DataManagerEnvironmentKey + EnvironmentValues.dataManager so
SwiftUI views can resolve DataManagerObservable via @Environment, mirroring
Compose's LocalDataManager ambient on the Kotlin side.

No view migrations yet — views continue to read DataManagerObservable.shared
directly. The actual screen-level substitution (fake DataManager for
parity-gallery / tests / previews) lands in P1 when ViewModels gain an
optional init param that accepts the environment-resolved observable. For
this commit we only need the key so P1 can wire against it.

Note: the iosSimulator Kotlin compile is broken at baseline (bb4cbd5)
with pre-existing "Unresolved reference 'testTagsAsResourceId'" errors
across 20+ screen files — Android-only semantics API imported in
commonMain. Swift-parse of the new file succeeds. Verified by checking
out bb4cbd5 and rerunning ./gradlew :composeApp:compileKotlinIosSimulatorArm64.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:11:15 -05:00
Trey T
00e215920a P0.2: migrate screens to LocalDataManager.current
Swaps direct `DataManager.xxx` access for `LocalDataManager.current.xxx`
across every Compose screen under ui/screens/** that references the
singleton. Each composable resolves the ambient once at the top of its
body and reuses the local val for subsequent reads — keeping rewrites
minimal and predictable.

Screens touched:
  - HomeScreen                          (totalSummary)
  - ResidencesScreen                    (totalSummary)
  - ResidenceDetailScreen               (currentUser)
  - ResidenceFormScreen                 (currentUser)
  - ProfileScreen                       (currentUser + subscription)
  - ContractorDetailScreen              (residences)
  - subscription/FeatureComparisonScreen (featureBenefits)
  - onboarding/OnboardingFirstTaskContent (residences × 3 sites)

No behavior change — in production the ambient default resolves to the
same DataManager singleton. The change is purely so tests, previews, and
the parity-gallery can `CompositionLocalProvider(LocalDataManager provides fake)`
to substitute a fake without tearing screens apart.

Files under ui/subscription/** and ui/components/AddTaskDialog.kt also
reference DataManager but live outside ui/screens/** (plan's scope) —
flagged for a follow-up pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:08:58 -05:00
Trey T
98b775d335 P0.1: extract IDataManager interface + LocalDataManager ambient
Adds a narrow IDataManager contract covering the 5 DataManager members
referenced from ui/screens/** (currentUser, residences, totalSummary,
featureBenefits, subscription) and a staticCompositionLocalOf ambient
(LocalDataManager) that defaults to the DataManager singleton.

No screen call-sites change in this commit — screens migrate in P0.2.
ViewModels, APILayer, and PersistenceManager continue to depend on the
concrete DataManager singleton directly; the interface is deliberately
scoped to the screen surface the parity-gallery needs to substitute.

Includes IDataManagerTest (DataManager is IDataManager) and
LocalDataManagerTest (ambient val is exposed + default type-checks to the
real singleton). runComposeUiTest intentionally avoided — consistent with
ThemeSelectionScreenTest's convention, since commonTest composition
runtime is flaky on iosSimulator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:06:16 -05:00
Trey T
bb4cbd58c3 Audit: form-error TalkBack + focus management + navigateUp polish
(a) liveRegion + error semantics on form error surfaces so TalkBack
    announces them when they appear:
    - Shared ErrorCard (used by LoginScreen, RegisterScreen,
      VerifyEmail/ResetCode, ForgotPassword, ResetPassword)
    - OnboardingCreateAccountContent inline error row
    - JoinResidenceScreen inline error row

(b) focusRequester + ImeAction.Next on multi-field forms:
    - LoginScreen: auto-focus username, Next→password, Done→submit
    - RegisterScreen: auto-focus username, Next chain through
      email/password/confirm, Done on last

(c) navigateUp() replaces navController.popBackStack() for simple back
    actions in App.kt (6 screens) and MainScreen.kt (3 screens), where
    the back behavior is purely navigation-controlled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 18:16:22 -05:00
Trey T
a1f366cb30 Audit: hardcoded Color.* → MaterialTheme.colorScheme (dark-mode parity)
Replaces ~31 literal Color.White / Color.Red / Color.Gray / Color(0x…)
usages in production screens with theme-aware colorScheme roles. Fixes
dark-mode regressions on those surfaces.

Covers: DocumentCard, DocumentStates, DocumentDetailScreen,
ContractorDetailScreen (delete/favorite tints), AddContractorDialog
(FilterChip, Switch, dialog container).

Overlay-specific Color.Black/Color.White (e.g., photo-grid darkening
overlay) and theme-defined brand tokens (OrganicDesign, ThemeColors)
are left untouched per spec.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 18:11:59 -05:00
Trey T
d49bc719b2 Audit: .dp → AppSpacing/AppRadius (tokenization, partial sweep)
Replaces ~163 raw .dp values with design-system tokens per CLAUDE.md rule.
Covers most visible screens (Tasks, Residences, Profile, Documents,
dialogs, kanban, forms). Adds AppSpacing/AppRadius imports where missing.

Remaining sites are geometric/canvas values (stroke widths, icon sizes,
non-standard values like 6.dp/14.dp/20.dp) or don't map to existing
tokens.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 18:08:23 -05:00
Trey T
0c554cce6a P8: Roborazzi golden image pipeline live
Records initial golden set + wires verifyRoborazziDebug into CI. Diffs
uploaded as artifact on failure. ScreenshotTests @Ignore removed.

Root cause of the prior RoboMonitoringInstrumentation:102 failure:
createComposeRule() launches ActivityScenarioRule<ComponentActivity>
which fires a MAIN/LAUNCHER intent, but the merged unit-test manifest
declares androidx.activity.ComponentActivity without a LAUNCHER filter,
so Robolectric's PM returns "Unable to resolve activity for Intent".
Fix: switch to the standalone captureRoboImage(path) { composable }
helper from roborazzi-compose, which registers
RoborazziTransparentActivity with Robolectric's shadow PackageManager
at runtime and bypasses ActivityScenario entirely.

Also pin roborazzi outputDir to src/androidUnitTest/roborazzi so
goldens live in git (not build/) and survive gradle clean.

36 goldens, 540KB total.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:57:35 -05:00
Trey T
77f32befb8 Audit: meaningful contentDescription for actionable icons (sweep)
Upgrades ~31 sites previously annotated `// decorative` to meaningful
strings where the icon is actionable. Purely decorative leading icons
retain null contentDescription per Material 3 guidance.

Focus areas:
- Selection indicators (CheckCircle vs RadioButtonUnchecked, check marks)
- Status icons (Error, Warning, CheckCircle, ErrorOutline, CloudOff, Info)
- Expand/collapse toggles (ExpandLess/ExpandMore)
- Feature inclusion indicators (Check/Close in comparison tables)
- Requirement indicators (password strength satisfied/not)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:57:08 -05:00
Trey T
d8569c7aed Audit: PullToRefreshBox on remaining list screens (iOS parity)
HomeScreen + AllTasksScreen + TasksScreen now support pull-to-refresh.
forceRefresh=true per CLAUDE.md mutation pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:50:27 -05:00
Trey T
95f7318ee6 Audit 9a.3: custom sheets → ModalBottomSheet (M3 parity)
CompletionHistorySheet + contractor-picker sheet now use
material3.ModalBottomSheet. Standard Material 3 dim-behind + swipe-down
dismiss + sheet state. Inner content unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:48:14 -05:00
Trey T
40d2607da8 Suite6 + P8: Comprehensive task tests + Roborazzi scaffolding
Suite6_ComprehensiveTaskTests ports iOS tests not covered by Suite5/10
(priority/frequency picker variants, custom intervals, completion history,
edge cases).

Roborazzi screenshot-regression scaffolding in place but gated with @Ignore
until pipeline is wired — first `recordRoborazziDebug` run needs manual
golden-image review. See docs/screenshot-tests.md for enablement steps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:39:39 -05:00
Trey T
0015a5810f Audit 9a: Long-press context menus + swipe-to-dismiss on cards
Partial implementation (wave 9a agent was rate-limited mid-flight).
Delivered:
- TaskCard long-press context menu (Edit / Delete / Share)
- ResidencesScreen + ContractorsScreen swipe-to-dismiss wired for delete
- Confirm dialog before dismissal (non-destructive gesture)

Not yet delivered (follow-up):
- CompletionHistorySheet → ModalBottomSheet migration
- Contractor-picker sheet migration

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:39:29 -05:00
Trey T
ba1ec2a69b Audit 9b: Dynamic color (Material You) + 48dp min touch target helpers
Dynamic color opt-in on Android 12+ via expect/actual DynamicColor:
- commonMain: expect isDynamicColorSupported() + rememberDynamicColorScheme()
- androidMain: SDK>=31 gate, dynamicLight/DarkColorScheme(context)
- iOS/JVM/JS/WASM: no-op actuals
- ThemeManager gains useDynamicColor StateFlow, persisted via ThemeStorage
- App.kt wires both currentTheme + useDynamicColor into HoneyDueTheme
- ThemeSelectionScreen exposes the "Use system colors" toggle

Touch target helpers:
- Modifier.minTouchTarget(48.dp) + Modifier.clickableWithRipple
- Applied at audit-flagged sites in CompleteTaskScreen

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:39:22 -05:00
Trey T
214908cd5c Maestro: 10 golden-path flows for critical user journeys
Cross-platform YAML flows (iOS + Android share the AccessibilityIds
test-tag namespace). Covers login, register, residence/task CRUD,
completion, join, document upload, theme, deeplink, widget.

Run: maestro test .maestro/flows/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 15:00:21 -05:00
Trey T
1946fb9e6a Maestro: 10 golden-path flows for critical user journeys
Cross-platform YAML flows (iOS + Android share the AccessibilityIds
test-tag namespace). Covers login, register, residence/task CRUD,
completion, join, document upload, theme, deeplink, widget.

Run: maestro test .maestro/flows/

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:59:54 -05:00
Trey T
95a5338abd CI: Gradle Managed Devices + GitHub Actions workflow
pixel7Api34 managed device runs instrumented tests headlessly on CI.
Three test-filter profiles (ci/parallel/full) mirror iOS xctestplan
variants. run_ui_tests.sh convenience wrapper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:57:42 -05:00
Trey T
227c0a9240 UI Test Suite8: Document/Warranty tests (iOS parity)
Ports representative subset of Suite8_DocumentWarrantyTests.swift
(22 of 25 iOS tests). testTags on document screens via
AccessibilityIds.Document.*. Documented deliberate skips in the
class header (5/7/8/10/11/12/16) — each either relies on iOS-only
pickers/menus or is subsumed by another ported test.

No new AccessibilityIds added — Document group already has parity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:54:24 -05:00
Trey T
840c35a7af P7 Stream Y: empty/error/loading state audit (iOS parity)
Audits every list + detail screen (non-document) for empty/error/loading
state parity with iOS. Reuses StandardEmptyState / StandardErrorState where
possible; adds missing states where screens currently show blank on error.

- Add StandardErrorState and CompactErrorState components under
  ui/components/common/ (mirrors iOS ErrorView pattern: icon + title +
  message + Retry).
- ManageUsersScreen: error state previously had no retry button; now uses
  StandardErrorState with a Retry CTA matching iOS ManageUsersView.
- ResidenceDetailScreen: task and contractor sub-section error cards now
  use CompactErrorState with inline retry (previously plain error text).

Other audited screens (ResidencesScreen, TasksScreen, AllTasksScreen,
ContractorsScreen, ContractorDetailScreen, EditTaskScreen,
CompleteTaskScreen, TaskTemplatesBrowserScreen, TaskSuggestionsScreen,
OnboardingFirstTaskContent) already had loading + error + empty parity
via ApiResultHandler / HandleErrors / inline state machines; no changes
needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:50:07 -05:00
Trey T
d42406cbec UI Test SuiteZZ: Cleanup tests (iOS parity)
Port SuiteZZ_CleanupTests.swift. Deletes test-prefixed residences/tasks/
contractors/documents via authenticated APILayer + TaskApi calls. Runs
alphabetically last via the SuiteZZ_ prefix. Each step is idempotent —
logs failures but never blocks the next run. Preserves one seed "Test
House" residence so AAA_SeedTests has a deterministic starting point.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:47:41 -05:00
Trey T
6980ed772b UI Test Suite4: Comprehensive residence tests (iOS parity)
Ports Suite4_ComprehensiveResidenceTests.swift. testTags on residence
screens via AccessibilityIds.Residence.*. CRUD + join + manage users +
multi-residence switch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:43:08 -05:00
Trey T
eedfac30c6 UI Test Suite5: Task tests (iOS parity)
Ports Suite5_TaskTests.swift. testTags on task screens via
AccessibilityIds.Task.*. CRUD + kanban + filter/sort + templates + suggestions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:40:38 -05:00
Trey T
c772215c04 UI Test Suite7: Contractor tests (iOS parity)
Ports Suite7_ContractorTests.swift. testTags on contractor screens via
AccessibilityIds.Contractor.*. CRUD + sharing + link-to-task.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:40:21 -05:00
Trey T
95dabf741f UI Test Suite1: Registration + SimpleLogin ports (iOS parity)
Ports iOS Suite1_RegistrationTests.swift + SimpleLoginTest.swift to
Android Compose UI Test. Adds testTag annotations on auth screens using
shared AccessibilityIds.Authentication constants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:38:56 -05:00
Trey T
b97db89737 UI fix 5/5: contentDescription for actionable icons (partial audit)
Actionable IconButton/Icon instances now expose meaningful
contentDescription for TalkBack. Purely decorative icons retain
contentDescription = null with clarifying comments. Full audit of
remaining 130+ sites is follow-up work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:27:17 -05:00
Trey T
00a217e8c8 UI fix 4/5: Button variant migration — OutlinedButton/TextButton for secondary + tertiary
Primary confirmation buttons remain as Button (M3 filled default). Secondary
actions (Cancel, Dismiss) → OutlinedButton. Inline links → TextButton. Reduces
visual hierarchy ambiguity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:22:59 -05:00
Trey T
2d80ade6bc Test infra: shared accessibility IDs + PageObjects + AAA_SeedTests
Ports iOS HoneyDueUITests AccessibilityIdentifiers + PageObjects pattern
to Android Compose UI Test. Kotlin AccessibilityIds object mirrors Swift
verbatim so scripts/verify_test_tag_parity.sh can gate on divergence.

AAA_SeedTests bracketed first alphanumerically; SuiteZZ cleanup to follow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:19:37 -05:00
Trey T
42c21bfca1 UI fix 3/5: imePadding on form screens
Soft keyboard no longer covers input fields. Applied to every screen
with text input.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:18:46 -05:00
Trey T
0ec2ac7744 UI fix 2/5: lifecycle-aware StateFlow collection in screens
Replace collectAsState() with collectAsStateWithLifecycle() so StateFlows
stop collecting when the host is in background — prevents memory/CPU leaks
on lifecycle transitions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:15:03 -05:00
Trey T
a78494c529 UI fix 1/5: mirror ArrowBack/ArrowForward/List icons for RTL locales
Material 3 AutoMirrored variants flip correctly in Arabic/Hebrew.
Previous Icons.Default.ArrowBack pointed wrong direction in RTL.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:14:02 -05:00
Trey T
03a68a8876 Test infra: add Compose UI Test + UI Automator to androidInstrumentedTest
Prep for parallel Android UI test suite build-out. These deps unblock
page-object tests using onNodeWithTag / createAndroidComposeRule and
cross-app permission-dialog interaction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 14:10:00 -05:00
Trey T
485f70dfa1 Integration: residence invite accept/decline APIs + wire notification actions
Adds acceptResidenceInvite / declineResidenceInvite to ResidenceApi
(POST /api/residences/{id}/invite/{accept|decline}) and exposes them via
APILayer. On accept success, myResidences is force-refreshed so the
newly-joined residence appears without a manual pull.

Wires NotificationActionReceiver's ACCEPT_INVITE / DECLINE_INVITE
handlers to the new APILayer calls, replacing the log-only TODOs left
behind by P4 Stream O. Notifications are now cleared only on API
success so a failed accept stays actionable.

Tests:
 - ResidenceApiInviteTest covers correct HTTP method/path + error surfacing.
 - NotificationActionReceiverTest invite cases updated to assert the new
   APILayer calls (were previously asserting the log-only path).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:36:59 -05:00
Trey T
10b57aabaa P6 Stream V: ImageCompression (expect/actual) + CameraPicker polish
Android: BitmapFactory + ExifInterface + JPEG quality 0.7 + 1920px downscale.
iOS: UIImage.jpegData. JVM/JS/WASM: no-op. CameraPicker uses TakePicture
ActivityResult + permission rationale.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:35:54 -05:00
Trey T
3069ec41de P5 Streams Q+R: TaskAnimations + AnimationTestingScreen
Port iOS TaskAnimations.swift specs (completion checkmark, card transitions,
priority pulse) + AnimationTestingView as dev screen.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:35:32 -05:00
Trey T
cf2aca583b P7 Stream X: ResidenceFormState + validation (iOS parity)
Pure-function field validators matching iOS ResidenceFormView rules.
Mirrors Stream W's TaskFormState pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:34:49 -05:00
Trey T
1cbeeafa2d Integration: remove legacy WidgetTaskActionReceiver (replaced by CompleteTaskAction — P3 Stream M follow-up)
Stream M replaced the widget's task-complete path with CompleteTaskAction +
WidgetActionProcessor. Grepping confirmed the only references to
WidgetTaskActionReceiver were its own class file and the manifest entry.
Remove both.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:32:26 -05:00
Trey T
975f6fde73 Integration: port push-token registration to new FcmService (P4 follow-up)
Move the legacy MyFirebaseMessagingService.onNewToken() device-registration
path onto the new iOS-parity FcmService. The legacy service is already
unwired from the manifest MESSAGING_EVENT filter; this removes its last
functional responsibility. Behaviour preserved: auth-token guard,
DeviceRegistrationRequest with ANDROID_ID + Build.MODEL, log-only on error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 13:31:52 -05:00