Android SDK Overview
Relay for Android ships the Fruga cashback screen as an Android library. Your app supplies a short-lived token and opens a screen - it never calls the Fruga API and never handles the widget document. This page covers how the library is put together, what it requires from your app, and how it relates to the Web SDK.
Relay for Android is the Fruga cashback screen as an Android library. It is a WebView that hosts the Fruga CDN shell page plus a JSON bridge.
The division of labour is deliberately narrow. Your app supplies a short-lived access token and opens a screen. It never talks to the Fruga API directly, and it never handles the widget document — that lives on Fruga’s CDN and updates there, independently of your app.
| Gradle module | :relay |
| Kotlin package | uk.co.fruga.relay |
| Library coordinate | uk.co.fruga:relay |
How it works
FrugaRelay.open(activity)
→ FrugaRelayFragment mounts a WebView
→ loads the CDN shell page over HTTPS
→ the shell asks your app for a token
→ your tokenProvider (suspend) calls your backend
→ your backend exchanges the user for a Fruga access token
← you return the token, delivered as tokenUpdate
The shell page itself is the same web widget used by the Web SDK, running inside your app’s WebView. Native and shell exchange JSON messages over a small bridge; decoding, timeouts, and message replay are handled for you.
Three things follow from that design, and they account for most of what you will read in this section:
- The widget UI is served from Fruga’s CDN, not from your app. Web-side fixes reach your users without an SDK release or an app update. The shell URL floats on an additive-only
/v1/channel. - Your backend stays the owner of the user. The SDK never asks your app for a Fruga API key at runtime, and never sees your secret. It asks for a token, you mint one server-side.
- Only two origins load inside the WebView. Everything else is handed to the system browser or refused outright. See Navigation and security.
What your app needs to provide
Only two things:
partnerKey— the public key Fruga issued you for your environment.- A
tokenProvider— a suspend function that returns a fresh Fruga access token for the signed-in user, called when the shell needs one.
FrugaRelay.configure(
partnerKey = "pk_live_…",
tokenProvider = { reason -> backend.frugaToken(reason) },
)
Everything else — theming, locale, safe-area insets, back handling, the balance round trip, error reporting — is handled by the library. See Configuration.
PARTNER_SECRET in the app. It signs assertions on your backend and is exchanged there for an access token. What reaches your app is only the short-lived access token — see Authentication for the backend flow.Requirements at a glance
| Requirement | Value |
|---|---|
minSdk | 29 (Android 10) |
compileSdk / targetCompatibility | 35 / Java 17 |
| Toolchain | AGP 8.13.2, Kotlin 2.2.21, Gradle 8.14.5, JDK 17 |
| Android System WebView | Chromium 111 or newer |
The WebView floor is hard and is checked before any WebView is created. There is no downlevelling for older devices — see Installation.
Where to go next
| Page | Covers |
|---|---|
| Installation | Requirements, the WebView floor, adding the dependency, permissions, R8 rules |
| Quickstart | Configure at app start, open the screen, read the balance |
| Configuration | Every option, its type, and its effect |
| Authentication | The token provider, its three reasons, and its guarantees |
| Presenting Relay | Opening, hosting directly, and every dismissal path |
| Balance | Reading the balance over the bridge |
| Errors | The error codes and what recoverability means |
| Logging | The log sink, its events, and what never reaches it |
| Navigation and security | Which origins load, and what happens to everything else |
| Release notes | The floating /v1/ channel and the versioning policy |
| API reference | The full public surface |
| Bridge reference | The messages that cross between your app and the shell |
If you are embedding Relay on the web as well, the Web SDK covers the browser side. Both share the same shell and the same backend token flow, so the assertion you already sign for the widget is reused unchanged.