Skip to content

Instantly share code, notes, and snippets.

@droidchef
Last active April 5, 2022 23:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save droidchef/64c249db574836046d0a11dee96c9865 to your computer and use it in GitHub Desktop.
Save droidchef/64c249db574836046d0a11dee96c9865 to your computer and use it in GitHub Desktop.
Transform Plugin to add functionality to a Fragment class
import com.android.SdkConstants
import com.android.build.api.transform.*
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.internal.plugins.AppPlugin
import com.android.build.gradle.internal.plugins.LibraryPlugin
import javassist.ClassPool
import org.apache.commons.io.FileUtils
import org.apache.commons.io.IOUtils
import org.gradle.api.Plugin
import org.gradle.api.Project
import java.io.File
import java.io.FileOutputStream
import java.util.jar.JarEntry
import java.util.jar.JarFile
import java.util.jar.JarOutputStream
class LingoTransformPlugin: Plugin<Project> {
override fun apply(project: Project) {
val isAndroid = project.plugins.hasPlugin(AppPlugin::class.java)
|| project.plugins.hasPlugin(LibraryPlugin::class.java)
if( !isAndroid) {
throw error("'com.android.application' or 'com.android.library' plugin required.")
}
val daggerTrackExtension = project.extensions.create("lingoTransform", LingoTransformExtension::class.java)
val android = project.extensions.findByName("android") as BaseExtension
android.registerTransform(LingoTransform(project, android, daggerTrackExtension))
}
open class LingoTransformExtension {
var applyFor: Array<String> = emptyArray()
}
}
class LingoTransform(
private val project: Project,
private val android: BaseExtension,
private val lingoTransformExtension: LingoTransformPlugin.LingoTransformExtension
): Transform() {
override fun getName(): String {
return "LingoTransform"
}
override fun getInputTypes(): Set<QualifiedContent.DefaultContentType> {
return setOf(QualifiedContent.DefaultContentType.CLASSES)
}
override fun getScopes(): MutableSet<in QualifiedContent.Scope> {
return mutableSetOf(QualifiedContent.Scope.EXTERNAL_LIBRARIES)
}
override fun isIncremental(): Boolean {
return true
}
override fun transform(transformInvocation: TransformInvocation) {
transformInvocation.inputs.forEach { transformInput ->
transformInput.jarInputs.forEach {
val jarName = it.name
val src = it.file
val dest = transformInvocation.outputProvider.getContentLocation(
jarName, it.contentTypes, it.scopes, Format.JAR
)
if (src.toString().contains("fragment-1.3.6")) {
val androidJar = "${android.sdkDirectory.absolutePath}/platforms/${android.compileSdkVersion}/android.jar"
val pool = ClassPool()
pool.insertClassPath(src.absolutePath)
pool.insertClassPath(androidJar)
val fragmentClass = pool.get("androidx.fragment.app.Fragment")
val methods = fragmentClass.getDeclaredMethods("getString")
methods.forEach { method ->
method.insertAfter("java.lang.System.out.println(\"THIS IS WHERE THE MAGIC HAPPENS\");")
}
val byteCode = fragmentClass.toBytecode()
val ip = JarFile(src)
val op = JarOutputStream(FileOutputStream(dest))
for (entry in ip.entries()) {
if (!entry.name.equals("androidx/fragment/app/Fragment.class")) {
val s = ip.getInputStream(entry)
op.putNextEntry(JarEntry(entry.name))
IOUtils.copy(s, op)
s.close()
}
}
op.putNextEntry(JarEntry("androidx/fragment/app/Fragment.class"))
op.write(byteCode)
op.close()
} else {
FileUtils.copyFile(src, dest)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment