A minimalist Android productivity app that locks you out of distracting apps for a defined period — so you can do deep work without fighting yourself.
Most productivity failures aren't about motivation — they're about friction. FocusGuard removes the option to mindlessly open Instagram, YouTube, or any other app while you're supposed to be working. Instead of relying on willpower, it forces a moment of intentional reflection before letting you break your focus session.
When you try to open a blocked app, the screen asks:
"Do you really want to break your goal? Is what you're about to do really worth it?"
That single pause is often enough to make you reconsider.
- Set a focus session from 1 to 240 minutes (up to 4 hours)
- Blocks all apps during the session except incoming phone calls
- Intercepts app launches in real time via Android's Accessibility Service
- If you try to break focus, you're asked to reflect before proceeding
- On reflection, you can choose to:
- Restart the clock — reset the timer to the full original duration
- Keep the current time — continue where you left off
- Unlock — temporarily disable blocking (you made a conscious choice)
- Persistent foreground notification shows remaining time at all times
- Multi-language support — English, Spanish, French, and Portuguese; follows the device locale automatically
- Works across all major Android OEMs (Samsung, Xiaomi, OnePlus, Motorola, etc.)
The app is written entirely in Kotlin and targets Android API 26+. It follows a simple, pragmatic architecture with no third-party frameworks — just Android SDK components wired together through shared state.
com.focusguard.app
├── FocusStateManager.kt — Shared state (SharedPreferences singleton)
├── AppBlockerAccessibilityService.kt — Detects app launches, triggers block screen
├── FocusTimerService.kt — Foreground countdown timer service
├── MainActivity.kt — Main UI: pick duration, start/stop session
└── InterruptionActivity.kt — Block screen: reflect, unlock, or restart
The single source of truth for the focus session. It wraps a SharedPreferences file and exposes simple read/write methods for the session state: whether focus is active, the end timestamp, the original duration, and whether the user has temporarily unlocked the phone. Because it's a singleton backed by persistent storage, all components — the service, the accessibility service, and both activities — share the same state across process boundaries.
Listens for TYPE_WINDOW_STATE_CHANGED accessibility events, which fire whenever a new app window comes to the foreground. On each event it checks the package name against an allowlist (phone/dialer apps and the FocusGuard app itself) and against a list of system UI prefixes (launchers, System UI). If the package is not on either list and a focus session is active and not unlocked, it immediately launches InterruptionActivity as a new task, covering the blocked app.
A 2-second debounce prevents the interruption screen from firing repeatedly for the same package in quick succession.
A Service running in the foreground with a sticky notification. It owns the CountDownTimer and is the authoritative clock for the session. It handles three intents:
ACTION_START— starts a new session with a given duration in minutesACTION_RESTART— cancels the current countdown and starts a fresh one with the original duration (used byInterruptionActivitywhen the user chooses "Restart the clock")ACTION_STOP— cancels the timer, clears state, and stops the service
Every second it broadcasts a BROADCAST_TIMER_TICK intent so MainActivity can update its live display without polling.
The entry point. Provides a NumberPicker (1–240 minutes), a start/stop button, and a live timer display fed by the broadcast from FocusTimerService. It also detects whether the Accessibility Service is enabled and shows a persistent warning banner if not, guiding the user to Settings → Accessibility to enable it.
A full-screen activity with two phases:
- Reflection phase — displays the motivational question with a prominent "Stay Focused" button (No) and a subdued "Let me in" button (Yes). The back button is disabled via
OnBackPressedCallbackso the user cannot bypass this screen. - Decision phase — shown if the user taps "No". Offers "Restart the clock" (sends
ACTION_RESTARTto the service) or "Keep current time" (simply closes the screen).
If the user taps "Yes", FocusStateManager.setUnlocked(true) is called and the screen closes, allowing AppBlockerAccessibilityService to stop intercepting until the session ends or the app is reopened.
User taps Start
│
▼
MainActivity ──► startForegroundService(ACTION_START) ──► FocusTimerService
│
FocusStateManager ◄─┤ writes session state
│
BROADCAST_TIMER_TICK ──► MainActivity (UI update)
User opens another app
│
▼
AppBlockerAccessibilityService (TYPE_WINDOW_STATE_CHANGED)
│ reads FocusStateManager.isActive() && !isUnlocked()
▼
InterruptionActivity
│
├── "No" → Decision screen
│ ├── "Restart" ──► startService(ACTION_RESTART) ──► FocusTimerService
│ └── "Stay" ──► finish() (timer continues unchanged)
│
└── "Yes" ──► FocusStateManager.setUnlocked(true) ──► finish()
All user-visible strings live in res/values/strings.xml. The app ships with translations for:
| Locale | Folder |
|---|---|
| English (default) | res/values/ |
| Spanish | res/values-es/ |
| French | res/values-fr/ |
| Portuguese | res/values-pt/ |
Android selects the right strings automatically based on the device language — no code changes required. To add another language, create a new res/values-<locale>/strings.xml file and translate all keys from the default strings.xml.
- Android Studio Hedgehog (2023.1.1) or later
- Android device running API 26 (Android 8.0) or higher
- A physical device is strongly recommended — app blocking does not work meaningfully on an emulator
- Open Android Studio and select File → Open, then choose the
FocusGuardfolder. - Wait for the Gradle sync to complete.
- Connect your Android device via USB with developer mode and USB debugging enabled.
- Press Run (▶) or use
Shift+F10.
The app cannot block other apps without the Accessibility Service being active. On first launch:
- Tap "Enable Accessibility Service" in the app.
- In the system settings screen that opens, find "Focus Guard — App Blocker" and tap it.
- Toggle it on and confirm the permission dialog.
- Return to the app — the warning banner will disappear.
Note: Some OEMs (Xiaomi, Huawei, OnePlus) may kill accessibility services when the app is in the background. If blocking stops working, check the battery optimization settings and add FocusGuard to the "unrestricted" or "no restrictions" list.
| Permission | Why it's needed |
|---|---|
FOREGROUND_SERVICE |
Required to run the timer service while the app is in the background |
FOREGROUND_SERVICE_SPECIAL_USE |
Required by Android 14+ for foreground services that don't fit a standard category |
POST_NOTIFICATIONS |
Displays the persistent countdown notification (Android 13+) |
BIND_ACCESSIBILITY_SERVICE |
Grants the Accessibility Service the right to observe window changes |
The app does not request internet access, read your contacts, access your camera, or collect any data. Everything stays on-device.
- Accessibility Service must be re-enabled after some device restarts on certain OEMs.
- The block is not root-level — a determined user can always go to Settings and disable the Accessibility Service. This app works by creating friction and prompting reflection, not by enforcing hard locks.
- Some launchers may not trigger
TYPE_WINDOW_STATE_CHANGEDconsistently. If the home screen bypasses the blocker, add your launcher's package toSYSTEM_ALLOWED_PREFIXESinAppBlockerAccessibilityService.