๐ŸŽจ ThemeBadgedIcons

An LSPosed module that makes your themed (monochrome) icons apply to badged shortcut icons too โ€” without removing the badge.

Platform Framework minSdk License Type


โœจ What it does

On modern Android (12+, reworked on 16), the launcher skips icon-theming for badged shortcut icons โ€” contact shortcuts, cloned / work-profile icons, and app-shortcut icons. The result: those icons stay full-color while everything else on your home screen is nicely themed, and only the badge picks up the theme.

ThemeBadgedIcons fixes that. It removes the launcher's "don't theme this, it's a raw file drawable" signal, so badged icons get run through the normal themed path just like every other icon.

Important

This module does NOT remove the badge. The badge stays exactly where it is โ€” the underlying icon simply becomes themed to match the rest of your set.

Before After
Before: badged icons unthemed, full color After: badged icons themed
Badged icons stay full color, unthemed Badged icons are themed (badge kept)

๐Ÿ” How it works (verified from the launcher's own bytecode)

Disassembled from the device's real launcher (Launcher3QuickStep.apk, Android 16 / SDK 36):

  1. When building a shortcut icon, the launcher calls:

    IconOptions.setSourceHint(SourceHint)

    SourceHint carries the original app's ComponentKey and an isFileDrawable flag.

  2. MonoIconThemeController.createThemedBitmap(..., SourceHint) reads that flag:

    if-eqz p4, :cond_2c                        # if SourceHint == null -> skip
    invoke-virtual {p4}, SourceHint;->isFileDrawable()Z
    move-result v3                             # v3 = isFileDrawable
    :cond_2c
    move v9, v3
    # getMonochromeDrawable(..., isFileDrawable)   isFileDrawable == true => SKIP theming
  3. Badged icons are raw file / bitmap drawables โ†’ isFileDrawable == true โ†’ the theme controller bails out of the monochrome/themed path.

The hook

Null the argument to IconOptions.setSourceHint(...):

findAndHookMethod IconOptions.setSourceHint(SourceHint) {
    before { param.args[0] = null; }   // no SourceHint => isFileDrawable defaults to false
}

With no SourceHint, isFileDrawable defaults to false, so the launcher themes the icon normally. The badge is drawn separately, afterwards, so it's untouched.

All hooks are scoped to the launcher process only and are pure runtime method hooks โ€” no overlays, no resource edits โ€” so this coexists cleanly with a custom ROM's built-in Iconify/overlay theming (different layer entirely).


๐Ÿ“ฆ Install & activate

  1. Install ThemeBadgedIcons-release.apk.
  2. Open LSPosed โ†’ Modules โ†’ ThemeBadgedIcons โ†’ enable.
  3. Scope it to your launcher (e.g. com.android.launcher3).
  4. Reboot your device.

Warning

๐Ÿ” After reboot: refresh existing badged icons

Icons the launcher already cached won't change automatically โ€” you need to rebuild them once.

โœ… Easiest way (recommended)

Go to Settings โ†’ Wallpaper & style โ†’ Themed icons and toggle it off, then on. This rebuilds every icon through the hooked path at once โ€” no need to touch your layout.

Alternative (per-icon)

For a specific badged shortcut (contacts, app shortcuts, cloned/work icons), remove it from your home screen, then add it again โ€” the freshly-created shortcut is built through the hooked path and comes out themed.

(Clearing the launcher's storage/cache + rebooting also works, but that wipes your layout โ€” use the toggle instead.)

  1. Verify it engaged:
    adb logcat -d | grep ThemeBadgedIcons
    # [ThemeBadgedIcons] Hooked com.android.launcher3.icons.BaseIconFactory$IconOptions.setSourceHint (force-theme badged icons)

๐Ÿ› ๏ธ Build from source

Requirements: Android SDK (compileSdk 34), JDK 17, and the Xposed API jar at app/libs/api-82.jar.

./gradlew assembleRelease
# APK -> app/build/outputs/apk/release/
SDK-tools build path (used to produce the shipped APK, no Gradle)
javac  -> compile ThemeHook.java + BadgeThemer.java (cp android.jar:api-82.jar)
d8     -> classes.dex
aapt2  -> compile res + link (manifest + assets + resources.arsc)
zip    -> add classes.dex
zipalign -p 4
apksigner sign  (debug keystore)

๐Ÿงฉ Compatibility & tuning

  • Built and confirmed on Android 16 / SDK 36, AOSP/LineageOS com.android.launcher3.
  • The scope list in app/src/main/res/values/xposed_scope.xml covers common launchers; add your launcher's package if it's missing.
  • If logcat shows setSourceHint NOT FOUND, your launcher uses a differently-named IconOptions class โ€” open an issue with the log and it can be added.

๐Ÿ“ Project structure

ThemeBadgedIcons/
โ”œโ”€โ”€ build.gradle ยท settings.gradle ยท gradle.properties
โ””โ”€โ”€ app/
    โ”œโ”€โ”€ build.gradle
    โ”œโ”€โ”€ libs/api-82.jar
    โ””โ”€โ”€ src/main/
        โ”œโ”€โ”€ AndroidManifest.xml            # xposedmodule meta + scope (inside <application>)
        โ”œโ”€โ”€ assets/xposed_init             # idali.themebadgedicons.ThemeHook
        โ”œโ”€โ”€ res/values/xposed_scope.xml    # launcher packages
        โ””โ”€โ”€ java/idali/themebadgedicons/
            โ”œโ”€โ”€ ThemeHook.java             # scope gate + entry point
            โ””โ”€โ”€ BadgeThemer.java           # the setSourceHint hook

๐Ÿ“œ History

Originally prototyped as NoShortcutBadge with the intent to remove the source-app badge. It turned out that nulling SourceHint doesn't remove the badge at all โ€” it makes badged icons get themed, which was the real goal. So it was renamed to match what it actually does.

๐Ÿ“„ License

MIT ยฉ daliboss

Releases

1.0

Stable

7/13/2026, 3:00:19 AM

First release.

Themes badged shortcut icons (contacts, cloned/work-profile, app shortcuts) by nulling IconOptions.setSourceHint so they take the launcher's themed icon path. Does not remove the badge.

Verified on Android 16 / SDK 36 (com.android.launcher3).

After enabling + reboot: remove already-placed badged icons from your home screen and re-add them so they rebuild through the themed path.

Assets

1