FocusBlocker is an Android productivity application that enforces app-usage limits using a foreground monitoring service, policy-based blocking rules, and a dedicated block screen experience.
It is built with modern Android tooling and a security-first baseline suitable for personal hardening and extension into a production deployment pipeline.
- Overview
- Core Capabilities
- Tech Stack
- Architecture
- Permission and Runtime Model
- Security Model
- Getting Started
- Build and Release
- Testing
- Project Conventions
- Contributing
- License
FocusBlocker tracks foreground app usage and applies configurable daily quotas per package. Once quota is exhausted, the app presents a full-screen block activity and keeps monitoring in the background through a foreground service.
The app is designed around:
- Deterministic quota tracking by logical day.
- Continuous monitoring with restart resilience (boot/package-updated receiver).
- Secure preference storage for sensitive settings.
- A Compose-first UI with permission gateway and focused workflows.
- Foreground app monitoring via
UsageStatsManagerinAppMonitorService. - App-level policy persistence with Room (
AppPolicy,Task, DAOs, andAppDatabase). - Quota accumulation and automatic daily reset based on a configurable day-start hour.
- Full-screen blocking UX using
BlockScreenActivitywhen a policy limit is reached. - Boot and package-replaced recovery through
BootReceiver. - Permission gateway flow for usage access, overlay permission, and battery optimization exemption.
- Encrypted sensitive preferences through
EncryptedSharedPreferences.
- Language: Kotlin
- UI: Jetpack Compose + Material 3
- Architecture primitives: ViewModel, Navigation Compose, Kotlin Coroutines/Flow
- Dependency Injection: Hilt
- Local persistence: Room + KSP
- Security: Android Keystore + AndroidX Security Crypto
- Build system: Gradle (Kotlin DSL)
Current project baselines:
- Android Gradle Plugin:
9.0.1 - Kotlin:
2.3.10 - Compile SDK:
36 - Target SDK:
35 - Min SDK:
26
The project currently uses a single app module (:app) with clean package boundaries.
app/src/main/java/com/focusblocker/app/
data/
local/
dao/
entity/
AppDatabase.kt
di/
security/
service/
ui/
screens/
block/
theme/
High-level flow:
- User grants required special permissions in the permission gateway.
AppMonitorServicestarts as foreground service.- Service polls recent usage events and resolves current foreground package.
- Policy and quota state are read/updated in Room.
- If quota is exhausted,
BlockScreenActivityis launched.
FocusBlocker requires the following permissions/special access for enforcement:
PACKAGE_USAGE_STATSfor foreground usage events.SYSTEM_ALERT_WINDOWfor overlay/blocking UX compatibility.REQUEST_IGNORE_BATTERY_OPTIMIZATIONSto reduce service kill risk.RECEIVE_BOOT_COMPLETEDfor automatic restart after reboot.- Foreground service permissions for persistent monitoring.
At runtime, the app gates entry through a dedicated permission screen and starts the monitor service only after required access is granted.
Sensitive state is stored in encrypted preferences:
- Backend:
EncryptedSharedPreferences - Key management: Android Keystore (
MasterKeyAES-256-GCM) - Preference file:
focusblocker_secure_prefs
Additional anti-tamper logic exists in SecurityManager:
- Logical-day computation using configurable day-start hour.
- Forward clock-drift detection using
SystemClock.elapsedRealtime()baseline.
- Android Studio (latest stable recommended)
- JDK 17+
- Android SDK with platform level 35+ installed
git clone <your-fork-or-repo-url>
cd FocusBlockerOpen the project in Android Studio and allow Gradle sync to complete.
./gradlew :app:assembleDebug./gradlew :app:installDebugRelease signing values are read from Gradle properties or environment variables:
RELEASE_STORE_FILERELEASE_STORE_PASSWORDRELEASE_KEY_ALIASRELEASE_KEY_PASSWORD
You can start from signing.properties.template and place values in user-level Gradle properties or exported environment variables.
./gradlew :app:assembleReleaseIf release signing values are missing, the build fails fast with explicit missing keys.
./gradlew :app:testDebugUnitTest./gradlew :app:connectedDebugAndroidTest./gradlew :app:lint :app:testDebugUnitTest- Base package must remain
com.focusblocker.app. - Keep Room schemas exported under
app/schemas/. - Use Hilt for dependency wiring in app scope and Android components.
- Prefer immutable UI state and coroutine-based asynchronous flows.
- Keep release-signing secrets out of version control.
Contributions are welcome. Please review CONTRIBUTING.md before opening a pull request.
Recommended pull request quality bar:
- Focused, single-purpose changes.
- Updated tests for behavioral changes.
- No debug logging or temporary scaffolding left in final diff.
- Clear migration notes for permission, schema, or signing behavior changes.
This project is licensed under the Apache License 2.0. See LICENSE.
This repository originated from Android architecture template foundations and has been adapted into the FocusBlocker application domain.