Companion code for Bytecode Surgery: Modifying Third-Party Libraries in Android with ASM.
It's a runnable, minimal reproduction of the article's example: an ASM-based AGP Instrumentation
plugin that rewrites calls to isSystemInDarkTheme() at build time, forcing them to return
false, without touching the source of the library that calls it.
build-logic— the actual plugin from the article:LightThemeVisitor,LightThemeVisitorFactory, andLightThemePlugin, registered as the Gradle pluginio.github.lucf15.bytecodesurgery.light-theme.onboarding-lib— stands in for a third-party UI library you depend on but can't edit. Its one screen callsisSystemInDarkTheme()and renders a different background depending on the result. Nothing else in this project calls that function.app— a minimal app that only depends ononboarding-liband applies the plugin. It never referencesisSystemInDarkTheme()itself.
Only app/build.gradle.kts applies the plugin, with InstrumentationScope.ALL. That's enough to
rewrite onboarding-lib's compiled bytecode during the app's build, since from Gradle's point of
view a project dependency is instrumented the same way an external library would be.
./gradlew :app:installDebug
Or open the project in Android Studio and run the app configuration.
Toggle your device's system Dark Theme setting on or off, reinstall, and the screen will always read:
isSystemInDarkTheme() reports: false
To see the effect directly instead of trusting the screen text, decompile the built APK and look
at OnboardingScreenKt:
./gradlew :app:assembleDebug
jadx -d /tmp/jadx-out app/build/outputs/apk/debug/app-debug.apk
cat /tmp/jadx-out/sources/io/github/lucf15/bytecodesurgery/onboarding/OnboardingScreenKt.java
There's no call to isSystemInDarkTheme() in the decompiled output, only a hardcoded 0
constant where the call used to be, which is exactly what LightThemeVisitor does: pop the
call's arguments and push ICONST_0 instead of invoking it.
This targets androidx.compose.foundation.DarkThemeKt.isSystemInDarkTheme, part of an
Apache 2.0-licensed library, for interoperability with the app that depends on it. See the
"A Word of Caution" section of the article before pointing this technique at code you don't
own under a different license.