The navigation module (and its associated UI features) serves as the architectural backbone of the ScrollLess application. It is responsible for orchestrating the across five primary domains: Dashboard, Insights, Limits, App Details, and Settings.The feature’s primary value proposition is transforming abstract “screen time” into physical metrics (e.g., “You scrolled 2.5 kilometers today”) and providing the tools (Limits/Nudges) to modify that behavior.
ScrollLess employs a robust reactive architecture. Data originates in local databases, is processed by specialized , and is finally synthesized into UI-ready models by ViewModels.
A critical pattern observed in ScrollDetailViewModel and TodaySummaryViewModel is the use of combine and flatMapLatest. These ViewModels do not just fetch data; they perform “Data Join” operations in memory:
Raw Usage: How many milliseconds was the app open?
Scroll Metrics: How many pixels/micrometers were traversed?
Metadata: What is the app’s name and icon?
Limits: Is there an active restriction on this app?
The AppNavigationHost acts as a gatekeeper. Upon launch, it queries the SettingsRepository for onboardingCompleted. If false, the user is funneled into the SetupScreen. Once true, the DASHBOARD_GRAPH_ROUTE becomes the default entry point.
The InsightsViewModel implements a pre-fetching strategy. In its init block, it calls calculateAllHistories(), which populates a ConcurrentHashMap cache. This ensures that when a user taps an insight card, the detailed historical chart opens instantly without a loading spinner.
Limits are handled via a LimitViewModelDelegate. This allows multiple screens (Dashboard, App Detail, Scroll Detail) to share the same logic for “Quick Limits.” When a user sets a limit, the delegate interacts with the LimitsRepository, which likely triggers a task or a background service to monitor usage.
The module performs significant data transformation to ensure user-friendliness:
Scroll Normalization: Raw scroll data is often stored in pixels. The ConversionUtil uses the calibratedDpi (from CalibrationScreen) to convert these into micrometers.
Trend Analysis: The TrendAnalyzer (used in InsightsViewModel) looks at 7-day windows to determine if a user’s “Context Switching” or “Notification Response Time” is improving or degrading.
Treemap Logic: The NotificationTreemapScreen uses a custom algorithm to group low-frequency apps into an “Other” category, ensuring the UI remains readable while still accounting for 100% of notification volume.
Jetpack Navigation: Handles the backstack and argument passing (e.g., passing packageName to AppDetailScreen).
Hilt: Provides for all ViewModels.
Coil: Used for asynchronous image loading of app icons from the filesystem.
Kotlin Coroutines/Flow: The primary mechanism for asynchronous data streaming from the DB to the UI.
Copy
// Example of the complex state combining found in the moduleval uiState: StateFlow<ScrollDetailUiState> = combine( _selectedDate, _period, repository.getTotalScrollPerDay(), visibleAppsFlow, allLimitedApps) { ... }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ScrollDetailUiState())
Copy
---**Architect's Note**: The separation of `CreateLimitGroupScreen` and `EditLimitGroupScreen` into distinct routes, despite sharing a ViewModel, is a deliberate choice to prevent "State Bleeding" where data from a previous edit session might accidentally populate a new creation form.---