Xpense Dev Retrospective
Package name is com.hassanbukhari.xpense. Strip it down to what actually matters and it looks like this:
Nine activities, one shared base class, one small data package doing all the real work, and one formatting helper. That's the whole app.
Every screen in Xpense is an Activity. Each one extends BaseActivity instead of Android's own AppCompatActivity directly, and that one decision is why the app never had to write repeated boilerplate for theming or insets in every single screen.
| Activity | What it's for |
|---|---|
MainActivity | Home screen. Calendar view, monthly budget, day-by-day spend coloring |
OnboardingActivity | Shown once on first launch, then never again |
AddExpenseActivity | Add or edit a single expense |
AddSettlementActivity | Add or edit a settlement between people |
HistoryActivity | Full expense history, filterable by day/week/month |
SettlementsActivity | List of settlements and balances |
SettingsActivity | App settings, currency, categories |
DataActivity | Backup export and restore |
AboutActivity | Version info, credits, links |
BaseActivity isn't a screen a user sees. Every other Activity extends it, and it handles two things once so every screen doesn't have to: enabling edge-to-edge display with proper inset padding, and forcing the app to follow the system's light/dark setting rather than any per-app toggle.
Nine files handle almost every screen's logic. This is intentional. Activities are supposed to be dumb, they show things and react to taps, the actual decisions about what data means and how it's stored live in data/.
| File | Role |
|---|---|
AppDatabase | The Room database itself, wires the DAOs together |
Expense / Category | The actual data models, what a row in the database looks like |
ExpenseDao / CategoryDao | The queries Room is allowed to run against those tables |
ExpenseRepository / CategoryRepository | The layer Activities actually talk to, sits between the UI and the DAOs |
SettingsManager | Simple key-value settings, currency, onboarding flags, still on SharedPreferences |
BackupData | The shape of an exported backup file |
LegacyMigration | Runs once, reads the old JSON and SharedPreferences data, writes it into Room |
Why a repository layer: an Activity never talks to a DAO directly. It asks the Repository. If the Room queries ever need to change, or caching gets added later, only the Repository has to change, not every Activity that happens to need expense data. It's one extra file per feature, in exchange for not touching nine Activities every time storage logic changes.
SettingsManager deliberately wasn't moved into Room during the rewrite. A handful of flags and a currency string don't need a schema, a query language, or a DAO. Room earns its place for structured, growing data like expenses. It would be overkill for four settings keys.
Nothing unusual here, but worth naming: layout/ holds the XML for every screen and list item, values/ and values-night/ hold light and dark color definitions respectively (no manual toggle logic, Android just picks the matching folder), drawable/ holds the calendar dot icons among other things, and mipmap-anydpi-v26/ holds the launcher icon.
Next up, the file Android actually reads before any of this code runs at all: AndroidManifest.xml.