September 17, 2026
NotiFly — Bank Notification Forwarder.
MobileAn Android app that listens for bank notifications, filters the relevant ones, keeps them as local history, and forwards them to your own backend server.

Description
NotiFly is an Android app that listens to bank notifications on the phone, filters the relevant ones, stores them as local history, and forwards each one to a backend server you control. It is a personal/internal tool rather than a multi-user app: the server URL and API key are configured directly on the Settings screen.
The app never parses amounts or transaction types itself. It forwards the raw notification and leaves all parsing to the server, which keeps the app simple and lets the parsing logic evolve without shipping a new APK.
Background
Many Indonesian banks do not offer a webhook or API for incoming transfers, but their mobile apps do post a push notification for every transaction. NotiFly turns those notifications into a data feed: a phone with the bank app installed becomes a bridge that pushes transaction events to a server, where they can be reconciled against orders or logged automatically.
Goal
Build a reliable, low-maintenance notification forwarder that captures only what it should, never loses an event when the network or server is down, and treats sensitive banking text carefully.
Features
- Notification capture via
NotificationListenerService, with group-summary notifications skipped to avoid duplicate sends. - Two-level filter: an app whitelist (required) plus optional case-insensitive keywords searched across title, text, big text, and sub text. Anything that fails the filter is discarded immediately and never written to disk.
- Reliable delivery: each captured notification is stored as
PENDINGin Room and sent by a WorkManager job toPOST /notifications, withSENT/FAILEDstatus tracking and retry. - History screen with live search, filters, status badges, day headers, and a detail sheet showing every stored field, including the raw extras JSON.
- Export the currently filtered history as JSON or CSV through the system share sheet.
- JSON Tester tab to preview the exact payload for a captured notification.
- Settings for server URL, API key, bank packages (with an installed-app picker), filter keywords, retention window (7 / 30 / 90 days or keep all), and a Test connection ping.
- Access tab that guides the user through notification access, battery-optimisation exemption, and OEM autostart settings.
- Automatic history pruning, boot-completed rescheduling, and a failure notifier.
Technologies
- Kotlin 2.0 with Jetpack Compose and Material 3 (blue theme, swipeable tabs).
- Room for local history and DataStore for configuration.
- Retrofit + OkHttp with kotlinx.serialization for the server API.
- WorkManager for sending, retrying, and pruning in the background.
- Android Keystore–backed encryption (
SecretCipher) for the stored API key. - Manual dependency injection through a small
AppContainer, with unit and instrumented tests.
How to Run
Requires JDK 17 and the Android SDK (platform 36). Create local.properties pointing at the SDK, then:
./gradlew assembleDebug # build debug APK
./gradlew test # run unit tests
./gradlew installDebug # install on a connected device
On the phone, grant notification access from the Access tab, then fill in the server URL, API key, and bank packages in Settings and tap Test connection.
Technical Decisions
Filtering happens before anything is persisted, so irrelevant notifications — which may still be personal — never touch disk or the network. Every accepted notification is written to Room first and only then handed to WorkManager, which makes sending durable across process death, reboots, and network outages. Payload logging is gated behind a debug-only build flag so release builds never log banking text, and cleartext HTTP is only allowed in debug builds for LAN testing.
What I Learned
This project was a practical tour of Android’s background-work constraints: special permissions like notification access, aggressive OEM power managers that kill listeners, and designing around WorkManager to guarantee delivery. It also reinforced keeping the client thin — pushing parsing to the server made the app smaller and easier to trust.
Notes
The repository is private. The app targets Android 8.0 (API 26) and newer; a release build is about 1.9 MB.

Hey! I’m Fanny, the software engineer tending to this digital garden. You can read more about me, or subscribe by email.