Wallet is a fully offline Android app for recording personal income and expenses. Data stays on the device; users can export a JSON-text backup and import it later without relying on a hosted service.
The app currently targets Android API 34 and supports Android 5.0 (API 21) and above.
- Add positive or negative wallet entries with an amount, details, and date/time.
- Edit, clone, delete, or copy an entry's details.
- Search details and amounts, including numeric searches such as
123or-123. - Filter entries with a precise From/To date and time range.
- Order results by date or amount.
- Hide the total balance for privacy.
- Select 12-hour or 24-hour time from the overflow menu.
- Export and import local backups.
app/
src/main/java/com/example/abdul/bank/
MainActivity.java Main list, filters, search, import/export entry points
AddOrSubtract.java Create and edit transactions
DBHelper.java Compatibility/repository facade used by UI code
SPManager.java User preferences (filters, ordering, privacy, time format)
data/
WalletEntity.java Room mapping for the legacy Wallet table
WalletDao.java Room data access methods
WalletDatabase.java Room singleton and migrations
common/utils/DateUtil.java Date/time display formatting
src/main/res/ Layouts, strings, icons, menus, styles
src/androidTest/ On-device Room migration tests
schemas/ Exported Room schema JSON
Requirements:
- JDK 17
- Android SDK with API 34 installed
- An emulator or Android device for instrumentation tests
Windows commands:
$env:JAVA_HOME='C:\Program Files\Java\jdk-17'
$env:Path="$env:JAVA_HOME\bin;$env:Path"
.\gradlew.bat assembleDebug
.\gradlew.bat testDebugUnitTest
.\gradlew.bat connectedDebugAndroidTest # requires a connected device/emulator
.\gradlew.bat assembleReleaseDebug APK output:
app/build/outputs/apk/debug/app-debug.apk
The release build produced by this repository is unsigned unless a release signing configuration is supplied.
The database is deliberately compatibility-sensitive. Existing users may already have data in the original SQLite database.
- Database file name:
wallet.db - Current database version:
2 - Table name:
Wallet - Canonical columns:
_id,Date,DateLong,Amount,Details DateLongis an epoch-millisecond value and is the authoritative transaction timestamp.- Room uses the same table, columns, and version so it can adopt an installed version-2 database without replacing rows.
When changing persistence code:
- Never rename
wallet.db,Wallet, or existing columns without a migration. - Never add
fallbackToDestructiveMigration(). - Never use
DROP TABLEas an upgrade strategy. - Increase the Room version for every schema change.
- Add an explicit Room migration and an instrumentation test for every new version path.
- Keep exported schema JSON under
app/schemas/up to date.
WalletDatabaseMigrationTest creates real legacy version-1 and version-2 databases, then opens them with Room. It verifies that IDs and transaction values survive migration/adoption.
Version 1 did not contain DateLong; it only stored a formatted local date string. The 1 -> 2 migration parses that string in the current locale/timezone where possible. If it cannot parse a date, the row is retained and DateLong remains null. This is unavoidable because the old format did not store a timezone or an absolute timestamp.
Transaction timestamps are stored as epoch milliseconds. The app displays an entry in the device's current timezone, preserving absolute-instant behavior.
The overflow menu includes Use 24-hour time. Its preference is persisted and controls:
- Entry dates in the list
- From/To filter labels and pickers
- Create/edit date display and time picker
- Result-summary dates
The backup filename remains a filesystem-safe technical timestamp.
Search settings and ordering are stored in SharedPreferences through SPManager.
- Details phrase / keyword modes use
LIKEmatching. - A fully parseable number also searches the amount column. For example,
123matches both123and-123because amount matching uses the existing “contains” behavior. - The six order choices cover newest/oldest date, signed high/low amount, and largest/smallest absolute amount magnitude.
SQL parameters are bound with SimpleSQLiteQuery; do not return to string-concatenated user search queries.
Exports are JSON arrays written to a .json.txt file. Each current record contains:
{
"date_string": "08-Aug-2026 04:44 AM",
"date_long": 1786160640000,
"amount": -1250,
"details": "Groceries"
}Import and export use Android's system document picker on every supported Android version. They do not require storage permissions or direct filesystem paths. Backup text is written and read as UTF-8, and imports are limited to 10 MB while reading.
Current exports intentionally omit _id. Older backup files that contain _id remain compatible because imports ignore it. Each imported entry receives a new local ID, avoiding primary-key collisions when adding backup entries alongside current entries. Imports are currently additive; the restore-choice workflow remains future work. date_long is the value used by the app after import.
- Keep data-entry actions clear and compact; the primary screen is designed for frequent use.
- Preserve at least 48dp targets for normal screen controls. Compact dialog actions are an intentional exception where several actions must remain visible.
- The entry-details dialog prioritizes a scrollable details area over actions.
- Use string resources for all user-visible text.
- A zero amount is allowed. Its success toast is simply
Saved, because+0and-0are misleading.
Before changing code:
- Check
git statusand preserve unrelated worktree changes. - Search call sites before changing a shared helper or database API.
- Keep
DBHelpercompatible unless intentionally refactoring every caller. - Avoid changing backup semantics without a backward-compatible import path.
- Prefer small, focused UI changes over visual redesigns that make common actions slower.
- For database work, run the instrumentation migration tests on an emulator/device before handoff.
For normal UI-only work, a focused debug build is usually sufficient:
.\gradlew.bat assembleDebug- Support for Fractional point amounts.
- Should there be difference a b/w date-created and date-of-transaction? If yes, then we need to add a new column for date-created and make it non-nullable with default value as current timestamp. And then we will have to update the import/export logic to support this new column.
- Scan for any memory leaks in this app. dont make any changes yet.
- Add optional Google Drive backup to the user's own Google account, using app-scoped Drive storage rather than a central server.
- Let users choose a backup file from a list during restore and explicitly choose whether to replace current entries or add imported entries.
- Consider removing the legacy
Datecolumn in a dedicated, tested Room migration;DateLongis already the canonical timestamp. - Move database operations off the main thread while retaining the current UI behavior.
- Improve the Import/Export behavior like stream import/export and progress feedback for large backups. For this we might need to introduce versioning in Import/Export options. Like latest app is exporting a v2 variant of backup files so that import option will then ask the user to choose which version of the backup file you are importing? And to support streaming backup files we will have to move towards jsonl-file-type kind of approach.
- Add focused UI tests for import/restore, ordering, time-format switching, and the privacy toggle.
- Speech-to-text for details entry. If possible, also detect the amount from the speech input.
- Detect micro-finance app notifications with the user-defined text pattern and which apps' notifications to apply listener to, and automatically add a wallet entry with the amount and details from the notification description.
- A better icon for the app.
- Floating button for an entry.