Memory.Wiki Android — iOS Parity Sprint (2026-05-30)
Single-day execution log: bring the Android-native companion app up to full iOS feature + visual parity, then push the platform-native surfaces (share intent, widget, app shortcuts) that Android does better than iOS.
Total: 25 commits, ~5,000 lines of polish + features, every surface verified on emulator-5554.
Phase 0 — The mess
The founder's words verbatim:
로그인 부터 모든 화면과 콤포넌트들이 ios에 비해서 너무나 완성도도 떨어지고 브랜딩 엘레멘트들도 반영도 안되고 아주 엉망진창임
Translation: every screen and component was visibly worse than iOS. Branding elements absent. A mess.
The Android scaffold existed (74 files, build pipeline working, demo signin working), but the visual + interaction parity wasn't there.
Starting state:
- 5-tab bar with subtle borderless icons, no labels, static blob
- Auth using Lucide.Apple fruit emoji for Sign in with Apple (HIG violation)
- Tab center "Start" tap was dead (WebView swallowed touches)
- MDs / Bundles / Start / etc. used basic Material chips and dot indicators
- No long-press menus, no pin/star, no edit/delete, no share, no semantic search
- No animated blob, no ambient backdrop, no skeletons-with-shimmer
Phase 1 — Primitives + visual parity
Shipped the shared visual atoms iOS uses everywhere, then rebuilt every screen on top.
Primitives
AmbientBlob— full-bleed faint morph blob at 4.5% alpha + 12dp blur behind empty statesDocStatusIcon— globe/users/cloud per privacy, with sync-badge composite (ink ring + microInfo circle + white check) when syncedBundleLayersIcon— Lucide Layers tinted by visibility (lime public / info-blue shared / faint private)RefreshingPip— inline 10dp ProgressView + mono 8 'REFRESHING' captionSkeletonRow/SkeletonList/SkeletonStatStrip— shimmering placeholdersBrandBlob— animated SMIL morph SVG hosted in a transparent WebView (only renderer on Android that plays the 10.867s morph cycle). iOS does the exact same thingMarkdownPillBar— 48dp Notes-style accessory bar above the keyboard with cursor-aware H/B/I/list/ordered/task/code/link/quote/hr inserts via TextFieldValue selection manipulationutil/TimeFormat.kt— centralcompactTime()so every list row formats dates identically ('now' / 'Xm' / 'Xh' / EEE / MMM d)
Surfaces (16 commits)
Every screen got rewritten or polished to iOS parity:
| Surface | What landed |
|---|---|
| Splash | 196dp animated blob + wordmark + tagline, staggered fade-in (spring 0.95/0.78, easeOut delay 160ms/280ms) |
| Auth | BrandBackdrop (huge ambient blob @ 7%), stacked hero (168dp blob → wordmark display 36 → tagline → 'ANDROID COMPANION' chip), glass ProviderButton (52dp, 12dp corner, gradient stroke, press scale 0.985 + dim), staggered entrance per iOS timing (0/180/280/380/450/700ms). Real Apple Inc. logo SVG, not a fruit |
| Onboarding | 3-card HorizontalPager (Capture / Feed your AI / Always within reach), capsule page dots, Skip + Next/Start CTA, ambient blob backdrop, mw.onboarded SharedPreferences flag |
| Tab bar | Capsule with translucent SheetBg + hairline border, labels under non-center tabs (mono 9 uppercase), 14×1dp ink indicator under active tab, animated blob centerpiece (WebView touch-passthrough fix), ProfileTabAvatar on Settings, re-tap shake + nav stack pop |
| Start | Greeting variants by time-of-day × day-of-year rotation, AI URL strip with Copy-for-AI sparkles→check 1.6s swap, Ask-your-hub CTA (microInfo border), pulse row as editorial hairline-bracketed 3-column strip (NOT card tiles), Quick actions, Recent (mixed docs+bundles), Starred section, Featured bundle card, per-block stagger entrance |
| Markdowns | Header with search toggle button + RefreshingPip, filter pills (All/Private/Shared/Synced), time bucket grouping (Today/Yesterday/This week/This month/Earlier), DocumentRow with DocStatusIcon + meta line (privacy badge + sync source + view count) + compactTime + share icon, long-press DropdownMenu (Star/Copy URL/Copy AI/Share/Open), pull-to-refresh, AmbientBlob empty states per filter, Pinned section above buckets, 300ms-debounced semantic search with BY MEANING section |
| Bundles | Same header/search/filter pattern as MDs but with Public filter + BundleLayersIcon rows |
| DocumentDetail | display 26 title, URL strip with DocStatusIcon + Copy-for-AI pill, meta strip (clock/lock/sync/eye), MarkdownBody via Markwon, owner brand accent override, overflow menu (Edit/Star/Add to bundle/Chat/Copy AI/Copy URL/Share/Make public-private/Open/Delete), edit mode with TextField, delete confirmation dialog, TOC sheet when ≥1 heading |
| BundleDetail | Layers glyph + display 26 title + description, Deploy card (DEPLOY THIS BUNDLE TO ANY AI header + explainer + inner URL pill with Copy-for-AI swap), Members section with MemberRow per doc + chevron, ellipsis menu |
| Capture | 6 mode pills (Write/URL/Photo/OCR/Voice/Files), title field display 28, body field with markdown pill toolbar above keyboard, Publish button in top bar |
| Chat | 2-line header (mono 9 SCOPE label + display 20 title) + 30dp glass xmark close, 'Try' empty hint with 3 sparkle prompts, Capsule user bubble, ASSISTANT caption above markdown assistant bubble, citation chips (resolve titles via DocCache), ThinkingIndicator, error row, composer with top hairline + Surface capsule input + animated keyboard-dismiss + 42dp ink Send circle. Tab bar hidden on chat |
| Settings | Hub URL card with Copy for AI / Copy URL / Open hub, Hub stats card (MEMORIES + BUNDLES tiles with public/private breakdown), Account section (Email/Username/Display Name editor sheet), Key color picker, LANGUAGE (opens system locale picker), LEARN (About + How it works + Replay welcome tour), FEEDBACK (Send feedback → mailto with device template), LEGAL, Version footer, Sign out |
| About | display 30 wordmark + tagline + 2 mono Tag pills + WHAT'S NEW bulleted card + ON THE WEB / ACROSS PLATFORMS / LEGAL link groups + credits |
Phase 2 — Platform-native affordances
Things Android does that iOS doesn't (or does differently). Shipped them to make Memory.Wiki feel native on Android, not a port.
| Affordance | Shipped |
|---|---|
| Share intent | ShareReceiverActivity routes text/plain from the system share sheet into Capture (pre-filled body + title) instead of silent-POSTing. image/* encodes WebP, uploads, and creates a doc referencing the upload URL |
| App Shortcuts | Long-press the launcher icon → 4 shortcuts (Capture / Ask / Search / Paste) via res/xml/shortcuts.xml. Each fires a memorywiki:// deep link straight into MainActivity. Verified via adb shell dumpsys shortcut |
| Glance widget | Home-screen widget with brand wordmark + ink 'New capture' primary action + Ask/Search/Paste quiet pills. XML preview layout for the widget picker so users see the chrome before adding it |
| Foreground refresh | RootShell installs a LifecycleEventObserver on ON_RESUME that emits RouterEvent.ForegroundRefresh. MDs/Bundles/Start consume it and re-pull so a user backgrounding the app + creating a doc on the web + coming back sees fresh state |
| PinnedStore | Central pin/star state singleton with optimistic local toggle + server reconciliation. Wired into MDs row + Bundles row + Start 'Starred' section + DocumentDetail menu |
Phase 3 — Workflow actions
Things the user can actually do with their data:
- Pin/unpin (PinnedStore) — Star/Unstar in long-press menus on MDs + Bundles + DocumentDetail. Star icon next to title when pinned. STARRED bucket above time buckets in MDs
- Edit document — inline TextField mode with mono 13 body + EDITING (MARKDOWN) caption, Save/Cancel toolbar swap, optimistic refresh
- Change visibility — Make public / Make private toggles
is_draftvia PATCH action=publish - Delete document — AlertDialog ('It moves to Trash on memory.wiki — recoverable for 30 days.') with destructive red Delete button
- Add to bundle — Bottom sheet with Create new bundle (inline TextField) + existing bundles list. Adding shows microLime checkmark for 700ms then dismisses
- Table of contents — Sheet that extracts ATX headings from doc body and displays them indented by level, tap to dismiss
- Semantic search — 300ms-debounced backend search runs alongside title-match. ProgressView spinner in search bar while loading. BY MEANING section beneath title-match results
- Publish from Capture — Top-bar Publish button creates the doc via POST /api/docs and routes the user to the new doc's reader
- Replay welcome tour — Settings row clears the SharedPreferences flag so onboarding plays again on next launch
Architecture notes
- Compose 1.7 + Material 3 dark-only, no Material You dynamic color
- Hilt 2.57.2 + KSP 2.2.10-2.0.2 for DI
- Ktor 3.0.2 for HTTP + streaming chat (
readUTF8Lineline-by-line) - Supabase-kt 3.0.2 with a JWT-decode workaround for service-role demo sessions (the standard
auth.userreturns null for those because/auth/v1/userreturns 401) - Markwon 4.6 for the markdown reader
- Glance 1.1.1 for the home-screen widget
- Coil 3 + coil-svg for provider logos + static brand assets
- Lucide compose (
com.composables:icons-lucide) wildcard-imported across all 17 screens for parity withlucide-reacton web + SF Symbols on iOS
The single-line summary
The Android app no longer feels like a port. It feels like the same product, with Android's affordances exposed natively.
What's next
Smaller polish + features remaining:
- In-app HelpScreen with DisclosureGroup-style expandable sections (Capture / Share / Widget / App Shortcuts / Deep links / Markdown / FAQ)
- Feedback template in Settings → mailto pre-filled with
App: Memory.Wiki Android v<x> · Device: <model> · OS: <version> · Locale: <id> - (lower priority) Quick Settings tile, additional widget sizes, deep-link from web → app banner
Everything above is shippable; the Android surface is launchable.
Built with Claude Opus 4.7 in a single 2026-05-30 working session. ~25 commits, all green, all visually verified on emulator-5554 with the demo account.