branchmark
Stamp your debug launcher icon with the current git branch so you always know which branch a build on your home screen came from. A diagonal ribbon shows the branch name; a color emoji shows the branch type. Zero config — branchmark reads the adaptive icon you already have and draws only the banner.
Overview
Install three debug builds from three branches and they're
indistinguishable on the launcher — same icon, same name. branchmark
fixes that. For a branch like fix/login-crash it draws a
red ribbon across the top-right corner reading
LOGIN-CRASH, and a 🔧 emoji for the
fix type in the lower-left.
It never asks you to supply a second icon. branchmark reads your app's
existing mipmap-anydpi-v26/ic_launcher.xml, rasterizes its
vector foreground, paints the banner on top, and generates a debug-only
adaptive icon that overrides the original — release builds are untouched.
No base image or template to provide — branchmark resolves and rasterizes the foreground you already ship.
Glyphs come from bundled Twemoji SVGs, so the wrench looks the same on macOS, Linux, and CI — no font tofu.
The generated icon is wired into the debug variant via the AGP Variant API. Release icons never change.
On main, a detached HEAD, or in CI without a branch, it falls back to a plain "DEBUG" ribbon.
The branch is resolved through a ValueSource; the task is cacheable and up-to-date aware.
Installation
Add Maven Central to your plugin repositories in settings.gradle.kts:
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
Then apply the plugin in your Android application module, after the Android plugin:
plugins {
id("com.android.application")
id("com.rohittp.plugables.branchmark") version "1.0.0"
}
That's the whole setup. branchmark expects a standard adaptive icon
(mipmap-anydpi-v26/ic_launcher.xml with a
<foreground>), which Android Studio's New Project
wizard generates by default.
Requires AGP 7.2+
The plugin wires generated resources through the Android Variant API
(AndroidComponentsExtension). Tested against AGP 9.2.0.
How it works
On every debug build, the generateBranchmarkIcon task runs
the following pipeline, once per density:
-
Parse
src/main/res/mipmap-anydpi-v26/ic_launcher.xmland resolve its<foreground>,<background>, and<monochrome>. -
Resolve the foreground to a bitmap — a vector
drawable is rasterized with Android's own
VdPreviewat the 108dp canvas size for the density; a raster foreground is read directly. - Draw the ribbon (Java2D) and the prefix emoji (a bundled Twemoji color SVG, rasterized with Apache Batik) onto a copy of the foreground, keeping the glyph inside the adaptive-icon safe zone.
-
Write
mipmap-<dpi>/ic_launcher_foreground_debug.pngand a debugmipmap-anydpi-v26/ic_launcher.xmlthat points<foreground>at the stamped image while preserving your original background and monochrome.
The generated res directory is registered as a debug
variant resource source, so AGP's resource merger overrides the icon for
debug builds only.
Branch detection
Branches are read as .../<type>/<name> —
only the final two slash-separated segments matter. The
second-to-last segment is the type (chooses the emoji);
the last segment is the name (the ribbon text). Any
leading segments are ignored, so personal and team prefixes work
without configuration.
| Branch | Type → emoji | Ribbon |
|---|---|---|
fix/login-crash | fix → 🔧 | LOGIN-CRASH |
john/fix/login-crash | fix → 🔧 | LOGIN-CRASH |
team/jane/feat/onboarding | feat → ✨ | ONBOARDING |
main | — (no emoji) | DEBUG (fallback) |
detached HEAD / no git | — (no emoji) | DEBUG (fallback) |
The branch name itself is resolved with this precedence:
branchOverride(DSL)-PgitBranch=…(Gradle property)GITHUB_HEAD_REF(GitHub Actions PR builds)GIT_BRANCH(common CI convention)git rev-parse --abbrev-ref HEAD
Configuration
Every option has a sensible default — zero config works. The
branchmark DSL block lets you override what you need:
branchmark {
ribbonColor.set("#D32F2F")
fallbackRibbonText.set("DEBUG")
// additive emoji overrides — use put, not set
emojiByPrefix.put("spike", "🧪")
}
| Property | Type | Default | Description |
|---|---|---|---|
buildType | Property<String> | "debug" | Build type whose icon is stamped. |
densities | ListProperty<String> | hdpi…xxxhdpi | Density buckets to generate. |
launcherIconName | Property<String> | "ic_launcher" | Existing adaptive icon to read. |
foregroundResourceName | Property<String> | "ic_launcher_foreground_debug" | Name of the generated stamped foreground. |
ribbonColor | Property<String> | "#D32F2F" | Ribbon fill, #RRGGBB or #AARRGGBB. |
ribbonTextColor | Property<String> | "#FFFFFF" | Ribbon text color. |
defaultEmoji | Property<String> | "🌿" | Emoji for unknown types. |
fallbackRibbonText | Property<String> | "DEBUG" | Ribbon text for undetectable branches. |
branchOverride | Property<String> | unset | Force a branch (highest precedence). |
emojiByPrefix | MapProperty<String,String> | built-in map | Type → emoji overrides. Use .put/.putAll to add. |
Prefix emoji map
These branch types ship with a bundled color glyph out of the box. Any
other type falls back to defaultEmoji (🌿).
| Type | Emoji | Type | Emoji |
|---|---|---|---|
feat | ✨ | docs | 📝 |
fix | 🔧 | test | 🧪 |
bug | 🐛 | perf | ⚡ |
hotfix | 🚑 | ci | 🔁 |
chore | 🧹 | claude | 🤖 |
refactor | ♻️ | unknown | 🌿 |
Custom emoji
emojiByPrefix.put("spike", "🧪") adds a mapping. Emoji
outside the bundled set render via a best-effort system font, which
may appear monochrome on some build machines.
CI setup
CI checkouts are often detached or shallow, so git can't
report a branch. Pass it explicitly and the ribbon stays accurate:
- run: ./gradlew assembleDebug -PgitBranch=${{ github.head_ref || github.ref_name }}
Without it, CI debug builds simply fall back to the static "DEBUG" ribbon — the build never fails.
Caveats
Two intentional limitations, surfaced upfront.
Adaptive foreground only
branchmark regenerates the adaptive-icon foreground and its
anydpi-v26 XML. On minSdk < 26 devices, the
legacy square mipmap-<dpi>/ic_launcher.png is shown
unstamped — the banner appears only where adaptive icons are supported.
Emoji coverage
The built-in branch types and the default emoji ship as bundled Twemoji SVGs. A custom emoji outside that set renders through a system font as a best-effort fallback, which may be monochrome or a missing-glyph box on a machine without an emoji font.
Consumer owns the rest of the icon
branchmark never changes the adaptive background, the release icon, or does any runtime (in-app) icon switching. It only paints the debug banner.