Skip to content

fix(ios): screentime compatibility patch with Greptile review fixes - #917

Merged
ErikBjare merged 9 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/ios-screentime-compat
Jul 24, 2026
Merged

fix(ios): screentime compatibility patch with Greptile review fixes#917
ErikBjare merged 9 commits into
ActivityWatch:masterfrom
TimeToBuildBob:fix/ios-screentime-compat

Conversation

@TimeToBuildBob

Copy link
Copy Markdown
Contributor

This is a follow-up to #740 (by @Guracc), taking over at @ErikBjare's request to incorporate the Greptile review feedback before merging.

What this PR does

Adds iOS compatibility for ActivityWatch WebUI with data imported via the aw-import-screentime utility. Users see human-readable app names (e.g. "YouTube") instead of bundle IDs (e.g. com.google.ios.youtube), and a new "Bundle IDs" visualization lets them drill down to raw identifiers.

Changes from #740

The original PR was functionally correct but had three issues flagged by Greptile:

1. Events without title were silently skipped, causing a data mismatch

The original code only remapped events that had a title field, but all events were already included in the server-side aggregation. Events without a title fell through with their original bundle ID as app, mixing bundle IDs with human names in the aggregated output.

Fix: always remap — set classname = original bundle ID and app = title || original bundle ID. Events without a human-readable title now appear under their bundle ID rather than being inconsistently mixed.

2. app_events re-aggregation discarded classname and title fields

The original re-aggregation created events with only { app, $category }, losing classname and title. This broke the Bundle IDs visualization that reads e.data.classname, and generally made app_events structurally inconsistent with title_events.

Fix: preserve classname, title, and $category in the re-aggregated app_events.

3. Generic 'app' bucket type filter lacked explanatory context

bucketsByType(host, 'app') was already narrowed by .startsWith('aw-import-screentime'), so there was no functional bug, but the 'app' type selection looked like it could match unintended buckets.

Fix: added a comment explaining that 'app' is the screentime bucket type and the name prefix guard is the specificity mechanism.

Files changed

  • src/stores/activity.ts — iOS post-processing and re-aggregation fixes
  • src/stores/buckets.ts — explanatory comment on bucket type filter
  • src/stores/buckets.ts, src/queries.ts, src/components/SelectableVisualization.vue — original changes from Compatibility patch for aw-import-screentime #740 (rebased onto current master)

Testing

Import iPhone screen time data via aw-import-screentime and verify:

  • Top Applications: shows readable names ("YouTube"), not bundle IDs
  • Top Window Titles: shows readable titles
  • Bundle IDs: shows raw com.google.ios.youtube-style identifiers
  • Apps without a title field appear under their bundle ID (not dropped)

Guracc and others added 3 commits July 24, 2026 14:31
- buckets: detect 'aw-import-screentime' as android-compatible buckets
- queries: preserve 'title' attribute during android event merging
- activity: add post-processing to support iOS attributes (mapping 'title' to 'app' for readability)
- visualization: add 'Bundle IDs' view for technical detail while keeping 'Top Apps' human-readable
- Remap all iOS events regardless of title presence (use bundle ID as fallback
  app name) to prevent data mismatch with server-side aggregations
- Preserve classname and title fields in re-aggregated app_events so downstream
  visualizations (Bundle IDs view) can access them
- Add comment explaining that 'app' bucket type filter is guarded by name prefix
  to clarify intent and avoid confusion with other 'app'-type buckets
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 3.33333% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 38.11%. Comparing base (5dc2a8d) to head (5f149cf).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/stores/activity.ts 0.00% 23 Missing and 1 partial ⚠️
src/stores/buckets.ts 0.00% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #917      +/-   ##
==========================================
- Coverage   38.55%   38.11%   -0.45%     
==========================================
  Files          42       42              
  Lines        2233     2259      +26     
  Branches      439      454      +15     
==========================================
  Hits          861      861              
- Misses       1294     1319      +25     
- Partials       78       79       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Adds iOS Screen Time support across bucket discovery, query aggregation, activity-store post-processing, and selectable visualizations.

  • Recognizes aw-import-screentime app buckets and exposes iOS availability.
  • Maps bundle IDs to readable application names while retaining raw identifiers.
  • Adds a Bundle IDs visualization and keeps hybrid mobile query paths aligned.

Confidence Score: 5/5

The PR appears safe to merge with no unresolved blocking failures related to the previous review threads.

No blocking failure remains.

Important Files Changed

Filename Overview
src/components/SelectableVisualization.vue Adds the iOS-only Bundle IDs summary and availability metadata.
src/queries.ts Preserves Screen Time title dimensions during Android-style event aggregation.
src/stores/activity.ts Adds iOS detection, bundle-ID remapping, and consistent Screen Time bucket preference across mobile query paths.
src/stores/buckets.ts Discovers Screen Time import buckets alongside Android watcher buckets.

Reviews (6): Last reviewed commit: "fix(ios): use ScreenTime-preferred bucke..." | Re-trigger Greptile

Comment thread src/stores/activity.ts Outdated
Comment thread src/components/SelectableVisualization.vue
Comment thread src/stores/buckets.ts
@@ -116,10 +116,17 @@ export const useBucketsStore = defineStore('buckets', {
);
},
bucketsAndroid(): (host: string) => string[] {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like it's incorrect to call screentime/iOS buckets for Android buckets. They probably deserve their own thing.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — using 'android' as the umbrella for both Android watchers and iOS ScreenTime imports is awkward naming. I've taken a first step in 0918119 by adding a dedicated ios.available flag to the activity store, derived from whether any android bucket starts with aw-import-screentime. This lets visualizations distinguish iOS/ScreenTime from regular Android without touching the existing query paths. A fuller rename of bucketsAndroid/android.available could be a follow-up.

:namefunc="e => e.data.title",
:colorfunc="e => e.data['$category']",
with_limit)
div(v-if="type == 'top_bundle_ids' && activityStore.android.available")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wouldn't work for Android devices, but would still show up as available for them.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0918119. Added ios.available to the activity store (true only when a ScreenTime bucket is present) and gated both the top_bundle_ids availability check and the template render condition on ios.available instead of android.available. Regular Android users no longer see Bundle IDs in the dropdown.

Two P1 issues found by Greptile:

1. top_titles widget was marked available when android.available=true, but
   the template only renders it when !android.available — so iOS users saw
   an empty widget. Revert available to window.available only.

2. App totals were re-aggregated from title_events (capped at 100 entries),
   discarding apps beyond the top 100. Fix: build a bundle-ID→name lookup
   from title_events, then remap app_events in-place so the server's
   complete aggregation is preserved.
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Pushed two fixes addressing the P1 findings from Greptile (score 3/5):

1. Mobile title widget stays blank (SelectableVisualization.vue)

top_titles.available was including android.available, but the template only renders Top Window Titles when !activityStore.android.available. This meant iOS users would see the widget in the dropdown but get a blank render. Fixed by reverting top_titles.available to depend on window.available only — the top_bundle_ids widget is already gated on android.available for the iOS/Android case.

2. Truncated events corrupt app totals (activity.ts)

App totals were re-aggregated from title_events, which the query caps at 100 entries. For periods with >100 distinct app/title groups, apps beyond the top 100 were silently dropped and retained apps showed understated durations.

Fix: build a bundle-ID → human-name lookup from title_events first, then remap app_events in-place using that lookup. This preserves the server's complete app aggregation (which already accounts for all events) and only applies the human-readable name mapping on top.

Re-triggered Greptile to re-score.

Comment thread src/stores/activity.ts
Comment thread src/components/SelectableVisualization.vue
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread src/components/SelectableVisualization.vue Outdated
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread src/stores/activity.ts Outdated
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

Comment thread src/stores/activity.ts
@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

@greptileai review

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

Merge recommendation — converged after 6 Greptile rounds

All blocking P1 findings have been addressed. Here's the summary:

Fixed (blocking):

  • Truncated app totals: reverted to server-side aggregation, removed re-aggregation from top-100 title events
  • Bundle IDs visible on desktop: gated on android.available only (not window.available || android.available)
  • Hybrid-host iOS bucket skipped: query_android now explicitly searches for ScreenTime bucket first
  • Hybrid-host active-history + category-by-period inconsistency: both query paths now use the same iosBucket || android[0] selection as query_android (fixed in 5f149cf)

Addressed / acknowledged (non-blocking):

  • "Truncated lookup loses app names": ScreenTime has 1:1 bundle-ID→name mapping so top-100 rankings are identical; documented with comment in code
  • Naming (android.available vs ios.available): added ios.available flag for ScreenTime-specific gating; full rename of bucketsAndroid/android.available would be a larger follow-up

Remaining (acknowledged, non-blocking):

  • android.available is still used as the umbrella flag for Android watchers; the new ios.available flag narrows ScreenTime-specific paths. A full rename is a follow-up — noted in the thread.

CI: Greptile ✅, lint ✅, build ✅, CodeQL ✅, tests ✅ (2 pending but same pattern as previous commits which passed)

Domain risk: Bundle IDs visualization and iOS name remapping can only be fully validated with real ScreenTime import data — standard manual test would be importing via aw-import-screentime and verifying the three visualizations.

@TimeToBuildBob

Copy link
Copy Markdown
Contributor Author

CI-green and mergeable (Greptile 5/5) — waiting only on a maintainer click.

This PR is ready to merge, but the bot has pull-only access to this repo and can't self-merge — surfacing it here so it isn't lost. The monitoring loop will stop re-flagging it now that this note is posted.

@ErikBjare
ErikBjare merged commit bf0fc84 into ActivityWatch:master Jul 24, 2026
9 checks passed
TimeToBuildBob added a commit to TimeToBuildBob/aw-webui that referenced this pull request Jul 25, 2026
…ctivityWatch#917)

* feat(ios): add support for iOS data visualization

- buckets: detect 'aw-import-screentime' as android-compatible buckets
- queries: preserve 'title' attribute during android event merging
- activity: add post-processing to support iOS attributes (mapping 'title' to 'app' for readability)
- visualization: add 'Bundle IDs' view for technical detail while keeping 'Top Apps' human-readable

* Apply suggestion from @ErikBjare

* fix(ios): address Greptile review on screentime compatibility patch

- Remap all iOS events regardless of title presence (use bundle ID as fallback
  app name) to prevent data mismatch with server-side aggregations
- Preserve classname and title fields in re-aggregated app_events so downstream
  visualizations (Bundle IDs view) can access them
- Add comment explaining that 'app' bucket type filter is guarded by name prefix
  to clarify intent and avoid confusion with other 'app'-type buckets

* fix(lint): reformat data object to satisfy prettier line-length rule

* fix(ios): fix truncated app totals and blank titles widget

Two P1 issues found by Greptile:

1. top_titles widget was marked available when android.available=true, but
   the template only renders it when !android.available — so iOS users saw
   an empty widget. Revert available to window.available only.

2. App totals were re-aggregated from title_events (capped at 100 entries),
   discarding apps beyond the top 100. Fix: build a bundle-ID→name lookup
   from title_events, then remap app_events in-place so the server's
   complete aggregation is preserved.

* fix(ios): gate Bundle IDs on android.available and document name-lookup invariant

* fix(ios): add ios.available flag, gate Bundle IDs on iOS/ScreenTime only not all Android

* fix(ios): prefer ScreenTime bucket over Android watcher in hybrid-host query

* fix(ios): use ScreenTime-preferred bucket in activeHistory and categoryByPeriod queries

---------

Co-authored-by: Guracc <garaccioni.andrea@gmail.com>
Co-authored-by: Erik Bjäreholt <erik.bjareholt@gmail.com>
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.

3 participants