By a solo Android developer
Small, fast tools for
Android & Jetpack Compose.
No sign-up, no clutter. Convert colors, size up your builds, and sort out Gradle versions — the everyday chores, done in one tap. Built and battle-tested while shipping real apps to Google Play.
Jetpack Compose Color Converter
HEX to Color(0xFF…), plus XML, RGBA, HSL, an alpha slider and a WCAG contrast check.
dp / sp / px Converter
Convert dp, sp and px across every density bucket (mdpi–xxxhdpi), with a font-scale option for text.
AdMob eCPM & Revenue Calculator
Project daily, monthly and yearly ad revenue from DAU, impressions and eCPM — and calculate eCPM from earnings.
API Level ↔ Version ↔ Codename
Search any Android API level, version, codename or VERSION_CODES constant. Current to API 37.
JSON Formatter & Validator
Pretty-print, validate and minify JSON with clear error messages. Runs entirely in your browser.
JWT Decoder
Read a token's header and payload, with expiry and issued-at shown as dates. Decodes locally — no upload.
Unix Timestamp Converter
Convert epoch ↔ date in local time and UTC, seconds or milliseconds, with a live current timestamp.
App Icon Generator
One square image → ic_launcher in every density (mdpi–xxxhdpi), round icons and the 512px Play icon, zipped.
QR Code Generator
Turn a Play Store link or any text into a QR code and download a crisp PNG. Runs entirely in your browser.
strings.xml Escaper
Paste plain text → a properly escaped Android string resource. Fixes apostrophes, quotes, ampersands and more.
Color Contrast Checker
Check text vs background against WCAG AA/AAA. Live ratio, pass/fail, and a preview — all in the browser.
Exposure Calculator
Phone Pro mode: set a shot, see every shutter/ISO pair with the same brightness. Freeze motion or cut noise.
Base64 Encoder / Decoder
Encode and decode Base64 with correct UTF-8 handling, URL-safe mode and data URIs. Runs in your browser.
Where to actually find 12 testers
The hard part of the closed-testing rule isn't the number — it's finding twelve real opt-ins who stay. Where to look, and how to keep them.
Firebase Functions vs a cheap VPS
Does your app even need a server? And if so, when serverless wins, when a small VPS is the better call, and how to choose without over-building.
My AdMob revenue looked broken — it was volume
The four dashboard numbers that prove your ads work, and why near-zero earnings are almost always traffic, not config.
Calling an LLM without shipping your API key
Why a key in buildConfigField ends up in the APK, and the server-proxy pattern that keeps it out of users' hands.
Measuring a child's height with ARCore
Plane drift, the top-of-head problem, and why a humble calibration step carried more than any clever vision math.
Migrating 10 apps to target API 36 in one weekend
The full version chain, every error I actually hit, and the two that cost the most time even though neither was a code bug.
The 12-tester, 14-day gate: what resets the clock
What "opted in" really counts, the re-apply trap that restarts your 14 days, and the mistakes that quietly drop you below 12.
Fighting engine vibration in a BLE brake sensor
Why a running engine breaks naive brake detection, and the signal-processing and mounting changes that finally made it reliable.
My app shipped English-only despite full translations
How App Bundle per-language splits strand your translations, how I reproduced it locally, and the two reliable fixes.
When to move off free hosting
When free tiers stop being enough, when a VPS actually makes sense, and why DigitalOcean is a clean first cloud — honest pricing and alternatives included.
minSdk vs targetSdk vs compileSdk
What each SDK setting actually controls, how they relate, and how to choose sensible values.
Fix missing translations in an AAB
Why language splitting shows English instead of your translations — and the one-line fix.
Closed testing: 12 testers for 14 days
Who the rule applies to, how to set it up, and why apps still get rejected after the 14 days are up.
Play App Signing explained
Upload key vs app signing key, what happens if you lose your upload key, and how to keep your keystore safe.
AAB vs APK, explained
What the App Bundle is, why Play wants it, how users still get an APK, and when to build an APK yourself.
Phone Pro mode: setting exposure
What ISO, shutter and exposure compensation do, how to read the meter, and starting settings for common scenes.
Beta testers wanted — help each other launch
Join the tester group to help pass a closed test, and submit your own app so we test each other's. Mutual, no cost.
targetSdk 36 build errors, and their fixes
The real errors from bumping to API 36 — AGP/Gradle/Kotlin chain, lint-vital failures, a Windows file lock — each with the fix.
Production access "needs more testing"
Usually not a rejection — it's the 14-day clock restarting. Why it happens and how to stop re-triggering it.
Internal vs closed testing: which counts
Only one track satisfies the 12-tester production requirement. What each is for, and the mistake of testing on the wrong one.
What if you miss the target API 36 deadline?
Your live app stays up — you just can't ship updates. Exactly what's blocked, what isn't, and how to request an extension.
Play Billing 8: removed & changed APIs checklist
Grep your billing code against this list: what changed, what's gone, and the five things you don't need to touch.
The target API 36 deadline (Aug 31)
What the Android 16 requirement actually blocks, and the AGP/Gradle chain reaction one line sets off. From migrating ten apps.
Play Billing Library 8 migration
The one breaking change that touched every app: queryProductDetailsAsync now returns a result object, not a List.
Content hidden behind the status bar
Edge-to-edge is enforced on API 35/36. Enable it and consume window insets so your toolbar and buttons stop getting clipped.
Requires compiling against a newer API
A library bump forced a compileSdk bump. What checkDebugAarMetadata enforces, and why raising compileSdk is safe.
More than one file found with path META-INF/…
Two dependencies shipped the same metadata file. The packaging { resources { } } fix — and when not to pickFirst.
Gradle ran out of heap
"JVM heap space is exhausted." How to size org.gradle.jvmargs correctly instead of just cranking it to the max.
Cannot fit classes in a single dex file
The 64K method-reference limit, when multidex is the fix, and why shrinking with R8 is often the better one.
@Composable invocations can only happen from a @Composable
Why Compose rejects composable calls in onClick, coroutines and helpers — and the state-driven fix.
AAPT2 error: check logs for details
The message hides the real cause. How to surface the actual error line and fix the common culprits.
My app shipped in English only
Translations worked in debug but the released bundle served English. What I ruled out, the real cause, and the one-line fix.
Don't ship real AdMob IDs in a closed test
Closed-test taps on live ads are real traffic. Use demo units or test devices so you don't get flagged for invalid activity.
Firebase broke my build with a Kotlin error
What "incompatible version of Kotlin" means, why a Firebase BoM bump triggers it, and how to line the versions back up.
Inconsistent JVM-target compatibility
Gradle stopped my build because Java and Kotlin targeted different JVM versions. Why they drift, and the one-line toolchain fix.
Debug works, release crashes
The release build crashed on launch while debug was fine. It was R8 stripping classes I use via reflection — and the keep rules that fixed it.
"Duplicate class" found in modules
One new dependency pulled the same library in twice. How to find the two culprits and exclude the duplicate.
INSTALL_FAILED_UPDATE_INCOMPATIBLE
Android refused the install because the APK was signed with a different key than the copy on the device. Why, and the safe fixes.
@Preview fails but the app runs
The preview pane showed a render error while the app ran fine. Why previews break — ViewModels, context, real data — and the split that fixes it.
Default FirebaseApp is not initialized
Firebase crashed on the first call. Usually the google-services plugin or config file is the missing piece — here's the checklist.
Manifest merger failed
A library set the same attribute to a different value than my app. What the merged manifest is, and how tools:replace resolves it.
SDK location not found
A fresh clone wouldn't build: local.properties never travels with the repo. Why that's correct, and the right way to fix it.
Cleartext HTTP traffic not permitted
Android blocks plain HTTP by default. Why usesCleartextTraffic=true is the wrong fix, and how to scope it to a dev server.
Keystore was tampered with
The scariest message in Android signing almost always just means a wrong password. How to tell which one, with keytool.
AGP · Kotlin · Compose Version Check
Find a compatible set of Gradle plugin, Kotlin and Compose compiler versions.
Why these tools exist
Every one of these started as a chore I hit while building and publishing my own Android apps — pasting a hex into a Color(0xFF…) literal, checking whether a color passed contrast, working out which Kotlin version a Compose compiler wants. Instead of searching each time, I turned them into small single-purpose pages that load fast, run entirely in your browser, and never ask you to sign in. New tools land here as I build them.