Summary
The application follows a modern MVVM (Model-View-ViewModel) architecture with a strong emphasis on Offline-First data persistence. It is structured to separate the user interface from the business logic and data handling. The app relies heavily on background services and workers to track usage data reliably, even when the app is closed. Dependency Injection (Hilt) is used throughout to wire these components together efficiently.Architecture Diagram
This diagram illustrates how data flows from the system and user inputs through the app layers to the database and UI.Layers
UI Layer
What it does: Displays data to the user and captures interactions. Key Parts:- Screens: Built with Jetpack Compose (implied).
- ViewModels: Holds the state for the screens. They survive configuration changes (like screen rotation) and act as the bridge between the UI and the Data Layer.
Data Layer
What it does: Acts as the “Single Source of Truth.” It handles where data comes from (local database vs. network) and how it is stored. Key Parts:- Repositories: The main entry point for data. ViewModels ask Repositories for data, and Repositories decide whether to fetch it from the API or the Database.
- Room Database: A robust local SQL database that stores usage stats, limits, and app metadata.
- Remote API: Used primarily for syncing app categories and metadata.
Background Layer
What it does: Performs heavy lifting without blocking the user interface, ensuring data tracking continues even when the app is in the background. Key Parts:- WorkManager: Handles scheduled tasks like daily processing, database cleanup, and syncing.
- Foreground Services: Runs persistent processes (like
AppTrackerService) to monitor app usage in real-time.
Key Patterns
MVVM
separates the UI code from the logic. The ViewModel prepares data for the UI, keeping the screens “dumb” and focused only on drawing.
Repository Pattern
Repositories abstract the data sources. The rest of the app doesn’t know (or care) if data comes from the network or a local database.
Dependency Injection
Using , the app automatically provides necessary objects (like the Database or Network Client) to classes, making the code modular and testable.
Offline-First
The app prioritizes the local database. Network calls (like syncing categories) happen in the background to update the local data, ensuring the app works perfectly without internet.
Technology Stack
| Category | Technology | Purpose |
|---|---|---|
| Language | Kotlin | Primary programming language. |
| DI | Hilt (Dagger) | Managing dependencies and scoping. |
| Database | Room | Local SQLite abstraction with complex migrations. |
| Network | Retrofit + OkHttp | Communicating with backend APIs. |
| Concurrency | Coroutines + Flow | Asynchronous programming and reactive data streams. |
| Background | WorkManager | Reliable scheduled tasks (e.g., daily summaries). |
Dependency Injection Structure
The app uses Hilt to manage the object graph.- Singleton Component (
AppSingletonProvides): Objects that live as long as the app runs.- Database: Created once with extensive migration logic.
- Network: Retrofit and OkHttp clients.
- Dispatchers: Threading contexts (IO, Main).
- ViewModel Component (
ViewModelModules): Objects that live as long as a specific screen.- Delegates: Helper logic specific to UI flows.
Database Management
TheAppDatabase is a critical component, featuring a robust migration strategy (currently tracking versions v28 through v44). This ensures that as the app evolves and data structures change, user data is preserved and upgraded safely without crashes or data loss.