Closer/gradle
null 7b8fa652f6 feat(avatar): frame a picked photo, and fall back to initials not a heart
Initials, shared. Home's partner bubble and the Settings "Connected with" row
are two views of one person and gave different answers when there was no photo —
Home showed initials, Settings showed a heart. Settings now shows the partner's
initials in a 48dp circle, so the photo and no-photo states share one
silhouette. The helper is lifted to ui/components/Initials.kt rather than copied;
an identical copy in two files is exactly how packArtworkRes drifted. The
unpaired state keeps the heart — there is nobody to take initials from yet.

Framing. Picking a photo now opens a crop sheet: pinch to zoom, drag to move,
inside the real avatar circle. The avatar was a blind ContentScale.Crop, so a
non-square or off-centre photo was centre-cropped and could lose the subject —
Ava's 640x480 test photo cropped straight past her face. The crop is applied at
pick time and handed back as a normal Uri, so setPhotoUri → upload is untouched.
EXIF orientation is honoured (a portrait selfie would otherwise crop sideways),
output is a 512px JPEG, and the source aspect is respected.

Two bugs found by driving it live, both of which looked fine in code:
- Panning did nothing at 1x. The clamp was viewport*(scale-1)/2, which is 0 at
  1x — correct only for a square source. A cover-fit 640x480 photo already
  overflows horizontally at 1x, so that pinned it dead centre and made framing
  impossible: the whole point of the sheet. Now clamped to the picture's actual
  overflow, computed from the source aspect.
- Panning then revealed empty space at the circle's edge. ContentScale.Crop had
  already discarded the overflow and returned a viewport-sized node, so
  graphicsLayer was sliding an already-cropped square around — the pixels being
  panned to had been thrown away first. The preview now draws the bitmap itself
  at cover*scale with the same maths as the crop, so preview == output.

Adds androidx.exifinterface. Verified live on 5554: sheet opens, pan moves the
picture (pixel-probed), no empty edge. 0 FATAL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 23:19:37 -05:00
..
wrapper feat: Firebase + RevenueCat stack, PLAN.md integration, 11 implementation batches (v0.1.0) 2026-06-15 18:43:43 -05:00
libs.versions.toml feat(avatar): frame a picked photo, and fall back to initials not a heart 2026-07-14 23:19:37 -05:00