Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jhappto

Humorous productivity/health app that intercepts food delivery app launches on Android and shows a full-screen deterrent flow (video + SFX + cooldown).

What it does

  • Watch selected target apps (Foodpanda, UberEats, etc.) via AccessibilityService
  • Intercept launch in near real-time
  • Open WarningActivity with separate Flutter entrypoint (warningMain)
  • Play the impact SFX on the alarm stream with transient audio focus
  • Offer two actions: dismiss now or start 15-minute bypass cooldown
  • Track interception count and display lightweight analytics

Tech stack

  • Flutter (UI and configuration screens)
  • Kotlin Android services/activities (watchdog + intercept path)
  • SharedPreferences bridge (Flutter writes, Kotlin reads directly)

Project structure

  • lib/main.dart main app shell, onboarding/home routing, and the warningMain entrypoint (must stay here — the embedding resolves activity entrypoints against the root library only)
  • lib/screens/ onboarding, home, app picker, warning, analytics
  • lib/services/ permissions, targets, warning channel
  • android/app/src/main/kotlin/dev/kashifkhan/jhappto/ native Android classes
  • android/app/src/main/res/xml/accessibility_service_config.xml service config

Requirements

  • Flutter SDK
  • Android SDK + NDK 27.0.12077973
  • Real Android device required — Accessibility is unreliable on emulators

Run locally

flutter pub get
flutter run --debug

Permissions

Both are required. The app cannot function without either.

  • Accessibility — detects which app moved to the foreground. Only package names are read; screen content is never accessed (canRetrieveWindowContent="false").
  • Display over other apps — an AccessibilityService is not exempt from background-activity-launch restrictions. SYSTEM_ALERT_WINDOW is the exemption that lets WarningActivity start over another app. Without it the intercept is detected and then silently dropped.

Package visibility

QUERY_ALL_PACKAGES is not permitted for this use case under Play policy, so the app declares an explicit <queries> list instead — currently 57 packages covering global delivery platforms and regional/fast-food ordering apps across Pakistan, India, US, UK/EU, SEA, Middle East, LATAM, ANZ, Canada and East Asia.

Adding a new food app means updating both:

  • android/app/src/main/AndroidManifest.xml (<queries>)
  • MainActivity.KNOWN_FOOD_APPS

test/food_apps_sync_test.dart fails the build if the two drift. Verify any new package resolves before adding it — a wrong id fails silently:

curl -s -o /dev/null -w "%{http_code}\n" \
  "https://play.google.com/store/apps/details?id=<package>"

Interception itself is unaffected by this list — event.packageName is not visibility-filtered. The list only determines what the picker can show.

Setup on device

  1. Open app and complete onboarding
  2. Enable Accessibility for jhappto Food Guard
  3. Allow Display over other apps
  4. In Guarded Apps, toggle target food apps ON

Test intercept

  1. Leave jhappto
  2. Open guarded food app
  3. Warning screen should appear quickly

If nothing appears, check whether the launch was blocked:

adb logcat -s ActivityTaskManager JhapptoWatchdog

Media assets

Placeholders are committed so the app runs before final art exists. Replace in place — no code changes needed:

  • assets/media/slap_video.mp4 — portrait (9:16), under 5MB
  • assets/audio/slap_sfx.mp3 — under 500KB, starts on the impact

The warning screen degrades gracefully if either is missing or corrupt, and its action buttons always appear within ~3 seconds regardless.

Release build

Create android/key.properties (git-ignored):

storeFile=/absolute/path/to/upload-keystore.jks
storePassword=...
keyAlias=upload
keyPassword=...

Then:

flutter build appbundle --release

Without that file the build falls back to debug signing, which Play rejects. Release builds are minified — proguard-rules.pro keeps the accessibility service and both activities, since stripping them breaks the app silently.

Play Store notes

  • targetSdk 36 is required for new submissions from 2026-08-31
  • AccessibilityService use requires a Permission Declaration Form plus a prominent in-app disclosure that is not bundled with other disclosures and does not live only in the privacy policy
  • The deterrent is framed as a user-configured, opt-in self-commitment tool: the user picks their own apps, one tap dismisses, nothing runs autonomously

Notes

  • Android-only by design (iOS does not expose the needed APIs)
  • Back is trapped on the warning screen; Home and Recents always escape

Troubleshooting

  • Intercept not firing: confirm Accessibility ON and overlay allowed
  • Warning not showing: confirm WarningActivity.getDartEntrypointFunctionName() returns warningMain
  • App missing from picker: add it to both the <queries> block and KNOWN_FOOD_APPS

License

Private/internal use unless a license file is added.

About

Humorous Android food-delivery deterrent app built with Flutter + AccessibilityService.

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages