Skip to main content

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.

Entity Relationship Diagram

Tables

scroll_sessions

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

ColumnTypeNullableDefaultDescription
idINTEGERNoAuto-incUnique identifier
packageNameTEXTNo-Package name of the app where scrolling occurred
scrollAmountINTEGERNo-Total distance scrolled (units depend on unit_type)
scrollAmountXINTEGERNo-Horizontal distance scrolled
scrollAmountYINTEGERNo-Vertical distance scrolled
dataTypeTEXTNo-Source of data (e.g., “MEASURED”, “INFERRED”)
sessionStartTimeINTEGERNo-Unix timestamp (ms) when scrolling started
sessionEndTimeINTEGERNo-Unix timestamp (ms) when scrolling stopped
dateStringTEXTNo-Local date (YYYY-MM-DD) for aggregation
sessionEndReasonTEXTNo-Why the session ended (e.g., “TIMEOUT”, “APP_CHANGE”)
accuracy_typeTEXTNo’UNKNOWN’Added v40: ‘PRECISE’ or ‘ESTIMATED’
unit_typeTEXTNo’PIXELS’Added v40: ‘PIXELS’ or ‘MICROMETERS’

Indices

Index NameColumnsUniquePurpose
ImplicitdateStringNoFast 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

ColumnTypeNullableDefaultDescription
idINTEGERNoAuto-incUnique identifier
packageNameTEXTNo-Package name associated with the event
classNameTEXTYes-Specific activity class name (if applicable)
eventTypeINTEGERNo-Event code (see Enums below)
eventTimestampINTEGERNo-Unix timestamp (ms)
eventDateStringTEXTNo-Local date (YYYY-MM-DD)
sourceTEXTNo-Source: “USAGE_STATS”, “ACCESSIBILITY”, “NOTIFICATION”
valueINTEGERYes-Generic value (e.g., scroll distance, impact score)
scrollDeltaXINTEGERYes-Raw X delta for scroll events
scrollDeltaYINTEGERYes-Raw Y delta for scroll events
accuracy_typeTEXTYesNULLAdded v39: ‘PRECISE’ or ‘ESTIMATED’

app_metadata

Purpose: Caches details about installed applications to avoid repeated PackageManager calls. Entity Class: AppMetadata Primary Key: packageName

Columns

ColumnTypeNullableDefaultDescription
packageNameTEXTNo-Unique package ID (e.g., com.android.chrome)
appNameTEXTNo-Human readable label
versionNameTEXTYes-App version string
versionCodeINTEGERNo-App version number
isSystemAppINTEGERNo-Boolean (0/1): Is it a pre-installed system app?
isInstalledINTEGERNo-Boolean (0/1): Is it currently installed?
isIconCachedINTEGERNo-Boolean (0/1): Is the icon saved to disk?
appCategoryINTEGERNo-Legacy category ID (superseded by app_categories table)
isUserVisibleINTEGERNo-Boolean (0/1): Does it have a launcher activity?
userHidesOverrideINTEGERYesNULLBoolean (0/1): User manually hid/showed this app
installTimestampINTEGERNo-When the app was installed
lastUpdateTimestampINTEGERNo-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

ColumnTypeNullableDefaultDescription
idINTEGERNoAuto-incUnique identifier
unlockTimestampINTEGERNo-When the screen was unlocked
lockTimestampINTEGERYes-When the screen was locked again
durationMillisINTEGERYes-Total duration of the session
dateStringTEXTNo-Local date (YYYY-MM-DD)
firstAppPackageNameTEXTYes-The first app used after unlocking
triggeringNotificationKeyTEXTYes-Key of notification that caused the unlock
triggeringNotificationPackageNameTEXTYes-Package of the triggering notification
unlockEventTypeTEXTNo-Type of unlock event
sessionTypeTEXTYes-”Glance”, “Intentional”, etc.
sessionEndReasonTEXTYes-Why session ended (e.g., “SCREEN_OFF”)
isCompulsiveINTEGERNo-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

ColumnTypeNullableDefaultDescription
dateStringTEXTNo-Local date (YYYY-MM-DD)
totalUsageTimeMillisINTEGERNo-Total screen-on time
totalUnlockedDurationMillisINTEGERNo-Total time device was unlocked
totalUnlockCountINTEGERNo-Number of unlocks
totalNotificationCountINTEGERNo-Number of notifications received
lastUpdatedTimestampINTEGERNo-When this record was last calculated
totalAppOpensINTEGERNo-Total app launch events
firstUnlockTimestampUtcINTEGERYes-Time of first unlock
lastUnlockTimestampUtcINTEGERYes-Time of last unlock
intentionalUnlockCountINTEGERNo-Count of long sessions
glanceUnlockCountINTEGERNo-Count of short sessions

daily_app_usage

Purpose: Aggregated usage statistics per app, per day. Entity Class: DailyAppUsageRecord Primary Key: id (Auto-increment)

Columns

ColumnTypeNullableDefaultDescription
idINTEGERNoAuto-incUnique identifier
packageNameTEXTNo-App identifier
dateStringTEXTNo-Local date (YYYY-MM-DD)
usageTimeMillisINTEGERNo-Total foreground time
activeTimeMillisINTEGERNo-Total time with user interaction
appOpenCountINTEGERNo-Number of times launched
notificationCountINTEGERNo-Number of notifications from this app
lastUpdatedTimestampINTEGERNo-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

ColumnTypeNullableDefaultDescription
idINTEGERNoAuto-incUnique identifier
dateStringTEXTNo-Local date (YYYY-MM-DD)
insightKeyTEXTNo-Key (e.g., “busiest_unlock_hour”)
stringValueTEXTYes-Text value (e.g., package name)
longValueINTEGERYes-Numeric value (e.g., timestamp, count)
doubleValueREALYes-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

ColumnTypeNullableDefaultDescription
idINTEGERNoAuto-incUnique identifier
notification_keyTEXTNo-System notification key
package_nameTEXTNo-App that posted the notification
post_time_utcINTEGERNo-When it was posted
titleTEXTYes-Notification title (redacted if setting enabled)
textTEXTYes-Notification body (redacted if setting enabled)
impactINTEGERNo-0=Passive, 1=Visual, 2=Interruption
typeINTEGERNo-0=Standard, 1=Conversation, 2=Service
stateINTEGERNo-0=Posted, 1=Dismissed, 2=Clicked, 4=Seen
group_idTEXTYes-System group key for bundling
date_stringTEXTNo-Local date (YYYY-MM-DD)
is_batchedINTEGERNo0Boolean (0/1): Was it intercepted by Postbox?
delivered_at_utcINTEGERYes-When it was delivered (if batched)

Indices

Index NameColumnsPurpose
index_notifications_notification_keynotification_keyLookup by system key
index_notifications_date_stringdate_stringFiltering by date
index_notifications_is_batched_deliveredis_batched, delivered_at_utcFiltering 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

ColumnTypeNullableDefaultDescription
idINTEGERNoAuto-incUnique identifier
nameTEXTNo-User-defined name (e.g., “Social”)
time_limit_minutesINTEGERNo-Daily allowance in minutes
is_enabledINTEGERNo-Boolean (0/1)
group_typeTEXTNo-Enum: “CUSTOM_GROUP”, “QUICK_LIMIT”
creation_timestampINTEGERNo-When created
paused_until_timestampINTEGERYes-If paused, when it resumes
pause_reasonTEXTYes-User reason for pausing

limited_apps

Purpose: Maps individual apps to a Limit Group. Entity Class: LimitedApp Primary Key: package_name

Columns

ColumnTypeNullableDefaultDescription
package_nameTEXTNo-App package name
group_idINTEGERNo-Foreign Key to limit_groups.id

Relationships

Foreign KeyReferencesRelationshipOn Delete
group_idlimit_groups.idMany-to-OneCASCADE

limit_outcomes

Purpose: Tracks daily success/failure for limit groups. Entity Class: LimitOutcomeEntity Primary Key: Composite (date_string, group_id)

Columns

ColumnTypeNullableDefaultDescription
date_stringTEXTNo-Local date (YYYY-MM-DD)
group_idINTEGERNo-ID of the limit group
limit_minutesINTEGERNo-The limit setting on that day
total_used_msINTEGERNo-Actual usage
exceeded_by_msINTEGERNo-Amount over limit (0 if success)
successINTEGERNo-Boolean (0/1)
snooze_countINTEGERNo0How many times user snoozed
last_updated_tsINTEGERNo-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

ColumnTypeNullableDefaultDescription
group_idINTEGERNo-ID of the limit group
date_stringTEXTNo-Local date (YYYY-MM-DD)
snooze_numberINTEGERNo-1-based attempt number for the day
granted_duration_msINTEGERNo-Time added (e.g., 15 mins)
cooldown_msINTEGERNo-Wait time enforced
timestampINTEGERNo-When snooze occurred
phaseTEXTNo-”COMPASSION”, “REFLECTION”, “DELIBERATE”

Relationships

Foreign KeyReferencesRelationshipOn Delete
group_idlimit_groups.idMany-to-OneCASCADE

app_categories

Purpose: Stores the category (e.g., “Social”, “Productivity”) for each app. Entity Class: AppCategory Primary Key: packageName

Columns

ColumnTypeNullableDefaultDescription
packageNameTEXTNo-App package name
categoryTEXTYes-Category name
statusTEXTNo-Sync status (see Enums)
lastCheckedUtcINTEGERNo-When last synced with API
pendingCheckCountINTEGERNo0Retries for pending status
isUserCategorizedINTEGERNo0Boolean (0/1): Manual override?
retryCountINTEGERNo0Retries 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

ColumnTypeNullableDefaultDescription
package_nameTEXTNo-App package name
dateTEXTNo-Local date (YYYY-MM-DD)
is_enabledINTEGERNo1Boolean (0/1): Are nudges active?
nudges_shown_todayINTEGERNo0Count 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

ColumnTypeNullableDefaultDescription
idINTEGERNoAuto-incUnique identifier
date_stringTEXTNo-Local date (YYYY-MM-DD)
session_idINTEGERNo-ID grouping events into one session
timestampINTEGERNo-When event occurred
event_typeINTEGERNo-Start, Usage, or End (see Enums)
package_nameTEXTYes-App used (if applicable)
duration_msINTEGERYes-Duration of usage
scroll_distance_pxINTEGERYes-Distance scrolled
trigger_refTEXTYes-Notification key that started session
metadataTEXTYes-Extra info

Indices

Index NameColumnsPurpose
index_journey_date_timestampdate_string, timestampTimeline sorting
index_journey_sessionsession_idGrouping by session

Enums and Constants

RawAppEvent Types

Used in: raw_app_events.eventType
ValueConstant NameMeaning
1ACTIVITY_RESUMEDApp came to foreground
2ACTIVITY_PAUSEDApp left foreground
23ACTIVITY_STOPPEDApp stopped
1001SCROLL_MEASUREDPrecise pixel scroll detected
1002SCROLL_INFERREDEstimated scroll based on list index
2001NOTIFICATION_POSTEDNotification received
2002NOTIFICATION_REMOVEDNotification dismissed/clicked

LimitGroupType

Used in: limit_groups.group_type
ValueMeaning
CUSTOM_GROUPUser-created group (e.g., “Social Media”)
QUICK_LIMITAuto-created group for a single app limit

CategoryStatus

Used in: app_categories.status
ValueMeaning
NEEDS_CHECKInitial state, needs API sync
PENDINGServer processing, check back later
SYNCEDSuccessfully categorized
FAILEDAPI failure or not found

UserJourney Types

Used in: user_journey.event_type
ValueConstant NameMeaning
1TYPE_SESSION_STARTPhone unlocked
2TYPE_APP_USAGEApp used within session
3TYPE_SESSION_ENDPhone locked

TypeConverters

ConverterInput TypeOutput TypeUsed For
fromGroupTypeLimitGroupTypeStringStoring Enum as text in DB
toGroupTypeStringLimitGroupTypeReading text as Enum from DB

Migrations

The database has evolved significantly. Key migrations include:
VersionChanges
28->29Created limit_outcomes table
29->30Added snooze_count to limit_outcomes
31->32Added pending_check_count to app_categories
32->33Added is_user_categorized to app_categories
33->34Optimized notifications indices
34->35Created notification_ingest (later removed)
35->36Added batching columns to notifications
36->37Added pause columns to limit_groups, created snooze_history
37->38Major: Dropped notification_ingest, recreated notifications table
38->39Added accuracy_type to raw_app_events
39->40Added accuracy_type, unit_type to scroll_sessions
40->41Recreated daily_app_nudge_stats to remove unused columns
43->44Created user_journey table for timeline visualization

Database Configuration

SettingValuePurpose
Namescroll_track_databaseDatabase file name
Version44Current schema version
Export SchemafalseSchema JSON not exported
FallbackDestructiveDowngrades destroy data (dev only)
Last modified on January 25, 2026