The data layer is the foundation of the application, designed to be offline-first. It captures high-frequency user interactions (like scrolling, unlocking, and app usage) and stores them locally on the device. To ensure the app remains fast, this layer automatically transforms raw, heavy data logs into lightweight daily summaries and timelines that the UI can display instantly.
Show Data Layer Files
Database Config: AppDatabase.kt
Raw Data Entities: RawAppEvent.kt, ScrollSessionRecord.kt, NotificationRecord.kt, UnlockSessionRecord.kt
The application uses a “write-heavy, read-aggregated” architecture. Raw events flow in from system services, are stored immediately, and are later processed into optimized tables for the UI.
1
Data Ingestion
The app listens to Android system events (accessibility, usage stats, notifications) and writes them directly to Raw Event Tables (e.g., raw_app_events, scroll_sessions).
2
Aggregation
Because raw tables grow very large, the app calculates totals (like “Total time on Instagram today”) and saves them into Summary Tables (e.g., daily_app_usage).
3
UI Consumption
The screens observe these Summary Tables. When the UI needs a timeline, it reads from the User Journey table, which is a pre-built view of the day’s events optimized for scrolling performance.
The app employs a strict Local-First strategy with specific rules for data retention and aggregation.
Strategy
Description
Aggressive Aggregation
Raw events are heavy. The app calculates daily totals (DailyAppUsageRecord) continuously so it doesn’t have to sum up millions of raw rows every time the user opens the dashboard.
Materialization
Complex views (like the daily timeline) are pre-calculated and stored in UserJourney. If the raw data changes, this table is regenerated.
Data Pruning
To save storage space, raw high-frequency data (like RawAppEvent) can be deleted after a certain period, while the aggregated summaries (DailyDeviceSummary) are kept indefinitely for long-term statistics.
Category Sync
AppCategory uses a status flag (NEEDS_CHECK, SYNCED, FAILED) to manage synchronization with a remote API without blocking the UI.