The application employs an Offline-First strategy where the is the local database.The AppUiModelMapper acts as the bridge between the raw outputs and the UI layer. It enriches usage data with metadata (icons, names) and real-time limit calculations (remaining time, pause status).
AppUiModelMapper: The central logic hub that converts DailyAppUsageRecord and AppScrollData into UI models. It performs complex joins in-memory to attach LimitInfo to every app item.
AppMetadataRepository: Manages the lifecycle of app-specific information. It synchronizes the local database with the Android PackageManager.
LimitsRepository: Handles the persistence and retrieval of user-defined time limits and app groupings.
The data layer is optimized to minimize expensive system calls (like querying the PackageManager) by caching metadata.
AppMetadata Table: Stores package names, version codes, and visibility flags.
Heuristic Visibility: The system determines if an app is “User Visible” by checking for a launcher intent.
Version Tracking: Uses versionCode to detect app updates and trigger metadata refreshes.
LimitGroup & LimitedApp: Implements a One-to-Many relationship. A LimitGroup defines a shared time bucket, and multiple LimitedApp entries link specific packages to that bucket.
Performance Optimization: The mapper provides “No-DB” variants (e.g., mapToAppOpenUiItemNoDb) to prevent when rendering large lists.
Icon Cache: App icons are not stored in the database. They are persisted as .png files in the internal app_icons directory. The database only stores an isIconCached boolean.
Write-Through Metadata: When an app is installed or updated, the AppMetadataRepository immediately fetches the new info and updates the local cache.
Cleanup Logic: The LimitsRepository includes a cleanupUninstalledApps routine to prevent orphaned limit records for apps no longer on the device.
The module is designed to be safe for UI consumption while performing heavy I/O.
Dispatcher Policy: All mapping operations are wrapped in withContext(Dispatchers.IO) to ensure the remains responsive during icon loading or database lookups.
Asynchronous Streams: Repositories expose data using , allowing the UI to reactively update when limits are changed or apps are uninstalled.
Atomic Transactions: Complex operations, such as moving an app between limit groups, are wrapped in appDatabase.withTransaction to prevent .