All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.com.xml.guard.tasks.DeclarationChangeTask.kt Maven / Gradle / Ivy

package com.xml.guard.tasks

import com.xml.guard.data.ResDefType
import com.xml.guard.entensions.GuardExtension
import com.xml.guard.model.MappingParser
import com.xml.guard.utils.allDependencyAndroidProjects
import com.xml.guard.utils.findLayoutDirs
import com.xml.guard.utils.javaDirs
import com.xml.guard.utils.replaceWords
import com.xml.guard.utils.replaceWordsByPattern
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.TaskAction
import java.io.File
import javax.inject.Inject

open class DeclarationChangeTask @Inject constructor(
    private val guardExtension: GuardExtension,
    private val variantName: String,
) : DefaultTask() {

    init {
        group = "guard"
    }

    private val mappingFile by lazy { project.file(MappingParser.MAPPING_DECLARATION) }
    private val mapping by lazy {
        MappingParser.parseDeclaration(
            project,
            guardExtension,
            mappingFile,
            isComparison = true
        )
    }

    @Suppress("RegExpUnnecessaryNonCapturingGroup")
    private val resDefNameRegex by lazy {
        val defTypes = ResDefType.values().joinToString("|") { it.defType }
        val pattern = "R\\.(?:${defTypes})\\.([a-zA-Z0-9_]+)"
        Regex(pattern)
    }

    @TaskAction
    fun execute() {
        val androidProjects = allDependencyAndroidProjects(project)

        // 1、字符串混淆
        val mappingInfoMap = mapping.findAllDeclaration(androidProjects, variantName)

        // 2、替换 Java/kotlin 文件里引用到的声明字符
        if (mappingInfoMap.isNotEmpty()) {
            val methodNameMapping = mapping.methodNameMapping
            androidProjects.forEach { project ->
                project.files(project.javaDirs(variantName)).asFileTree.files.forEach asFileTree@{ file ->
                    // 白名单
                    if (mapping.inClassWhiteList(project, file)) {
                        return@asFileTree
                    }
                    replaceText(file, mappingInfoMap)
                }

                if (methodNameMapping.isNotEmpty()) {
                    project.files(project.findLayoutDirs(variantName)).asFileTree.files.forEach asFileTree@{ file ->
                        var fileText = file.readText()
                        methodNameMapping.forEach { (oldName, newName) ->
                            var oldValue = """
                            android:onClick="$oldName"
                        """.trimIndent()
                            var newValue = """
                            android:onClick="$newName"
                        """.trimIndent()
                            fileText = fileText.replaceWords(oldValue, newValue)

                            oldValue = """
                                .${oldName}(
                            """.trimIndent()
                            newValue = """
                                .${newName}(
                            """.trimIndent()
                            fileText = fileText.replaceWords(oldValue, newValue)
                        }
                        file.writeText(fileText)
                    }
                }
            }
        }

        // 3、混淆映射写出到文件
        mapping.writeMappingToFile(mappingFile)
    }

    private fun replaceText(
        file: File,
        mappingInfoList: Map
    ) {
        val builder = StringBuilder()
        file.forEachLine { line ->
            line.trim().run {
                if (startsWith("package")) {
                    builder.append(line)
                } else {
                    var newLine = line
                    mappingInfoList.forEach { (oldName, newName) ->
                        newLine = newLine.replaceWordsByPattern(oldName, newName)

                        if (newLine != line) {
                            extractResStrings(line).forEach extractResStrings@{ (fullName, defName) ->
                                if (defName != oldName) return@extractResStrings
                                // 若资源相关名称被混淆,需还原;例如:R.id.ivIcon 被混淆为 R.id.xsgf
                                val oldValue = fullName.replace(oldName, newName)
                                newLine = newLine.replaceWordsByPattern(oldValue, fullName)
                            }
                        }
                    }
                    builder.append(newLine)
                }
                builder.append("\n")
            }
        }
        if (builder.isNotEmpty()) {
            builder.deleteCharAt(builder.length - 1)
        }
        file.writeText(builder.toString())
    }

    private fun extractResStrings(input: String): Map {
        val map = mutableMapOf()
        val matches = resDefNameRegex.findAll(input)
        matches.forEach { match ->
            val result = match.groupValues.getOrNull(1)
            if (!result.isNullOrBlank()) {
                map[match.value] = result
            }
        }
        return map
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy