Repository navigation
Bugs found while taking v0.14.2 screenshots on an emulator (incl. data lost after first restart) #321
Description
Activity
Acknowledged — triaging the 8 items by priority:
Critical (affects real users):
- 1. Data loss on first restart — highest priority. Likely a WAL checkpoint race or the DB being recreated on first real start before the imported data flushes. Will investigate the DB init path in
MainActivity/AppDatabase. Repro: fresh install → onboarding → POST buckets+events → force-stop → reopen → check/api/0/buckets/. - 2. ANR on fresh boot — main thread blocked in
MainActivity.onResumewhile starting a thread. Will move blocking work off the main thread.
Quick wins:
- 7. "Unsupported aw-notify setting" toast — already fixed in fix(settings): treat an unset aw-notify setting as defaults aw-webui#1043 (merged). Just needs a webui version bump in aw-android.
- 5. Sync settings ignores dark mode — straightforward theme attribute fix.
Medium effort:
- 3. Desktop-only tabs on Android — need to check how the webui detects the platform and suppresses Window/Browser/Editor tabs on Android.
- 6. Back-swipe finishes MainActivity — need to intercept
onBackPressedfor gesture nav and either open the drawer or dispatch the event to the WebView.
Depends on webui work:
- 4. Week view starts on given date, not Monday — same root cause as UI bugs found while generating v0.14 marketing screenshots aw-webui#1058 item 1; will coordinate there.
- 8. Multi-device returns 0 s — check after the webui bump lands.
Starting with item 1 (data loss) since it's the most likely to affect real users. Will post findings and PRs here.
- 1. Data loss on first restart — highest priority. Likely a WAL checkpoint race or the DB being recreated on first real start before the imported data flushes. Will investigate the DB init path in
Update from a second run (logs captured this time):
- Item 1 root cause: on a fresh install the embedded aw-server-rust can be started twice; both run DB setup and one crashes with
table key_value already exists. After that the API errors or silently drops writes on later starts. Clearing app data and redoing onboarding recovers it. Fix: make server start idempotent (single-instance guard) and make the migration tolerant (CREATE TABLE IF NOT EXISTS/ inside a transaction). - New: sync status warning on every phone. "Found 0 remote db files … Zero peers …" shows on push-only phones because (a) the filter meant to hide it matches
zero peersbut the message saysZero peers(case), and (b) it looks for peer dbs in the app's private dir instead of the SAF folder the user picked. - New: pushed sync db is named
test.db(<host>/<device-id>/test.db), likely a leftover testing name.
- Item 1 root cause: on a fresh install the embedded aw-server-rust can be started twice; both run DB setup and one crashes with
Thanks for the second-run findings — this is exactly the context needed to fix item 1 properly.
Item 1 (data loss) — root cause confirmed:
The double-start path makes sense as the trigger. Fix plan:- Single-instance guard in Android — check if server process is already running before spawning a second. Can use a port check or a PID file in app data dir.
- Idempotent schema migrations in aw-server-rust —
CREATE TABLE IF NOT EXISTS/ wrapping in a transaction withIF NOT EXISTSguards on all DDL. This is the safer fix since it protects against any double-init scenario, not just this one path.
Will open separate PRs for each (Android-side guard + aw-server-rust migration hardening).
New items from second run:
- Sync status warning (case + path bug) — will fix the
zero peers→Zero peerscase sensitivity and the SAF path lookup. This is a pure Android fix. test.dbfilename — sounds like a leftover from development. Will track down where the filename is set and replace with<device_id>.dbor similar.
Adding these 3 new items to the tracking list. I'll post links here as PRs open.
Status update — linked PRs so far:
aw-server-rust side (data loss fix)
- fix(datastore): make v3→v4 key_value migration idempotent aw-server-rust#792 —
CREATE TABLE IF NOT EXISTS key_valuein the v3→v4 migration. The double-start race puts two instances atuser_version < 4simultaneously; the secondCREATE TABLEpanics. One-word fix, 29 tests pass.
Android side (data loss fix)
- The Android-side single-instance guard (prevent the second
startServer()call) is the remaining blocker. I need to add a port-occupancy check beforeRustInterface.startServer()to short-circuit rather than queue a second start. Will open a follow-up PR.
aw-webui bugs (items 4, Trends, Work report)
- fix: snap period-start URL dates; fix Trends chart height and x-axis labels aw-webui#1059 — snaps
/#/activity/@all/year/2026-10-05to Jan–Dec 2026; fixes Trends barchart height/x-axis; fixes Saturday-week day-switch; fixes Work report host names. Greptile review passed on current HEAD.
Other items opened earlier
- PR fix(ui): add a visible button to open the navigation drawer #318 — visible drawer open button (item 6)
- PR fix(server): don't crash at startup when the server socket can't be created #315 — server socket startup crash on GrapheneOS (Network permission denied path)
Still pending (will open PRs):
- Android single-instance guard (the Android half of the data loss fix above)
- Sync status "Zero peers" case-sensitivity + SAF path lookup
test.db→<device_id>.dbrename in sync push- ANR on fresh boot (main thread blocking in
onResume)
- fix(datastore): make v3→v4 key_value migration idempotent aw-server-rust#792 —
One more from today's investigation: the 'zero peers' warning filter in
SyncSettingsActivity.ktwas broken due to a case mismatch — the code checked for lowercase"zero peers"but aw-server-rust emits"Zero peers"(capital Z). PR #323 fixes it with a one-line.lowercase()call.- added a commit that references this issue
on Oct 6, 2026 Android single-instance guard opened: #324
Identified and fixed the race that allowed two concurrent
onStartCommandinvocations to both pass the!serverStartedcheck before either set the flag:@VolatileonserverStartedso readers outside the synchronized block always see fresh valuessynchronized(this)wraps the entire check-probe-set sequence instartServerTask(), making the guard atomic
Complements ActivityWatch/aw-server-rust#792 (idempotent migration, merged) — together they prevent both the double-start and the crash if a double-start somehow still occurs.
Item 2 (ANR on fresh boot) — PR #325 open.
Root cause:
lifecycleScope.launch {}usesDispatchers.Main.immediate, running the coroutine body inline on the main thread.sendHeartbeatsSuspend()accessed lazysessionWatcher, constructingSessionEventWatcher.SessionEventWatcher.rustwas eagerly initialised — itsOffThreadInitcallsThread.start()immediately. On a heavily-loaded fresh bootThread.start()can block long enough to ANR.Fix: (a) make
SessionEventWatcher.rustlazy (same pattern asUsageStatsWatcher.rust), and (b) addwithContext(Dispatchers.IO)insendHeartbeatsSuspend()so the lazy init always happens off the main thread.Following up on items 2 and 5:
Item 2 — ANR on fresh boot (PR #325)
Root cause:
lifecycleScope.launch {}runs onDispatchers.Main.immediate, which executes the coroutine body inline on the UI thread until the first suspension point. Accessing the lazysessionWatchertriggeredSessionEventWatcherconstruction on the main thread, which in turn calledOffThreadInit→Thread.start(). On a fresh boot under high load,Thread.start()can block the UI thread long enough to ANR.Two-part fix in #325:
- Made
SessionEventWatcher.rustaby lazyproperty (matching the pattern already inUsageStatsWatcher) so construction ofOffThreadInitis deferred. - Added
withContext(Dispatchers.IO)inUsageStatsWatcher.sendHeartbeatsSuspend()before touchingsessionWatcher, ensuring all lazy construction and JNI init happens off the main thread even when the caller is onDispatchers.Main.immediate.
Item 5 — Sync settings ignores dark mode (PR #326)
Root cause:
applySafeWindowInsets()always calledapplySystemBarAppearance(darkContent = false), hardcoding a white window background for all native screens. In dark mode the DayNight theme resolved text colours to white → white text on white background.Fix in #326: Added
followNightMode: Boolean = falseparameter toapplySafeWindowInsets().SyncSettingsActivitynow passesfollowNightMode = true. The layout already uses only DayNight theme attributes, so no layout changes were needed. Default isfalsesoMainActivity(which drives its own appearance from the embedded web UI) is unaffected.- Made
Item 6 (back-swipe finishes MainActivity): #327. Back now closes the drawer if it's open, otherwise goes back in the WebView's history, and only finishes the activity when there's no history left. 3 new unit tests. I haven't checked it on a device yet; the PR lists the emulator steps.
test.dbfilename: this isn't a one-line rename, so I haven't opened a PR. The name is set in aw-server-rust (setup_local_remote,aw-sync/src/sync.rs), which desktop and Android share. Two things get in the way:- The Android SAF mirror (
mirrorDirectoryinSyncInterface.kt) only ever adds files. If the file is renamed locally, the oldtest.dbstays in the SAF folder next to the newsync.db. find_remotespicks up every*.dbin a device dir. A phone would then show up as two dbs with the same device_id, which is the aw-sync: duplicate folders for one device_id silently truncate history on pull aw-server-rust#683 truncation bug.
The safe order is:
- aw-server-rust renames
test.db→sync.dbtogether with its-wal/-shmsidecars, before the datastore opens. - The Android mirror deletes
*.dbfiles in its own<host>/<device_id>/that no longer exist locally. - Both land in the same Android release.
aw-sync statusalready warns about the name (status.rs), so nothing is broken in the meantime. Doing it in that order unless you'd rather keeptest.dbuntil sync v2 replaces the format (ActivityWatch/aw-server-rust#691).Sync warning, part (b): Android reads peers from its private sync dir because the SAF mirror only goes outward. Pulling peers from the SAF folder is new inbound-sync work, not a filter fix. For push-only phones, the case fix in #323 is enough to hide the warning.
Conflicting PRs for item 5: #309 locks Auth/Sync/Onboarding to a light-only theme, while #326 makes Sync settings follow dark mode. Your report asks for dark mode, so #326 is the right direction for Sync. #309 should drop
SyncSettingsActivity, or be closed if you want those screens to follow DayNight as well. Only one of them should merge.Item status:
Item Status 1. Data loss after fresh install ActivityWatch/aw-server-rust#792 (merged) + #324 2. ANR on fresh boot #325 3. Desktop-only tabs on Android ActivityWatch/aw-webui#1061: root cause is that --os=androidwas never parsed under the ESMvue.config.js, so every Android build shipped desktop defaults. Needs a web UI bump in aw-android after merge4. Week view not snapped to Monday ActivityWatch/aw-webui#1059 5. Sync settings ignore dark mode #326 (see conflict above) 6. Back-swipe finishes MainActivity #327 7. Unsupported aw-notify toast needs the web UI bump (same bump as item 3) 8. Multi-device shows 0 s check after the web UI bump Sync "Zero peers" warning #323 test.dbfilenameplan above - The Android SAF mirror (
- added 5 commits that reference this issue
on Oct 6, 2026 Item 3 (desktop tabs on Android): ActivityWatch/aw-webui#1061 (merged) fixes fresh installs only. Builds before it had already stored the desktop views on the server on any settings change, so existing users would keep the desktop tabs. ActivityWatch/aw-webui#1088 shows the Android views on Android when the stored views are an unmodified desktop default. Views the user edited are kept.
Neither fix is in the app yet. aw-android pins aw-server-rust
70ba50d, whose aw-webui is 38 commits behind #1061. Items 3, 7 and 8 need the next aw-webui bump in aw-server-rust, then an aw-server-rust bump in aw-android.- added a commit that references this issue
on Oct 10, 2026 - added a commit that references this issue
on Oct 10, 2026 - added a commit that references this issue
on Oct 10, 2026 #356 — aw-server-rust bump (items 3, 7, 8 + data-loss server layer)
#356 bumps aw-server-rust from c535d77 to 795b264 (9 commits).
Key changes:
- Items 3, 7, 8 (desktop tabs / aw-notify toast / multi-device 0 s) — webui bump (3b1c444) lands aw-webui#1061 + #1088: Android-only views on fresh install and stored desktop-default views upgraded to Android views.
- Item 1 (data loss) server layer — fix(android): open the JNI datastore exactly once (aw-server-rust#801) complements fix(server): make startServerTask() single-instance guard atomic #324 + aw-server-rust#792.
Updated item status after #356 merges: items 3, 7, 8 closed; item 1 has all three layers in place.
Final item status (2026-10-10):
Item Status 1. Data loss after fresh install ✅ #324 (atomic guard, MERGED) + aw-server-rust#792 (idempotent migration, MERGED) + #356 (JNI once-only, pending merge) 2. ANR on fresh boot ✅ #325 MERGED 3. Desktop-only tabs on Android ⏳ #356 pending merge (webui bump with #1061+#1088) 4. Week view not snapped to Monday ✅ aw-webui#1059 MERGED (picked up by #356 bump) 5. Sync settings ignore dark mode ✅ #326 MERGED 6. Back-swipe finishes MainActivity ✅ #327 MERGED 7. Unsupported aw-notify toast ⏳ #356 pending merge 8. Multi-device shows 0 s ⏳ #356 pending merge (verify after) Sync "Zero peers" warning ✅ #323 MERGED test.dbfilenamePlan in earlier comment; deferred to sync v2 Items 3, 7, 8 and the final data-loss layer all land with #356. 9/10 items resolved or pending one merge.
Found while scripting marketing screenshots of v0.14.2 (release APK, universal) on an Android 16 emulator (AVD on CT 102, fresh install each run, demo data imported through the app's REST API at localhost:5600 with the API key from the app's config.toml).
@TimeToBuildBob this one is yours (assigned by Erik). Item 1 first; it may affect real users.
/api/0/buckets/. Done when imported data survives a force-stop immediately after first launch.MainActivity.onResumewhile starting a thread. Move the blocking work off the main thread./week/<date>), probably the same root cause as UI bugs found while generating v0.14 marketing screenshots aw-webui#1058 item 1 (period not snapped to its start).@all, or several hosts) returns 0 s in the v0.14.2 web UI even with synced desktop buckets present; check after the web UI bump.Screenshot script (re-runnable, ~8 min): CT 102
/root/aw-screenshots/android_screenshots.py --wipe.