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.

Reads your real icon

No base image or template to provide — branchmark resolves and rasterizes the foreground you already ship.

Color emoji everywhere

Glyphs come from bundled Twemoji SVGs, so the wrench looks the same on macOS, Linux, and CI — no font tofu.

Debug only

The generated icon is wired into the debug variant via the AGP Variant API. Release icons never change.

Always distinct

On main, a detached HEAD, or in CI without a branch, it falls back to a plain "DEBUG" ribbon.

Config-cache safe

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:

settings.gradle.kts
pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
}

Then apply the plugin in your Android application module, after the Android plugin:

build.gradle.kts
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:

Readparse ic_launcher.xml
Rasterizevector → bitmap (VdPreview)
Stampribbon + emoji
Emitdebug res + adaptive XML
  1. Parse src/main/res/mipmap-anydpi-v26/ic_launcher.xml and resolve its <foreground>, <background>, and <monochrome>.
  2. Resolve the foreground to a bitmap — a vector drawable is rasterized with Android's own VdPreview at the 108dp canvas size for the density; a raster foreground is read directly.
  3. 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.
  4. Write mipmap-<dpi>/ic_launcher_foreground_debug.png and a debug mipmap-anydpi-v26/ic_launcher.xml that 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.

BranchType → emojiRibbon
fix/login-crashfix → 🔧LOGIN-CRASH
john/fix/login-crashfix → 🔧LOGIN-CRASH
team/jane/feat/onboardingfeat → ✨ONBOARDING
main— (no emoji)DEBUG (fallback)
detached HEAD / no git— (no emoji)DEBUG (fallback)

The branch name itself is resolved with this precedence:

  1. branchOverride (DSL)
  2. -PgitBranch=… (Gradle property)
  3. GITHUB_HEAD_REF (GitHub Actions PR builds)
  4. GIT_BRANCH (common CI convention)
  5. 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:

build.gradle.kts
branchmark {
    ribbonColor.set("#D32F2F")
    fallbackRibbonText.set("DEBUG")
    // additive emoji overrides — use put, not set
    emojiByPrefix.put("spike", "🧪")
}
PropertyTypeDefaultDescription
buildTypeProperty<String>"debug"Build type whose icon is stamped.
densitiesListProperty<String>hdpi…xxxhdpiDensity buckets to generate.
launcherIconNameProperty<String>"ic_launcher"Existing adaptive icon to read.
foregroundResourceNameProperty<String>"ic_launcher_foreground_debug"Name of the generated stamped foreground.
ribbonColorProperty<String>"#D32F2F"Ribbon fill, #RRGGBB or #AARRGGBB.
ribbonTextColorProperty<String>"#FFFFFF"Ribbon text color.
defaultEmojiProperty<String>"🌿"Emoji for unknown types.
fallbackRibbonTextProperty<String>"DEBUG"Ribbon text for undetectable branches.
branchOverrideProperty<String>unsetForce a branch (highest precedence).
emojiByPrefixMapProperty<String,String>built-in mapType → 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 (🌿).

TypeEmojiTypeEmoji
featdocs📝
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:

GitHub Actions
- 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.