SelfLock is a personal Android app (Pixel 8, Android 16/API 36) for controlling app and website usage. Built with Kotlin, Jetpack Compose, Material 3, Hilt DI, and Room.
./gradlew assembleDebug # Build debug APK
./gradlew clean assembleDebug # Clean buildAPK output: app/build/outputs/apk/debug/app-debug.apk
- MVVM + Clean Architecture with Hilt dependency injection
- Data layer: Room database (
AppDatabase, version 2) with 4 entities:WebsiteRule,AppRule,UsageLog,BlockEvent+AggregatedUsagequery POJO - Domain layer: Use cases for block status checking, rule locking, installed apps
- UI layer: Jetpack Compose with 3 tabs (Websites, Apps, Statistics) + Settings as ModalBottomSheet + onboarding + lock screen + block overlay
- App blocking: Accessibility service detects foreground app → checks rules → launches
BlockOverlayActivityas full-screen overlay.MonitoringServicepollsUsageStatsManagerevery 5s as backup enforcement. - Website blocking: Accessibility service reads browser URL bars (Chrome, Firefox, Samsung Internet, Edge) → checks domain against
BlocklistManager→ launches overlay if blocked. No VPN (user runs Tailscale). - Usage tracking:
MonitoringServiceaccumulates daily usage in Room DB. Website usage tracked when browser is foreground + URL matches a rule. - Self-control: Active rules are read-only (locked).
IsRuleLockedUseCasechecks schedule windows and daily limit exhaustion. - Schedule alarms:
ScheduleAlarmHelperschedulesAlarmManageralarms at block start/end times viaScheduleReceiver. - App Lock: Optional master password gates app access.
MasterPasswordManagerstores hashed password inEncryptedSharedPreferences.LockScreenActivityshown on launch/resume when enabled.AppLockState.isUnlockedin-memory flag resets ononStop().FLAG_SECUREapplied when enabled. - Password-guarded rules: Individual rules can have their own password (
isPasswordProtected,passwordHashcolumns). Modifications require password entry viaPasswordEntryDialog. Master password serves as universal override. Password hashing uses PBKDF2WithHmacSHA256 (600k iterations, 32-byte salt). - App icons: Loaded via
PackageManager.getApplicationIcon()and cached inAppIconCache(LruCache). Displayed inAppRuleCard,AddAppRuleSheet, andBlockOverlayContent. - Website favicons: Loaded via Google favicon service (
google.com/s2/favicons) using CoilAsyncImage. Displayed inWebsiteRuleCardandBlockOverlayContent. Falls back to globe icon on error.
- No comments unless explicitly asked
- Surgical changes — touch only what the task requires
- Simplicity first — no speculative features, no unnecessary abstractions
- Match existing style — follow patterns in neighboring files
- Kotlin idioms — use
lateinit varfor Hilt injection,suspendfor DB calls,Flowfor reactive data
com.selflock.app/
├── di/ # Hilt modules (AppModule, DatabaseModule)
├── security/ # MasterPasswordManager (EncryptedSharedPreferences + PBKDF2), AppLockState
├── data/local/ # Room entities, DAOs, database (v2), type converters, AggregatedUsage POJO
├── data/repository/ # Repositories wrapping DAOs
├── domain/model/ # Enums and data classes (BlockType, TargetType, RuleStatus)
├── domain/usecase/ # Business logic (CheckBlockStatus, IsRuleLocked, GetInstalledApps)
├── service/ # Android services (Monitoring, Accessibility, Boot/Schedule receivers)
├── vpn/ # DNS packet processing (legacy, not active — user uses Tailscale)
├── ui/
│ ├── navigation/ # NavGraph (3 tabs + settings ModalBottomSheet)
│ ├── theme/ # Material 3 theme
│ ├── components/ # Reusable composables (BlockOverlayContent, AppIcon, FaviconImage,
│ │ # PasswordEntryDialog, PasswordProtectionSection, UsageBar,
│ │ # SummaryCard, DateRangeSelector)
│ ├── screens/
│ │ ├── onboarding/ # OnboardingScreen (7-step permission wizard)
│ │ ├── website/ # WebsiteBlockScreen, WebsiteRuleCard, AddWebsiteRuleSheet
│ │ ├── app/ # AppBlockScreen, AppRuleCard, AddAppRuleSheet
│ │ ├── statistics/ # StatisticsScreen, StatisticsViewModel
│ │ ├── settings/ # SettingsSheet, SettingsViewModel, SetPasswordSheet, DisablePasswordDialog
│ │ └── lock/ # LockScreenActivity, LockScreen, LockScreenViewModel
│ └── util/ # Formatters, AppIconCache
└── util/ # Helpers (PermissionHelper, BrowserUrlParser, ScheduleHelper, etc.)
- No VPN — website blocking is accessibility-based only (user runs Tailscale)
- Crash handler —
CrashHandlerActivityruns in:crashprocess, shows full stack traces - minSdk 34, targetSdk 36 — can use modern Android APIs freely
- Personal use app — no privacy concerns, show everything on crash
- DB version 2 — migration adds
isPasswordProtectedandpasswordHashcolumns to both rule tables - Settings is a ModalBottomSheet — accessible via gear icon in TopAppBar on all tabs, not a separate tab
- Unused dependencies —
security-cryptois now used byMasterPasswordManager;coil-composeis used byFaviconImage