Skip to content
WhatTFFPublic

Unplug

Unplug

Take back your time. One mindful decision at a time.

Download APK License: GPL-3.0

Version 2.2.8 · Android 7.0+ · Free · Open source · No accounts


What it does

You have 40 minutes of Instagram a day. Unplug holds you to it.

It does that in a way that doesn't make you uninstall it. When you're out, you get a full-screen lock with two ways through: wait, or write. Both are deliberately a bit annoying. That's the feature.

The difference from apps like this that you hate: it doesn't delete anything, and it doesn't nuke an app for being open. It blocks the tab that actually eats your life — Shorts, Reels, Spotlight, Stories — and leaves the rest of the app alone.

Everything stays on your phone. No account, no sign-up, no cloud, no analytics. The only thing it ever sends anywhere is a check for whether a newer version exists.


See it

Screenshots coming soon

Screenshots and short GIFs of the dashboard, lock screen, wait timer, open gate and settings are being captured on a real device. The gallery page lists every screen and names the file each one will use — it fills in as they land.


Install it

There's no Play Store listing. Get the APK from the releases page and install it.

2.2.8 is the current release, and the banner will offer it. 2.2.7 went out as a pre-release only, so releases/latest never pointed at it and the banner never showed it. Coming from 2.2.6, you move straight to 2.2.8 — there's nothing to install by hand.

  • Android 7.0 or newer
  • ~51 MB
  • You'll need to allow your browser to "install unknown apps"

Your phone will warn you. That's expected, and it's about the app, not you. Google Play Protect flags any app it hasn't seen before — Unplug is new and has almost no installs, so it has no reputation with Google. The warning means "I don't know this app", not "I found something bad".

If that makes you uneasy, don't install it. That's a reasonable call. But know that the entire app is on this page — the Flutter UI in lib/, the enforcement code in android/, all of it. If you'd rather read it than trust it, that's the better order.

When you open it, it asks for three things. Here's what each one actually does:

Permission What Unplug does with it
Usage Access Reads how long you've used each app today, so it can count against your limit
Accessibility Service Sees which app is in front, and closes the app or drops the lock screen when you're out
Display over other apps Draws that lock screen on top of whatever you were using

Android shows a genuinely alarming warning when you enable an accessibility service — "can read all screen content and perform actions". It shows that exact text for every accessibility app ever made, including screen readers and password managers. Unplug uses it to see which app is open and to press Back. It does not record your screen, read your keystrokes, or send content anywhere. There's no server to send it to.

You can revoke all three, any time, in Android Settings.

One more, later: on your first in-app update the phone will offer Unplug permission to "install unknown apps". Unplug asks for that only when you tap an update, never at setup, and uses it for exactly one thing — installing the next version of itself, which you confirm in Android's own installer. It cannot install anything else, and it won't ask again once you've decided.


Setting it up

Make a group, put some apps in it, pick a limit. 0 means blocked immediately — useful for the app you keep saying you'll stop opening.

Then enable the accessibility service. Unplug does nothing without it. This is the part most people skip, and then wonder why nothing happens.

On OxygenOS, ColorOS, or MIUI

These manufacturers bury the accessibility list somewhere different, and their battery savers are aggressive about killing it.

  • Settings → Accessibility → Downloaded apps → Unplug
  • Also add it to your auto-start / battery optimisation allowlist
  • If enforcement stops working after a reboot or a nap, this is why

Two more ways to catch yourself

Both are off by default and both are set per group, so you can gate a messaging app and leave a two-tap calculator alone. Settings → tap a group → the two rows at the bottom of the sheet.

Open gate. Every time you open one of that group's apps, a full-screen countdown runs with no way through. After five seconds two buttons appear: Open Instagram, or Go back. The app isn't closed or paused behind it — the gate is just a question, asked on top of an app that already launched. Leaving the screen dismisses it. Go back takes you to your home screen, not to whatever app you tapped the icon in — if you came from TikTok, it doesn't hand you TikTok.

Break reminder. After you've been in one of that group's apps continuously for a set number of minutes (10 by default, 1–60 if you prefer), it asks whether this was meant to be a long sit or a quick check. It doesn't dismiss itself and it doesn't close anything — after five seconds a Close button appears, and it's waiting for you.

Both work the same way the lock screen does: a real system-level overlay, drawn over everything including the launcher. And both lose to the time limit — if you're out, you get the lock screen, not a gentler version of it.

Switching one on is free — same as the tab blockers, where turning Snapchat Spotlight on never asks anything. You're arming a guard on yourself, and that shouldn't cost you a wait. Switching one off asks the challenge, because that's the direction that takes the guard away.

The break reminder's interval follows the same rule, because it is a budget too: it's how long you can use the app before it interrupts. Shortening it — 10m down to 5m — interrupts you sooner, so it's free. Lengthening it buys more uninterrupted time, so that's the direction that asks. Choosing the interval in the same pass that switches the reminder on is free either way, since that's part of arming it.

Creating a group is free as well. A new group starts at the default limit with both of these off, so it can't loosen anything.

The same rule governs the numbers — the daily limit, the reward per wait, the wait itself. Only the direction that makes things easier asks; see Making settings stick.


When you run out

Wait. A timer. You choose the length on a min / sec wheel — anything from 30 seconds to 3 minutes 59 seconds — because how much friction you're willing to accept is your call, not the app's. Spin below 30 seconds and it springs back. The timer restarts if you leave the app or shrink the window, so it can't tick away in the background while you're doing something else.

Or write. A 50-word challenge, matched exactly. No copy-paste, no "close enough".

Whatever you pick, you get the same thing: a set amount of extra screen time, starting from wherever you already are. If you're 50 minutes into a 40-minute limit and you complete a wait worth 5 minutes, you get 55 — not 45, which is still under what you've used and would feel like nothing happened. You don't have to earn your way back to the limit first. Then the bonus is spent, and it's gone.

Both lengths are adjustable, independently. Same payout either way — a longer wait just costs you more.

About the wait screen

It's a plain timer, not an ad. There's no ad SDK in this app and no ad network it talks to. The dialog shows a link to our own shop, yourwatch.no, where we sell custom watches. It makes no network requests. If you tap the link, your browser opens and that's the entire interaction.

If that bothers you: it's there because the wait has to be worth something to us, and this was the honest version of that. The alternative was an ad SDK, and we decided that was worse.


Blocking the tabs, not the apps

This is the part that makes Unplug survivable.

App Blocked Still works
YouTube Shorts Everything else
Instagram Reels Feed, DMs, stories
Snapchat Spotlight, Stories Camera, chat, map
TikTok For You Profile, inbox
Facebook Reels & Watch Feed, groups, messenger
Reddit Popular / Watch / Shorts Your subreddits

Unplug exits the tab and leaves you where you were. You came to check a DM; you can still check the DM.

These ship switched off — turn on the ones you actually want blocked, in Settings. You can add your own keywords per app too.


Making settings stick

Limits, groups and blockers all sit behind a lock. Loosening one costs the same wait or writing challenge as everything else — including turning the lock off.

It's aimed at the 11pm version of you who is genuinely convinced that "just 5 more minutes" is a reasonable request. It's not security: those values live in ordinary storage on your phone, and someone deliberately trying to get past it on a rooted device can. It stops an impulse, not an attacker. Nothing stops you uninstalling.

Tightening is always free. The challenge is only ever on the direction that makes the app more permissive, and never on the one that makes it stricter — so you can always make yourself harder to fool without paying for it. Charging a price for tightening would mean you couldn't arm your own guard without permission, which is the same as not letting you arm it at all.

Concretely:

Change Asks?
Daily limit 40m → 30m No — stricter
Daily limit 30m → 40m Yes — more time for the same effort
Reward per wait 5m → 3m No — stricter
Reward per wait 3m → 5m Yes — more free time for the same wait
Wait duration 30s → 60s No — you made the deal harder on yourself
Wait duration 60s → 30s Yes — same reward, less effort
Break reminder 10m → 5m No — it interrupts you sooner
Break reminder 5m → 10m Yes — more time before it asks

The wait is the one that reads backwards, and deliberately: it's the price of a reward rather than a budget, so its permissive end is the lower one. Making it longer costs you more for the same thing, which is you tightening it.

Re-saving a number you didn't change isn't a change either — it never asks.


Your data

It's on your phone. That's the whole story, but concretely:

  • Groups, limits, usage history and bonus time live in local storage on the device
  • Enforcement runs entirely on-device and reads those same local values
  • The only things it ever fetches are the GitHub Releases API and the raw VERSION file, to check whether a newer version exists — and, once, the release APK itself when you tap the update banner. Nothing about your usage, your groups, or you is ever in any of those requests.
  • No accounts, no analytics, no crash reporting, no tracking

If you don't believe that, it's searchable: grep the repo for HttpClient, http, and WebView and follow every call. There are two files that do network I/O — one checks for a new version, one downloads it — and neither sends anything outward.


Updating

Unplug checks for new versions on its own. When there's one, a blue banner appears on the dashboard.

Tap it and Unplug downloads the APK itself and hands it to Android's installer. You stay in the app, confirm the install, and that's it — no browser, no release page, no hunting through a file manager.

The first time you do this, Android will ask permission for Unplug to install apps. That's the one permission this release adds. Grant it once, from the screen it sends you to, and every later update is a single tap.

If the download fails — bad connection, captive wifi, the release pulled — the banner says so and offers to open the release page in your browser instead. The old way still works; it's just no longer the way.

The banner only shows when there's genuinely something newer, and you can dismiss it. It comes back for the next release.

If updates aren't appearing
  • On v2.2.4 or older: the time limit could not stop you while you were inside the app. Android doesn't count an app's screen time until you leave it, and the check that was supposed to cover that gap never fired. You were only ever blocked when you closed the app and came back — which meant the limit did not actually hold. 2.2.5 fixes it. If you've been relying on a daily limit, upgrade by hand.
  • On v2.1.0: don't stay on it. It has a bug that stops the app blocker working until you force-close Unplug. Upgrade to 2.1.1 or later by hand. Details below.
  • On v2.0.2 or older: those builds send you to the browser instead of downloading in-app. Everything still works, it's just the longer route.
  • On v1.1.7 or older: those builds are missing a permission they need to reach the network at all. They can't detect any update, including the one that fixes it. Download the latest release by hand.
  • On v1.1.9: also by hand. That release is a pre-release, and pre-releases aren't offered to anyone.
  • Otherwise: the check is throttled to once every 30 minutes, and a fresh app start always checks straight away. Force-close and reopen, then give it a few seconds.
  • Still nothing? adb logcat -s Unplug: UpdateChecker prints every HTTP response the checker gets, including rate-limit errors.

Building it

git clone https://github.com/WhatTFF/Unplug.git
cd Unplug
flutter pub get
flutter build apk --release

Flutter 3.47.7, Gradle 8.14+, AGP 8.11.1+, Kotlin 2.2.20, NDK 28.x. Android minSdk 24.

The version lives in one place, the version: line in pubspec.yaml; lib/generated/app_version.dart is generated from it by ./tool/gen_version.sh. Everything else follows.

Layout

lib/
├── main.dart                       Entry point, MethodChannel
├── home_screen.dart                Dashboard: ring chart, stats, update banner
├── settings_screen.dart            Locked settings
├── lock_screen_popup.dart          The lock screen and its two ways out
├── generated/app_version.dart      Generated — don't edit
├── services/
│   ├── storage_service.dart        Local storage, bonus and limit maths
│   ├── update_checker.dart         GitHub check, throttle, cache, fallback
│   ├── usage_tracker.dart          Usage stats for the dashboard numbers
│   ├── reward_gate.dart            The wait timer
│   ├── settings_gate.dart          Which direction of a change needs a challenge
│   └── message_verification.dart   The writing challenge
└── android/app/src/main/kotlin/com/egbytest/unplug/
    ├── MainActivity.kt             Flutter bridge
    └── UsageAccessibilityService.kt   Foreground watch and enforcement

Enforcement lives entirely in the Kotlin service. It reads the same preferences the Dart side writes, and never asks Dart for anything — which is why it keeps working while the app is closed.

Verifying a release

  1. The tag and the published VERSION file agree.
  2. aapt2 dump badging on the built APK, to confirm the permissions are actually in the binary. Not the source — the binary.
  3. Network audit: every outbound request should be in lib/services/update_checker.dart. There is no ad SDK and no analytics SDK to find.

License

Unplug is free software under the GNU General Public License, version 3.0 only. See LICENSE for the full text.

You can read it, build it, change it, and share it. If you distribute a changed version, it has to stay under the same licence, with the source available. That is deliberate: the whole point of this app is that you can check what it does to your phone, and a closed fork of it would defeat that. It also means nobody can take this code, add ads and an account system, and ship it as the same app.

The name and icon are not part of that grant — a modified build is not Unplug and should not be presented as it.

Contributing

Bug reports, device reports, and small pull requests are welcome. Read CONTRIBUTING.md first — especially if you plan to touch the Kotlin enforcement code, which has no test harness and needs a real device. Participation is covered by CODE_OF_CONDUCT.md.

For a security problem, use the private advisory form in SECURITY.md, not a public issue.