A vending machine sells when nobody is there. The cabinet, the motor and the coin reader do their work on their own, but the machine still needed a face for the customer: something that could show what is in stock, take the order, and tell the controller to drop the right product into the right slot.
That face is this app. It opens on the catalogue of the machine, filtered live as you type, and every card is a product with a price. Tapping a product opens the detail screen, where the quantity can be adjusted and the total is recalculated. Paying moves the order through a real state machine - CREATED, PAID, DISPENSING, COMPLETED - and the moment the order reaches DISPENSING the app writes a dispense command to the controller over USB serial at 9600 baud. The customer gets a confirmation, the machine turns the motor, and the slot opens.
Underneath, the app is a small Clean Architecture project. The presentation layer holds the catalogue and the payment screen, the domain layer holds the use cases (get items, create order) and the models (item, order, order status), and the data layer holds the repository implementation, the Retrofit service, and the serial link to the hardware. Koin wires it together, Moshi parses the payloads, and Glide loads the product images.
The catalogue is deliberately offline-first. The original build fetched a product list from a placeholder API and three of its four image URLs pointed at third-party hosts that no longer served anything, so a customer standing in front of the machine saw half-empty cards. The catalogue now lives in the app, the images are bundled inside the APK, and the machine keeps selling when the shop Wi-Fi is down.
The repository had not compiled for years when it was picked up again: duplicated model classes, a broken parameter object, missing drawables, a font naming rule that Android no longer accepts, and a build pinned to jcenter and Android Gradle Plugin 4. It was rebuilt on AGP 8.7.3, Kotlin 2.1.0 and Gradle 8.9, cleaned up, and given unit tests for the catalogue and order creation.
The interface speaks Indonesian, uses large touch targets, and keeps every action one thumb away - it is meant to be used quickly, in front of the machine, by someone who just wants a drink.