← Back to Projects
LKR P2P Rate cover
Android App · Home-Screen Widgets

LKR P2P Rate

Shows the USDT to LKR P2P rate you can actually get, along with exchange rates for any currency, live on your Android home screen.

Role
  • Product
  • UI design
  • Android engineering
Platform
  • Android 8.0+
  • phones & tablets
Type
Widgets + companion app
Stack
  • Kotlin
  • Jetpack Compose
  • Glance
Version
  • v1.1.0
  • Oct 2026
Licence
  • Open source
  • MIT

01Overview

In Sri Lanka, a lot of people move money using USDT, and the number they really care about is the Binance P2P rate, which is how many rupees a real trader will pay for one USDT right now. Checking it usually means opening an exchange app, choosing whether you’re buying or selling and how you’ll pay, then scrolling past ads that need a minimum order much bigger than yours.

LKR P2P Rate puts that number right on your home screen. Its resizable widgets read the public P2P order book and show the best rate you can actually get for your usual order size, along with a trend chart for the day, week or month. The companion app adds a currency converter, the live list of ads, rate alerts, a second widget for normal exchange rates across more than 300 currencies, and nine colour themes. There’s no account to create, no ads and no tracking.

The headline price lies

The top P2P ad often needs a minimum order much bigger than most people trade. Showing that rate to someone selling 500 USDT means showing them a price they can never actually get.

Promoted ads jump the queue

Binance pins paid "Promoted Ad" listings above ads with better prices, so the first ad in the list often isn’t even the best one.

Android sleeps

Android’s Doze mode and the battery savers that phone makers add can delay background work for hours, which leaves gaps in the rate chart overnight.

One widget, every size

The same widget might be a small 2x1 strip on one launcher and a large 4x4 card on another, with different corner shapes and font sizes.

02Key Features

Resizable rate widget

It goes from a small 2x1 strip all the way up to a full 4x4 card. There are three layouts, and the widget picks one based on its exact size, with text that scales to fit.

Charts on the home screen

Day, week and month charts are drawn right on the widget, with a small trend label. If data collection paused at some point, the chart shows a break there instead of making up a flat line.

Sell / Buy per widget

Tap a widget to switch between the sell rate and the buy rate. Each widget remembers its own side, time range and theme.

Converter priced from real ads

Convert USDT to LKR or the other way round. The price comes from the best ad that will accept that exact amount, and the app tells you which ad that is.

Live order book

The Ads tab filters the list by your order size, highlights the best ad for you and tucks away the ones that can’t take your order, along with the reason why.

Edge-triggered alerts

Set an alert like "Sell rate rises to 332.00". It fires once when the rate crosses that line, and only resets after the rate crosses back, so a rate that hovers around your target won’t keep spamming you.

Exchange Rate widget

A second widget and converter for any currency pair, covering more than 300 currencies and the major cryptocurrencies, with 1W, 1M, 3M and 1Y charts that work offline.

Nine themes

Choose from Emerald, Ocean, Amethyst, Sunset, Rose, Gold, Graphite, OLED Midnight and Material You, and adjust how transparent the widgets are.

03Screenshots

Screens taken from the real product. Tap any of them to see it full size.

04Architecture

It’s a single-module Android app written in Kotlin. At its heart is a core that knows nothing about Android, with a small data layer on SQLite and background collection handled by AlarmManager and WorkManager. One set of drawing code is shared by the Glance widgets and the Compose app, so both draw exactly the same chart.

Pure core

The core handles the data format, the clients for Binance P2P and exchange rates, the calculations for the price you can actually fill and the median, the trend, the converter and the alert engine. It’s plain Kotlin and is unit-tested against real recorded responses from Binance.

Fillable rate

To work out the rate, the app puts promoted ads back into proper price order, sorts them from best to worst for the side you’re on, and then takes the first ad whose rupee limits and available USDT can handle your order size. That ad’s price is the rate for 1 USDT.

Local history

SQLite tables store rate samples, the latest snapshot of the order book and your alerts. The app keeps 30 days of history for each side and order size, which you can export as a CSV, and none of it ever leaves your phone.

Doze-safe collection

An inexact alarm that’s allowed to run even when the phone is idle handles the 15-minute check, with WorkManager as a backup. There’s no foreground service, no permanent notification and no special permission needed.

Shared painters

The background and chart drawing code renders to images that are used by both the Glance widgets and the Compose screens, so the widget and the app can never show different charts.

Exchange rates

Exchange rates come from daily files based on the US dollar, provided by an open-source currency API. Any currency pair is worked out by going through the dollar, and history is downloaded once per time range and saved so it works offline.

05Tech Stack

App

Kotlin 2.2Jetpack ComposeMaterial 3ViewModel · coroutines

Widgets

Jetpack Glance 1.1Size-dependent layoutsPer-widget config activity

Background & data

AlarmManagerWorkManagerSQLiteNotifications

Quality & tooling

JUnitRobolectric render testsAndroid lintR8 shrinkingPython + Pillow image pipeline

06Engineering Challenges

ChallengeThe rate that gets shown as the headline price is often impossible to get for an ordinary order.
SolutionIn this app, the rate means the best ad that will actually accept your order size (500 USDT by default). The converter tells you which ad it used, and explains when no single ad can handle the amount.
ChallengeOvernight, Doze mode delayed the background job for hours, so the charts ended up as short pieces with gaps between them.
SolutionI moved the checks onto an alarm that’s allowed to run while the phone is idle, with WorkManager as a backup. Any reading that has missed three updates is marked as Stale in amber, and real gaps are drawn as a faint dashed line instead of being hidden.
ChallengeA widget doesn’t know how big it is on any particular launcher, and the system font size can push text outside its box.
SolutionThere are three layouts chosen based on the widget’s exact size. Text is sized in dp and scales with the width, numbers shrink before they can overlap, and the corners follow the launcher’s shape but are limited on short strips.
ChallengeAn alert set at a price the rate keeps hovering around would go off every fifteen minutes.
SolutionAlerts only fire when the rate crosses the line. They go off once, only reset after the rate crosses back, and pause if they were set for a different order size.
ChallengeScreenshots made by hand go out of date as soon as the interface changes.
SolutionEvery screenshot is the real app code, rendered by Robolectric using sample history and recorded order books, and then framed by a script. That means the docs, and the images on this page, can be regenerated straight from the source code.

07Results & Learnings

2
resizable home-screen widgets
300+
currencies in the converter
9
colour themes
34
unit and render tests
  • An Android app that’s ready for the Play Store, with widgets from a small 2x1 strip to a 4x4 card, a P2P tab with a converter and charts, the live order book, alerts, an exchange rate widget and converter, and themes for each widget.
  • Privacy is built in from the ground up. There’s no account, no analytics, no ads and no crash reporting. The only requests the app makes are to Binance’s public P2P search and to public daily exchange rate files.
  • Every image in the README and on this page is rendered from the real app code, not mocked up in a design tool.
  • It’s free and open source under the MIT licence, and the rate logic was ported from DFanso’s macOS widget.

What I learned

  • Work out honestly what the number should be first. The whole product comes down to the difference between the top ad and the ad you can actually use.
  • On Android, "every 15 minutes" is really a negotiation with the operating system. It’s better to design the interface to admit when data is out of date than to hide it.
  • Sharing one piece of drawing code between the widget and the app gets rid of a whole category of bugs where the widget shows something different from the app.
  • Rendering screenshots from tests means the documentation can never fall out of sync with the code.