Dunio is a mobile-first Android app for shared household finance tracking.
It is designed for small shared workspaces such as couples or families who want a simple way to track:
- net worth
- assets and liabilities
- income and expenses
- recurring transactions
- monthly household progress
The app is intentionally not an accounting suite. The focus is fast daily use, clear numbers, and a simple shared-household model backed by Firebase.
- Android app source code
- Gradle project files
- GitHub CI workflow
- contribution and release documentation
- dedicated Wear OS quick-entry architecture notes in docs/wear-os-quick-entry.md
All financial data belongs to a household.
Supported roles:
ownermember
Both users share the same data inside a household:
- dashboard
- assets
- transactions
- categories
- recurring transactions
- month close state
Each household uses one shared currency configured from Settings. The app does not support per-asset currencies or mixed-currency totals.
- Google sign-in with Firebase Authentication
- persistent session restore
- create household
- join household with invite + household ID
- owner/member household model
- household onboarding keeps the user behind a loading state until session access is fully resolved
- net worth
- total assets
- total liabilities
- monthly cash flow
- savings rate
- financial runway
- 3 / 6 / 12 month net worth chart
- month selector
- full-screen get-started flow for households missing entries or assets
The dashboard now renders a lightweight skeleton state while its monthly summaries load, so the screen structure appears immediately instead of blocking behind a generic full-screen spinner.
- monthly transaction list
- Firestore reads scoped to the selected month instead of the full household history
- grouped by day
- search
- entries / reports toggle in the toolbar
All / Expenses / Incomefilter- unusual spending insights
- category spending report with a horizontal bar chart plus totals, averages, percentages, and transaction counts
- entry edit and delete flow
- quick-entry flow
The entries screen now keeps the list month-scoped, loads category reports only when the user opens Reports, and limits unusual-spending insights to a small rolling window around the selected month instead of the full transaction archive.
It also renders a local shimmer skeleton during cold loads and uses a lightweight route shell plus progressive body mount so the first tab switch feels more immediate on slower devices.
To reduce ANR risk on older phones, Firestore transaction listeners now dispatch off the main thread and the visible ledger list is flattened into truly lazy day headers plus entry rows instead of rendering whole day groups in one composed block.
Entry category pickers also keep a tiny local Room usage index so the most-used categories for the current household and entry type can be shown first without querying Firestore. The index is keyed by householdId + type + categoryStableId, grows only by category used, and is treated as best-effort so entry saves never depend on local ranking updates.
- monthly asset snapshots
- Firestore reads scoped to the selected month plus the previous month instead of the full asset history
- assets and liabilities in one feature
- search
- edit and delete flow
- copy previous month snapshot
- monthly net worth summary
- household currency applied automatically to new asset snapshots
The assets screen now reads only the selected month and previous month snapshots so list rendering, month-over-month change, and Copy previous month stay responsive without downloading the full asset archive.
Like Entries, it now uses a local shimmer skeleton during cold loads plus a lightweight route shell/progressive body mount instead of blocking the whole app shell.
- household currency
- app language selection
- app theme selection
- household data export to CSV or JSON
- category management
- recurring transactions
- month close
- household members
- invite management
- household details
The household currency is changed from a dedicated searchable settings screen, and the selected currency is surfaced at the top of the list.
The app also includes a dedicated language selection screen in Settings. It uses the same list style as the currency screen, keeps a stable alphabetical order, and highlights the currently selected language.
The app theme is also changed from a dedicated Settings screen using the same list treatment as language selection. Users can choose Light, Dark, or System, and the selected theme is persisted across launches.
The app also includes a dedicated export screen in Settings. It can export the current household as a full JSON backup, plus entries and assets as CSV files through the Android system document picker.
Recurring transactions can also be auto-applied on app startup. Once per day, after the session is ready, the app checks whether the current household has due recurring items for the current month and materializes them into the entries list without requiring a manual Apply due tap.
The month-close screen now reads only the selected month's entries and asset snapshots instead of loading the full transaction and asset history.
Bottom navigation now restores state across the primary tabs (Dashboard, Entries, Assets, Settings) so rapid tab switching reuses warm screens more reliably.
Realtime Firestore DAO listeners now use a shared background executor for snapshot callbacks, which keeps document deserialization and mapping away from the UI thread across dashboard, entries, assets, members, invites, recurring, categories, and month close.
Local-only convenience data is stored in Room. The first local table tracks category usage counts for the entry category picker; source-of-truth finance data remains in Firestore under the current household.
Current app-language support includes:
- English
- Italian
- French
- German
- Dutch
- Spanish
- Portuguese (Brazil)
- Russian
- Arabic
- Chinese (Simplified)
- Japanese
- Quick Settings tile
- home-screen quick-entry widget
- home-screen spending summary widget
- home-screen top categories widget
- dedicated Wear OS app module
- non-standalone watch quick entry
- watch-to-phone RPC over Wear Data Layer
- local queue on the watch when the phone is temporarily unavailable
- package layout split into
data,model,navigation,presentation,protocol,sync,theme, andui
Wear OS implementation details and architecture notes live in docs/wear-os-quick-entry.md.
The spending widget now renders from a tiny local cache first and refreshes remotely afterward. This avoids the launcher getting stuck on the static preview while Firebase work is still in flight.
The top-categories widget follows the same cache-first pattern. It shows the top three expense categories for the selected month and renders each share as a horizontal meter that uses the same percentage semantics as the in-app category spending report.
- Kotlin
- Jetpack Compose
- Material 3
- Hilt
- Coroutines + Flow
- Firebase Authentication
- Cloud Firestore
- Navigation Compose
- Glance widgets
Main source root:
app/src/main/java/com/davideagostini/summ
Top-level packages:
summ/
├── data/ # Firebase access, repositories, entities, session state, DI
├── domain/ # Shared models and lightweight use cases
├── tile/ # Quick Settings tile integration
├── ui/ # Compose screens, ViewModels, state, feature components
└── widget/ # Home-screen widgets and widget data sources
The current app pattern is:
Screen composable
-> collects immutable state with collectAsStateWithLifecycle()
ViewModel
-> exposes uiState + renderState
Repository / Use Case
-> reads and writes Firebase-backed data
App text is resource-based and ready for Android per-app language switching through the system locale picker and the in-app language screen.
Composable screens should stay focused on rendering and screen orchestration. Derived business data such as totals, grouped lists, chart models, and filtered lists should be prepared in ViewModel or shared model/use-case code.
- Android Studio
- JDK 17
- Android SDK
- Firebase project
To actually run the app, you need your own Firebase project.
Minimum backend setup:
- Firebase Authentication with Google sign-in enabled
- Cloud Firestore created
app/google-services.jsonadded locallyfirebase/firestore.rulesdeployedfirebase/firestore.indexes.jsondeployed
The provided Firestore rules support the initial household bootstrap flow used by the Android app:
- create household document
- create the owner membership
- seed default categories
This repository is app-first, but the Android client depends on that Firebase setup to work correctly.
- Open Firebase Console
- Create a new project
Use this package name:
com.davideagostini.summ
- Open
Authentication - Enable
Google - Set the support email if requested
- Open
Firestore Database - Create a database
- Choose the mode you prefer for local development
From the repository root:
cd mobile-app
./gradlew signingReportAdd the debug SHA-1 and SHA-256 fingerprints to the Android app in Firebase.
Download the file from Firebase and place it here:
app/google-services.json
This file is intentionally ignored by Git.
From the mobile-app/firebase folder:
cd mobile-app/firebase
firebase use --add
firebase deploy --only firestore:rules
firebase deploy --only firestore:indexesIf you prefer, you can deploy with --project <your-project-id> instead of setting an active project first.
cd mobile-app
./gradlew assembleDebug
./gradlew installDebugExpected structure:
users/{uid}
households/{householdId}
households/{householdId}/members/{userId}
households/{householdId}/categories/{categoryId}
households/{householdId}/transactions/{transactionId}
households/{householdId}/assets/{assetId}
households/{householdId}/assets/{assetId}/history/{entryId}
households/{householdId}/recurringTransactions/{recurringTransactionId}
households/{householdId}/monthCloses/{period}
households/{householdId}/invites/{inviteId}
Create a local keystore.properties file by copying keystore.properties.example and filling in your local values.
Example:
storeFile=/absolute/path/to/your-upload-key.jks
storePassword=change-me
keyAlias=upload
keyPassword=change-meBuild a release bundle with:
cd mobile-app
./gradlew bundleReleaseThe output bundle will be generated under:
app/build/outputs/bundle/release/app-release.aab
This repository is set up so that pushing a tag like v0.0.5 can:
- build a signed release APK
- build a signed release AAB
- generate a release mapping file for deobfuscation
- keep optional native debug metadata and symbol tables as workflow artifacts when available
- publish both files to a GitHub Release
Required GitHub Actions secrets:
ANDROID_KEYSTORE_BASE64ANDROID_KEYSTORE_PASSWORDANDROID_KEY_ALIASANDROID_KEY_PASSWORDGOOGLE_SERVICES_JSON_BASE64
Release notes:
- CI validates that the decoded
google-services.jsonmatchescom.davideagostini.summand generatesdefault_web_client_id - release builds explicitly keep
@string/default_web_client_idsoshrinkResourcesdoes not remove Google Sign-In configuration
How it works:
- Convert your keystore to base64 locally
- Store the value in
ANDROID_KEYSTORE_BASE64 - Add the other signing values as repository secrets
- Push a tag such as:
git tag v0.0.5
git push origin v0.0.5The workflow will decode the keystore, sign the release build, generate both APK and AAB, and attach them to the Dunio GitHub Release.
Release builds use minification and resource shrinking. The generated mapping.txt is uploaded as a GitHub Actions artifact so you can keep the deobfuscation file for Play Console or post-release analysis without exposing it in the public GitHub Release. Native debug metadata and symbol tables are also uploaded as optional workflow artifacts when AGP produces them.
Do not commit:
app/google-services.jsonlocal.propertieskeystore.properties.jks/.keystorefiles- service account JSON files
If a local Firebase file was added to Git by mistake:
git rm --cached app/google-services.json- income vs expense analysis with category breakdown
- user feedback mechanism
- budgets with weekly, monthly, and yearly spending limits by category
- reports and summaries for monthly review
- notifications and reminders
- on-device AI category suggestion during quick entry
- import transactions from CSV with validation
- bank CSV import support
- better backup and migration flows
- on-device monthly financial summaries
- Analyze habits with on-device AI insights
- receipt or invoice scan to draft an entry with description, amount, date, and category
Dunio is free to use and open source. If you find it useful and want to support ongoing development, you can do it here:
If you find Dunio useful, consider giving the repository a star.
This repository uses the MIT license. See LICENSE.









