$ an ad-free Android tip calculator with rounding modes and bill splitting
A simple, ad-free Android tip calculator built to get started with Android development. Open the app, type the bill amount using the custom NumPad, and the tip and total calculate immediately using your saved settings. No extra taps required. Supports rounding modes, multiple currencies, bill splitting among up to 99 people, and several preset themes. Works entirely offline. Available on the Play Store.
The entire UI is built programmatically with no XML layouts. All views are constructed and constrained in Java, which made the layout logic explicit and testable but required manually managing all the constraint relationships that XML would otherwise handle declaratively.
Input flows through an Observer chain: the custom NumPad fires events on each key press; a TextBox observer receives them and updates the displayed input string; a Calculator observer watches the TextBox and recomputes the tip and total; display components observe the Calculator and update their text. This makes the data flow unidirectional and means any component can be tested independently by observing its output without touching the UI.
Tip arithmetic uses BigDecimal throughout to avoid floating-point rounding errors on monetary values. Four rounding modes are supported: UP (always rounds away from zero), DOWN (always truncates), DYNAMIC (rounds to nearest, half-up), and NONE (no rounding. exact decimal result). The selected mode is applied to the final per-person amount after splitting.
Settings (last-used tip percentage, rounding mode, currency symbol, theme selection) persist across
sessions via RxDataStore. Page transitions between the calculator view and the settings view use
ViewPropertyAnimator with hardware layer enabled: 300ms slides that compose on the GPU
rather than redrawing in software.
Programmatic UI without XML means the view hierarchy must be reasoned about entirely in code. ConstraintLayout's constraint API is verbose in Java: every edge constraint is a separate method call, and the relationship between views must be specified explicitly in both directions. Keeping this readable required careful decomposition into factory methods that construct logically related groups of views together.
BigDecimal rounding mode selection exposed a design decision: DYNAMIC mode (round half-up) matches what most people expect from a calculator, but UP and DOWN modes exist for specific expense-reporting cases where the rounding direction matters. Exposing all four and making DYNAMIC the default required documenting the behavior clearly in the help page so users understand why their total might differ by a cent.
First Android app shipped to the Play Store. The combination of programmatic UI, reactive settings storage, and exact decimal arithmetic made it a good introduction to Android's more nuanced APIs. Some key takeaways: