Summary
The application uses a local Room Database (SQLite) named scroll_track_database. It is an offline-first architecture designed to handle high-frequency event logging (scrolling, app usage) and aggregate them into daily summaries. The schema is currently at Version 44 and includes extensive migration logic to preserve user data across updates.
/data/local/
├── AppDatabase.kt - Database definition & Migrations
├── entities/
│ ├── LimitOutcomeEntity.kt
│ ├── SnoozeHistoryEntity.kt
│ └── … (Inferred from DAOs)
├── dao/
│ ├── ScrollSessionDao.kt
│ ├── RawAppEventDao.kt
│ ├── NotificationDao.kt
│ └── …
└── converters/
└── Converters.kt
Entity Relationship Diagram
Tables
Purpose: Stores discrete sessions of scrolling activity within specific apps. Used to calculate total distance scrolled.
Entity Class: ScrollSessionRecord
Primary Key: id (Auto-increment)
Columns
Column Type Nullable Default Description id INTEGER No Auto-inc Unique identifier packageName TEXT No - Package name of the app where scrolling occurred scrollAmount INTEGER No - Total distance scrolled (units depend on unit_type) scrollAmountX INTEGER No - Horizontal distance scrolled scrollAmountY INTEGER No - Vertical distance scrolled dataType TEXT No - Source of data (e.g., “MEASURED”, “INFERRED”) sessionStartTime INTEGER No - Unix timestamp (ms) when scrolling started sessionEndTime INTEGER No - Unix timestamp (ms) when scrolling stopped dateString TEXT No - Local date (YYYY-MM-DD) for aggregation sessionEndReason TEXT No - Why the session ended (e.g., “TIMEOUT”, “APP_CHANGE”) accuracy_type TEXT No ’UNKNOWN’ Added v40: ‘PRECISE’ or ‘ESTIMATED’ unit_type TEXT No ’PIXELS’ Added v40: ‘PIXELS’ or ‘MICROMETERS’
Indices
Index Name Columns Unique Purpose Implicit dateString No Fast lookup for daily stats
raw_app_events
Purpose: The raw log of all system events (screen on/off, app opens, notifications). This is the source of truth for all aggregations.
Entity Class: RawAppEvent
Primary Key: id (Auto-increment)
Columns
Column Type Nullable Default Description id INTEGER No Auto-inc Unique identifier packageName TEXT No - Package name associated with the event className TEXT Yes - Specific activity class name (if applicable) eventType INTEGER No - Event code (see Enums below) eventTimestamp INTEGER No - Unix timestamp (ms) eventDateString TEXT No - Local date (YYYY-MM-DD) source TEXT No - Source: “USAGE_STATS”, “ACCESSIBILITY”, “NOTIFICATION” value INTEGER Yes - Generic value (e.g., scroll distance, impact score) scrollDeltaX INTEGER Yes - Raw X delta for scroll events scrollDeltaY INTEGER Yes - Raw Y delta for scroll events accuracy_type TEXT Yes NULL Added v39: ‘PRECISE’ or ‘ESTIMATED’
Purpose: Caches details about installed applications to avoid repeated PackageManager calls.
Entity Class: AppMetadata
Primary Key: packageName
Columns
Column Type Nullable Default Description packageName TEXT No - Unique package ID (e.g., com.android.chrome) appName TEXT No - Human readable label versionName TEXT Yes - App version string versionCode INTEGER No - App version number isSystemApp INTEGER No - Boolean (0/1): Is it a pre-installed system app? isInstalled INTEGER No - Boolean (0/1): Is it currently installed? isIconCached INTEGER No - Boolean (0/1): Is the icon saved to disk? appCategory INTEGER No - Legacy category ID (superseded by app_categories table) isUserVisible INTEGER No - Boolean (0/1): Does it have a launcher activity? userHidesOverride INTEGER Yes NULL Boolean (0/1): User manually hid/showed this app installTimestamp INTEGER No - When the app was installed lastUpdateTimestamp INTEGER No - When metadata was last refreshed
unlock_sessions
Purpose: Tracks every time the device is unlocked, how long it stays unlocked, and what app was opened first.
Entity Class: UnlockSessionRecord
Primary Key: id (Auto-increment)
Columns
Column Type Nullable Default Description id INTEGER No Auto-inc Unique identifier unlockTimestamp INTEGER No - When the screen was unlocked lockTimestamp INTEGER Yes - When the screen was locked again durationMillis INTEGER Yes - Total duration of the session dateString TEXT No - Local date (YYYY-MM-DD) firstAppPackageName TEXT Yes - The first app used after unlocking triggeringNotificationKey TEXT Yes - Key of notification that caused the unlock triggeringNotificationPackageName TEXT Yes - Package of the triggering notification unlockEventType TEXT No - Type of unlock event sessionType TEXT Yes - ”Glance”, “Intentional”, etc. sessionEndReason TEXT Yes - Why session ended (e.g., “SCREEN_OFF”) isCompulsive INTEGER No - Boolean (0/1): Was this a rapid re-check?
daily_device_summary
Purpose: High-level daily statistics for the entire device.
Entity Class: DailyDeviceSummary
Primary Key: dateString
Columns
Column Type Nullable Default Description dateString TEXT No - Local date (YYYY-MM-DD) totalUsageTimeMillis INTEGER No - Total screen-on time totalUnlockedDurationMillis INTEGER No - Total time device was unlocked totalUnlockCount INTEGER No - Number of unlocks totalNotificationCount INTEGER No - Number of notifications received lastUpdatedTimestamp INTEGER No - When this record was last calculated totalAppOpens INTEGER No - Total app launch events firstUnlockTimestampUtc INTEGER Yes - Time of first unlock lastUnlockTimestampUtc INTEGER Yes - Time of last unlock intentionalUnlockCount INTEGER No - Count of long sessions glanceUnlockCount INTEGER No - Count of short sessions
daily_app_usage
Purpose: Aggregated usage statistics per app, per day.
Entity Class: DailyAppUsageRecord
Primary Key: id (Auto-increment)
Columns
Column Type Nullable Default Description id INTEGER No Auto-inc Unique identifier packageName TEXT No - App identifier dateString TEXT No - Local date (YYYY-MM-DD) usageTimeMillis INTEGER No - Total foreground time activeTimeMillis INTEGER No - Total time with user interaction appOpenCount INTEGER No - Number of times launched notificationCount INTEGER No - Number of notifications from this app lastUpdatedTimestamp INTEGER No - When this record was last calculated
daily_insights
Purpose: Stores calculated behavioral insights (e.g., “Night Owl”, “Most Scrolled”).
Entity Class: DailyInsight
Primary Key: id (Auto-increment)
Columns
Column Type Nullable Default Description id INTEGER No Auto-inc Unique identifier dateString TEXT No - Local date (YYYY-MM-DD) insightKey TEXT No - Key (e.g., “busiest_unlock_hour”) stringValue TEXT Yes - Text value (e.g., package name) longValue INTEGER Yes - Numeric value (e.g., timestamp, count) doubleValue REAL Yes - Floating point value (e.g., ratio)
notifications
Purpose: Stores history of incoming notifications. Recreated in Migration 38.
Entity Class: NotificationRecord
Primary Key: id (Auto-increment)
Columns
Column Type Nullable Default Description id INTEGER No Auto-inc Unique identifier notification_key TEXT No - System notification key package_name TEXT No - App that posted the notification post_time_utc INTEGER No - When it was posted title TEXT Yes - Notification title (redacted if setting enabled) text TEXT Yes - Notification body (redacted if setting enabled) impact INTEGER No - 0=Passive, 1=Visual, 2=Interruption type INTEGER No - 0=Standard, 1=Conversation, 2=Service state INTEGER No - 0=Posted, 1=Dismissed, 2=Clicked, 4=Seen group_id TEXT Yes - System group key for bundling date_string TEXT No - Local date (YYYY-MM-DD) is_batched INTEGER No 0 Boolean (0/1): Was it intercepted by Postbox? delivered_at_utc INTEGER Yes - When it was delivered (if batched)
Indices
Index Name Columns Purpose index_notifications_notification_key notification_key Lookup by system key index_notifications_date_string date_string Filtering by date index_notifications_is_batched_delivered is_batched, delivered_at_utc Filtering pending batch items
limit_groups
Purpose: Defines user-created groups of apps that share a time limit.
Entity Class: LimitGroup
Primary Key: id (Auto-increment)
Columns
Column Type Nullable Default Description id INTEGER No Auto-inc Unique identifier name TEXT No - User-defined name (e.g., “Social”) time_limit_minutes INTEGER No - Daily allowance in minutes is_enabled INTEGER No - Boolean (0/1) group_type TEXT No - Enum: “CUSTOM_GROUP”, “QUICK_LIMIT” creation_timestamp INTEGER No - When created paused_until_timestamp INTEGER Yes - If paused, when it resumes pause_reason TEXT Yes - User reason for pausing
limited_apps
Purpose: Maps individual apps to a Limit Group.
Entity Class: LimitedApp
Primary Key: package_name
Columns
Column Type Nullable Default Description package_name TEXT No - App package name group_id INTEGER No - Foreign Key to limit_groups.id
Relationships
Foreign Key References Relationship On Delete group_id limit_groups.id Many-to-One CASCADE
limit_outcomes
Purpose: Tracks daily success/failure for limit groups.
Entity Class: LimitOutcomeEntity
Primary Key: Composite (date_string, group_id)
Columns
Column Type Nullable Default Description date_string TEXT No - Local date (YYYY-MM-DD) group_id INTEGER No - ID of the limit group limit_minutes INTEGER No - The limit setting on that day total_used_ms INTEGER No - Actual usage exceeded_by_ms INTEGER No - Amount over limit (0 if success) success INTEGER No - Boolean (0/1) snooze_count INTEGER No 0 How many times user snoozed last_updated_ts INTEGER No - Timestamp of last update
snooze_history
Purpose: Tracks individual snooze attempts to enforce exponential friction.
Entity Class: SnoozeHistoryEntity
Primary Key: Composite (group_id, date_string, snooze_number)
Columns
Column Type Nullable Default Description group_id INTEGER No - ID of the limit group date_string TEXT No - Local date (YYYY-MM-DD) snooze_number INTEGER No - 1-based attempt number for the day granted_duration_ms INTEGER No - Time added (e.g., 15 mins) cooldown_ms INTEGER No - Wait time enforced timestamp INTEGER No - When snooze occurred phase TEXT No - ”COMPASSION”, “REFLECTION”, “DELIBERATE”
Relationships
Foreign Key References Relationship On Delete group_id limit_groups.id Many-to-One CASCADE
app_categories
Purpose: Stores the category (e.g., “Social”, “Productivity”) for each app.
Entity Class: AppCategory
Primary Key: packageName
Columns
Column Type Nullable Default Description packageName TEXT No - App package name category TEXT Yes - Category name status TEXT No - Sync status (see Enums) lastCheckedUtc INTEGER No - When last synced with API pendingCheckCount INTEGER No 0 Retries for pending status isUserCategorized INTEGER No 0 Boolean (0/1): Manual override? retryCount INTEGER No 0 Retries for failed network calls
daily_app_nudge_stats
Purpose: Tracks how many times a user was “nudged” (interrupted) for a specific app.
Entity Class: DailyAppNudgeStats
Primary Key: Composite (package_name, date)
Columns
Column Type Nullable Default Description package_name TEXT No - App package name date TEXT No - Local date (YYYY-MM-DD) is_enabled INTEGER No 1 Boolean (0/1): Are nudges active? nudges_shown_today INTEGER No 0 Count of interventions shown
user_journey
Purpose: A materialized view of the user’s day, combining sessions and events into a timeline.
Entity Class: UserJourney
Primary Key: id (Auto-increment)
Columns
Column Type Nullable Default Description id INTEGER No Auto-inc Unique identifier date_string TEXT No - Local date (YYYY-MM-DD) session_id INTEGER No - ID grouping events into one session timestamp INTEGER No - When event occurred event_type INTEGER No - Start, Usage, or End (see Enums) package_name TEXT Yes - App used (if applicable) duration_ms INTEGER Yes - Duration of usage scroll_distance_px INTEGER Yes - Distance scrolled trigger_ref TEXT Yes - Notification key that started session metadata TEXT Yes - Extra info
Indices
Index Name Columns Purpose index_journey_date_timestamp date_string, timestamp Timeline sorting index_journey_session session_id Grouping by session
Enums and Constants
RawAppEvent Types
Used in: raw_app_events.eventType
Value Constant Name Meaning 1 ACTIVITY_RESUMED App came to foreground 2 ACTIVITY_PAUSED App left foreground 23 ACTIVITY_STOPPED App stopped 1001 SCROLL_MEASURED Precise pixel scroll detected 1002 SCROLL_INFERRED Estimated scroll based on list index 2001 NOTIFICATION_POSTED Notification received 2002 NOTIFICATION_REMOVED Notification dismissed/clicked
LimitGroupType
Used in: limit_groups.group_type
Value Meaning CUSTOM_GROUP User-created group (e.g., “Social Media”) QUICK_LIMIT Auto-created group for a single app limit
CategoryStatus
Used in: app_categories.status
Value Meaning NEEDS_CHECK Initial state, needs API sync PENDING Server processing, check back later SYNCED Successfully categorized FAILED API failure or not found
UserJourney Types
Used in: user_journey.event_type
Value Constant Name Meaning 1 TYPE_SESSION_START Phone unlocked 2 TYPE_APP_USAGE App used within session 3 TYPE_SESSION_END Phone locked
TypeConverters
Converter Input Type Output Type Used For fromGroupType LimitGroupType String Storing Enum as text in DB toGroupType String LimitGroupType Reading text as Enum from DB
Migrations
The database has evolved significantly. Key migrations include:
Version Changes 28->29 Created limit_outcomes table 29->30 Added snooze_count to limit_outcomes 31->32 Added pending_check_count to app_categories 32->33 Added is_user_categorized to app_categories 33->34 Optimized notifications indices 34->35 Created notification_ingest (later removed) 35->36 Added batching columns to notifications 36->37 Added pause columns to limit_groups, created snooze_history 37->38 Major: Dropped notification_ingest, recreated notifications table38->39 Added accuracy_type to raw_app_events 39->40 Added accuracy_type, unit_type to scroll_sessions 40->41 Recreated daily_app_nudge_stats to remove unused columns 43->44 Created user_journey table for timeline visualization
Database Configuration
Setting Value Purpose Name scroll_track_databaseDatabase file name Version 44 Current schema version Export Schema false Schema JSON not exported Fallback Destructive Downgrades destroy data (dev only)