Skip to content

feat(web-watcher): set audible on browser events from media sessions - #299

Draft
0xbrayo wants to merge 2 commits into
ActivityWatch:masterfrom
0xbrayo:feat/web-watcher-audible
Draft

0xbrayo wants to merge 2 commits into
ActivityWatch:masterfrom
0xbrayo:feat/web-watcher-audible

Conversation

@0xbrayo

@0xbrayo 0xbrayo commented Sep 20, 2026

Copy link
Copy Markdown
Member

WebWatcher hard-codes audible=false on every aw-watcher-android-web event (// TODO). aw-webui uses audible browser events as not-AFK evidence (include_audible in queries.ts), so the field was worthless on Android.

Changes

  • BrowserAudibleDetector — reports whether the browser package owns a media session in STATE_PLAYING via MediaSessionManager.getActiveSessions(). This is the precise per-app signal (Chrome/Firefox publish a media session for page audio/video) but needs the existing MediaWatcher notification-listener access. Without that access it falls back to AudioManager.isMusicActive(), which is global and can't tell browser audio from a background music app. Result is cached for 1s since accessibility events arrive many times a second while scrolling.
  • BrowserSessionTracker — carries audible per session and splits the session when it flips, so each logged event has the right value for its stretch of time (desktop aw-watcher-web likewise starts a new event when data changes). Url, browser and title carry over across the split.
  • WebWatcher — samples audible on every browser accessibility event and feeds it to the tracker.

Known limitations

  • incognito is still hard-coded to false; detecting it needs per-browser accessibility-tree heuristics and is out of scope here.
  • Audible is sampled on accessibility events, so if the user starts a video and then doesn't touch the screen, the split lands on the next event rather than at the exact play/pause moment.
  • Chrome only publishes a media session for media longer than ~5s, so short sound effects won't register (same as the notification-based MediaWatcher).

Addresses the audible half of #106.

Validation

:mobile:testStandardDebugUnitTest --tests 'net.activitywatch.android.watcher.*': 39 tests pass (4 new tracker tests, 2 new detector tests). Not yet exercised on a device — draft until I've checked Chrome + Firefox with and without notification access.

WebWatcher hard-coded audible=false on every aw-watcher-android-web event.
aw-webui uses audible browser events as not-AFK evidence, so the field
was worthless on Android.

BrowserAudibleDetector reports whether the browser package owns a media
session in STATE_PLAYING via MediaSessionManager, which is the precise
per-app signal but needs the MediaWatcher notification-listener access.
Without that access it falls back to AudioManager.isMusicActive(), which
is global and can't tell browser audio from a background music app. The
result is cached for one second because accessibility events arrive many
times a second while scrolling.

BrowserSessionTracker now carries audible per session and splits the
session when it flips, so each logged event has the right value for its
stretch of time (the desktop web watcher likewise starts a new event
when data changes). Url, browser and title carry over across the split.

incognito is still hard-coded to false; detecting it needs per-browser
accessibility-tree heuristics and is left for a follow-up.
@greptile-apps

greptile-apps Bot commented Sep 20, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the previously reported correctness defects are fixed and no new actionable failures remain.

Summary

This PR derives browser audibility from active media sessions and records audibility changes as browser-session boundaries.

  • Adds notification-access-gated, per-browser media-session detection with a short cache to limit main-thread Binder calls.
  • Extends browser sessions and web event payloads with the detected audible state.
  • Splits an active page session when playback starts or stops, including when the URL remains unchanged.
  • Adds tracker tests covering defaults, transitions, state preservation, same-URL changes, and inactive sessions.

Diagram

sequenceDiagram
    participant Android as Accessibility event
    participant Web as WebWatcher
    participant Detector as BrowserAudibleDetector
    participant Media as MediaSessionManager
    participant Tracker as BrowserSessionTracker
    participant Store as Web event bucket

    Android->>Web: Browser package, URL, and title
    Web->>Detector: isAudible(browser package)
    Detector->>Media: getActiveSessions(listener)
    Media-->>Detector: Active media controllers
    Detector-->>Web: Browser audible state
    Web->>Tracker: Update URL and audible state
    alt URL or audible state changed
        Tracker-->>Web: Completed prior session
        Web->>Store: Write URL, title, duration, audible
    else Context unchanged
        Tracker-->>Web: No completed session
    end
Loading

Reviews (2) · Last reviewed commit: "fix(web-watcher): split on same-url audi..."

Comment thread mobile/src/main/java/net/activitywatch/android/watcher/BrowserAudibleDetector.kt Outdated
… fallback

handleUrl returned early when the url and browser were unchanged, so an
audible transition sampled on a same-page event was discarded and the
session logged with a stale value. Same-page calls now route through
handleAudible.

The AudioManager.isMusicActive() fallback is removed: it is device-wide,
so a background music app would have marked silent browser sessions
audible, which aw-webui counts as not-AFK evidence. Without notification
access audible is now always false, as before this change.
@0xbrayo

0xbrayo commented Sep 20, 2026

Copy link
Copy Markdown
Member Author

@greptile review

@ErikBjare

ErikBjare commented Sep 21, 2026

Copy link
Copy Markdown
Member

@0xbrayo I'm wondering if we should just keep a separate "audible" bucket and not make it browser-only. There is no "AFK" bucket/state on Android anyway to filter by, so it doesn't affect filtering.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants