Skip to main content

Overview

The ScrollLess module is a sophisticated digital wellbeing engine designed to combat mindless scrolling. Unlike standard screen-time trackers, it focuses on active engagement by measuring physical scroll distance and providing real-time interventions. It leverages Android’s to observe user behavior and for persistent limit enforcement and data synchronization.

Architecture & Data Flow

The module follows a pattern for the UI, while the background engine operates on a reactive observer-based architecture.

Key Components

1. The Engine (ScrollTrackService)

The heart of the module is the ScrollTrackService. It acts as the primary data collector, capturing events, app opens, and typing frequency. It doesn’t just log data; it orchestrates the “Blocking” UI when limits are exceeded.

2. The Brain (LimitMonitor)

The LimitMonitor is a that calculates the “Effective Usage” for the current foreground app. It accounts for:
  • Live Session Time: How long the app has been open right now.
  • Historical Usage: Data already persisted in the database.
  • Snooze Logic: Temporary allowances granted by the user.

3. The Enforcer (BlockingStateManager & OverlayEnforcer)

These components manage the “Locked” state. When the LimitMonitor determines a limit is hit, the BlockingStateManager updates a reactive . The ScrollTrackService observes this and triggers the OverlayEnforcer to draw a system-level barrier, preventing further app interaction.

4. The Data Layer (Repositories)

  • AppMetadataRepository: Manages the “Identity” of apps (icons, names, and whether they are user-visible).
  • AppCategoryRepository: Communicates with a remote API to classify apps (e.g., “Social” vs “Productivity”) to apply different heuristic weights.
  • LimitsRepository: Manages user-defined groups and time constraints.

Operational Logic

Initialization & Lifecycle

When the app starts, ScrollTrackApplication initializes the system:
  1. Worker Scheduling: Sets up UsageStatsWorker and LimitEnforcementWorker to run every 15 minutes as a safety net.
  2. Metadata Sync: Performs a full scan of installed apps to ensure the local database matches the device state.
  3. Service Toggle: Dynamically starts or stops the AppTrackerService (a fallback tracker) based on whether the Accessibility permission is granted.

Real-Time Enforcement

The enforcement loop is highly optimized to prevent “flicker” or bypasses:
  • Epsilon Buffering: The system uses an EPSILON_MS (usually 1-5 seconds) to trigger blocks slightly before the limit is hit, ensuring the user cannot “squeeze” extra time during transition animations.
  • Midnight Rollover: A dedicated handleMidnightRollover logic clears all blocks and resets daily counters exactly at 00:00, preventing stale blocks from the previous day.

The Blocking Experience

When a user hits a limit, they are presented with the BlockingActivity. This isn’t just a “Stop” sign; it’s a friction-based intervention:
  • Compassion/Reflection Phases: The “Snooze” button uses a to force the user to pause and reflect before gaining more time.
  • Contextual Stats: It shows exactly how many times the app was opened today, using data to highlight compulsive behavior.

Data Engineering & Transformation

Scroll Physics & Metrics

The module performs complex transformations to turn raw pixels into meaningful human metrics:
  • Pixel-to-Meter Conversion: Uses the device’s to translate screen pixels into physical meters of scrolling.
  • Inferred Scroll Heuristics: For apps that don’t report precise pixel deltas (like some custom lists), the system uses a sqrt(count) * multiplier model to estimate distance based on content change events.

Usage Projection

Because Android’s UsageStatsManager can have delays, the module uses a Projection Engine:
  1. Base Data: Queries the for usage up to the last minute.
  2. Live Delta: Adds the delta of the current foreground session.
  3. Result: A real-time “Usage Snapshot” that is more accurate than the system’s native reporting.

Dependencies

  • Hilt: Handles across Workers, Services, and ViewModels.
  • Timber: Centralized logging for debugging complex background state transitions.
  • Retrofit: Used by the AppCategoryRepository to fetch app classifications from the backend.
  • Coroutine/Flow: Manages all asynchronous data streams and background processing.
Last modified on January 22, 2026