Summary
The data layer serves as the single source of truth for the application. It acts as a bridge between the raw Android system data (like screen time and app installs) and the user interface. Its primary job is to fetch raw data from the device, process it into meaningful statistics, store it locally for offline access, and synchronize app categories with a remote server.Data Flow
The application follows a “Sync and Store” pattern. It pulls raw data from the Android system, processes it, and saves it to a local database. The UI only reads from this local database to ensure the app remains fast and responsive.Ingestion
Repositories request raw data from Android system services (like
UsageStatsManager for screen time or PackageManager for installed apps).Processing & Storage
The data is cleaned, aggregated (e.g., summing up daily usage), and stored in the local . Icons are cached as files.
Repositories
Repositories are the managers of data. They decide whether to fetch new data from the system/network or return existing data from the database.ScrollDataRepository
What it manages: The core usage statistics of the app. Role: This is the heaviest repository. It syncs with the AndroidUsageStatsManager to retrieve raw app usage events. It processes these raw events into daily summaries, scroll sessions, and unlock counts. It also handles privacy redaction for notifications.
AppMetadataRepository
What it manages: Information about installed applications. Role: It detects when apps are installed or uninstalled using the systemPackageManager. It caches app names, version numbers, and saves app icons to the local file system so they load quickly.
AppCategoryRepository
What it manages: Categorization of apps (e.g., “Social”, “Productivity”). Role: It checks the local database for uncategorized apps. If found, it batches them and queries a Remote API. It handles offline scenarios by queuing sync jobs viaWorkManager.
LimitsRepository
What it manages: User-defined usage limits. Role: Allows the user to create groups of apps (e.g., “Social Media”) and set time limits. It manages the creation, updating, and deletion of these rules in the local database.UsageRepository
What it manages: Real-time usage calculation against limits. Role: A wrapper that combines historical data from the database with a live “in-memory” buffer to tell the UI exactly how much time is left for a specific app limit right now.JourneysRepository
What it manages: The “Timeline” or “Journey” view. Role: It aggregates disparate data points—unlocks, app usage, and notifications—into a linear timeline. It contains logic to merge rapid app switches into single cohesive “sessions” to reduce noise in the UI.BackupRepository
What it manages: Data export and import. Role: Reads all tables from the database, compresses them into a GZIP JSON file for backup, and handles the restoration process by clearing tables and re-inserting data transactionally.SettingsRepository
What it manages: User preferences. Role: A wrapper aroundSharedPreferences for simple flags like Theme (Dark/Light), Onboarding status, and Developer Mode toggles.
Data Sources
- Local Database
- Android System
- Remote API
- File System
The primary storage for the app. All UI data comes from here.
| Table | Content |
|---|---|
daily_app_usage | Aggregated time spent per app, per day |
raw_app_events | Raw system events (screen on/off, app open) |
app_metadata | Names, versions, and install status of apps |
limit_groups | User-defined rules for limiting app usage |
notifications | Log of received notifications (for analysis) |
Caching Strategy
The app employs a robust offline-first strategy.| Strategy | Description |
|---|---|
| System Mirroring | The app does not query the Android System for every UI update. Instead, it periodically “syncs” system data into the local database. The UI only reads the local database. |
| Icon Caching | App icons are extracted once upon install and saved as PNG files. The app loads these files instead of querying the system repeatedly, improving scroll performance. |
| Stale-while-revalidate | For app categories, the app displays existing local data immediately. If an app is “Uncategorized”, it schedules a background worker to fetch the category from the API without blocking the user. |
| In-Memory Projection | For real-time limits, the app combines the “stale” database numbers with a live in-memory buffer of the current session to provide instant feedback without constant database writes. |