A travel-first problem
I built Fexra for my own trips, where I wanted to check a conversion even when mobile data was unavailable. The initial pair is Paraguayan guaraní (PYG) to euros (EUR), and the app keeps the latest successfully downloaded rates on the device.
The conversion flow
The converter lets me change the source and target currencies, swap the pair and enter an amount using rules that reflect each currency’s precision. PYG and JPY are handled as whole-unit currencies; other supported currencies accept up to two decimal places. A rates view puts favourite currencies first and shows their value against one euro.
Working with a saved rate snapshot
Fexra reads its saved data immediately on launch. When a connection is available, it downloads EUR-based rates from fawazahmed0/exchange-api; jsDelivr is the primary endpoint and the project uses the documented pages.dev endpoint once as a fallback. It refreshes automatically when there is no previous successful download, 24 hours have elapsed, or the device clock is earlier than the last download. The refresh control can also request an update manually.
A response replaces the saved snapshot only after its date and required EUR and PYG rates pass validation. If the request fails or the data is invalid, the previous snapshot stays available. The interface distinguishes the rate provider’s date from the time the phone downloaded it. Rates three days old or more are marked as stale, but remain usable for conversion. On a first launch without a saved snapshot, the app asks for a connection before rates can be loaded.
Engineering decisions
The calculation and input rules live in a Kotlin domain layer, separate from the Android UI and network repository. The repository owns HTTP requests and stores each validated snapshot in one SharedPreferences value; a ViewModel exposes screen state through StateFlow to the Jetpack Compose interface.
Conversions use BigDecimal and DECIMAL128 rather than binary floating-point arithmetic, with rounding applied at the displayed currency precision. The JSON parser rejects malformed dates and missing or non-positive required rates while discarding invalid optional currencies. JVM unit tests cover conversion, parsing, storage round-trips, input handling, formatting and refresh-age rules.
Release and availability
Version 1.0.0 has a debug-signed APK release intended for personal use. The release belongs to the private cambio-offline repository, so this portfolio does not offer a public download or source-code link.
Possible next steps
These are future improvements, not implemented features: add repository-level tests with controlled network responses for the primary/fallback flow and snapshot preservation, and evaluate a production-signed distribution route if the app is ever prepared for wider release.