A distraction blocker for Android that actually takes you offline.
Start a session and every app you haven't explicitly allowed is shut out — it can't be opened, it's cut off the network, and its process is dropped so your contacts see you go offline. No notifications, no "last seen", no temptation. Just focus.
Kotlin · Jetpack Compose · Room · AccessibilityService · VpnService
Most focus apps just hide or nag — the blocked app is still running, still connected, and everyone can still see you online. Monk Mode draws a hard line between two things:
- Blocked — you can't open the app.
- Disabled / offline — the app is cut off so other people see you disappear.
It does both, using only public Android APIs (no root, no MDM enrolment required).
- Allow-list blocking — pick the handful of apps you're allowed during a session; everything else (plus any system apps you choose) is blocked.
- Can't-open enforcement — an
AccessibilityServiceplus aUsageStatspoll bounce any blocked app back to the launcher the instant it's opened. - Goes offline — a local
VpnServiceblackhole cuts blocked apps off the network, and their background processes are killed at session start so live connections drop immediately. Contacts see you offline instead of "online / last seen just now". - Total silence — Do Not Disturb for the session, plus active clearing of blocked apps' notifications from the shade (DND alone only mutes them).
- Floating overlay — an always-on-top pill with a live session timer; tap to reveal pause / end controls, drag it around, fling it to an edge to minimise.
- Pause / resume — briefly step out (lifts DND + network block, freezes the timer) with an automatic re-lock after 2 minutes.
- Achievements — badges at 5 / 10 / 20 / 40 min, 1 hour, and each hour beyond; an "unlocked" badge pops up over the overlay the moment each milestone is crossed.
- Color themes — five dark palettes (Amber, Ice, Forest, Crimson, Violet), switchable live from the Theme screen; the overlay follows your choice.
- Survives reboots — an in-progress session resumes after a restart, continuing from the original start time.
Blocking is layered, so no single point of failure lets an app through:
| Layer | Mechanism | Effect |
|---|---|---|
| App launch | AccessibilityService + 500 ms UsageStats poll → GLOBAL_ACTION_HOME |
You can't open it |
| Network | VpnService blackhole (blocked packages routed into an unread tun fd) |
It can't reach its servers |
| Live connection | ActivityManager.killBackgroundProcesses() at session start / resume |
You drop offline now, not after a timeout |
| Notifications | NotificationListenerService cancels + sweeps blocked apps' posts |
Nothing reaches the shade |
Without root or Device Owner (which requires a device with zero accounts — not realistic on a daily driver), Android will not let one app force-stop another. So apps that run a persistent foreground service (e.g. WhatsApp) survive the process kill and go offline via the slower network-cut timeout rather than instantly. Everything else drops offline immediately. This is the ceiling of what's possible with public APIs, and the app is built to hit exactly that ceiling.
| Permission | Why |
|---|---|
Display over other apps (SYSTEM_ALERT_WINDOW) |
The floating timer overlay |
| Accessibility service | Detect and bounce blocked-app launches |
| Do Not Disturb access | Silence all interruptions for the session |
Usage access (PACKAGE_USAGE_STATS) |
Backup foreground-app detection + app list |
| Notification access | Clear blocked apps' notifications from the shade |
VPN consent (VpnService) |
The local network blackhole for blocked apps |
| Battery unrestricted | Keep the services alive under aggressive OEM battery management |
KILL_BACKGROUND_PROCESSES |
Drop blocked apps' live connections for instant offline |
Requires JDK 17 and the Android SDK (compileSdk 34, minSdk 29).
# Debug build
./gradlew assembleDebug
# Install debug on a connected device
./gradlew installDebugCreate a keystore and a keystore.properties at the project root (both are git-ignored):
storeFile=monkmode-release.jks
storePassword=********
keyAlias=monkmode
keyPassword=********keytool -genkeypair -v -keystore monkmode-release.jks -alias monkmode \
-keyalg RSA -keysize 2048 -validity 10000
./gradlew assembleRelease
# → app/build/outputs/apk/release/app-release.apk (minified, shrunk, signed)If keystore.properties is absent, the release build falls back to debug signing so the
project still builds on a fresh checkout.
app/src/main/java/com/monkmode/
├── MainActivity.kt # Compose nav host (+ back handling for sub-screens)
├── MonkModeApp.kt # Application + in-memory SessionState
├── ui/ # HomeScreen, pickers, Achievements, Themes, About, theme palettes
├── service/
│ ├── SessionController.kt # start / stop / pause / resume — the orchestrator
│ ├── MonkModeAccessibilityService.kt
│ ├── SessionTimerService.kt # foreground timer + backup blocking poll + milestone badges
│ ├── NetworkBlockerService.kt # VpnService blackhole
│ ├── NotificationBlockerService.kt
│ └── BootReceiver.kt
├── oem/ # OemStrategy interface (+ Realme/Default stubs)
└── data/ # Room DB, DAOs, entities
This repo is maintained with the project-knowledge skill from
github.com/YahyaZekry/claude-code-skills —
a living .project-knowledge/ folder (stack, schema, systems, roadmap, session log) that
keeps AI-assisted sessions up to speed on the project's state instead of one growing doc.
Primarily developed and tested on a Realme 7 Pro (Realme UI / ColorOS, Android 12).
An OemStrategy interface is stubbed in for future OEM-specific behaviour.
MIT © 2026 Yahya Zekry