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

main.com.xml.guard.tasks.ResGuardTask.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.model.ResInfo
import com.xml.guard.utils.allDependencyAndroidProjects
import com.xml.guard.utils.convertToCamelCase
import com.xml.guard.utils.getLayoutBindingName
import com.xml.guard.utils.isJava
import com.xml.guard.utils.isKt
import com.xml.guard.utils.isXml
import com.xml.guard.utils.javaDirs
import com.xml.guard.utils.lowercaseFirstChar
import com.xml.guard.utils.manifestFile
import com.xml.guard.utils.replaceWords
import com.xml.guard.utils.replaceWordsByPattern
import com.xml.guard.utils.resDirs
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.tasks.TaskAction
import java.io.File
import javax.inject.Inject

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

    init {
        group = "guard"
    }

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

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

        // 1、仅修改资源名,返回本次修改的文件
        val resFiles = androidProjects.flatMap { project ->
            val resDirs = project.resDirs(variantName)
            project.files(resDirs).asFileTree.files
        }
        val resInfoList = mapping.obfuscateAllRes(resFiles)

        // 2、替换 java/kotlin/xml 文件里引用到的资源
        if (resInfoList.isNotEmpty()) {
            androidProjects.forEach { replaceText(it, resInfoList) }
        }

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

    private fun replaceText(project: Project, resInfoList: List) {
        val dirs = mutableListOf()
        dirs.add(project.manifestFile())
        dirs.addAll(project.resDirs(variantName))
        dirs.addAll(project.javaDirs(variantName))

        project.files(dirs).asFileTree.forEach fileTree@{ file ->
            if (!file.isJava && !file.isKt && !file.isXml) {
                return@fileTree
            }
            var replaceText = file.readText()
            resInfoList.forEach { info ->
                replaceText = replaceText(file, replaceText, info)
            }
            file.writeText(replaceText)
        }
    }

    private fun replaceText(rawFile: File, rawText: String, info: ResInfo): String {
        return when {
            rawFile.isXml -> replaceTextInXml(rawText, info)
            rawFile.isJava || rawFile.isKt -> replaceTextInClass(rawText, info)
            else -> rawText
        }
    }

    private fun replaceTextInClass(rawText: String, info: ResInfo): String {
        val defType = info.defType
        if (defType.isBlank() || defType == ResDefType.VALUES.defType) {
            return rawText
        }

        var oldValue = info.rawName
        var newValue = info.obfuscateName
        var replaceText = rawText.replaceWords(
            oldValue = "R.${defType}.${oldValue}",
            newValue = "R.${defType}.${newValue}"
        )
        when (defType) {
            ResDefType.LAYOUT.defType -> {
                val oldBinding = oldValue.getLayoutBindingName()
                val newBinding = newValue.getLayoutBindingName()
                replaceText = replaceText.replaceWordsByPattern(oldBinding, newBinding)
            }

            ResDefType.ID.defType -> {
                val oldBindingView = oldValue.convertToCamelCase().lowercaseFirstChar()
                val newBindingView = newValue.convertToCamelCase().lowercaseFirstChar()
                replaceText = replaceText.replaceWordsByPattern(oldBindingView, newBindingView)
            }

            ResDefType.STYLEABLE.defType -> {
                val rawPrefix = info.rawNamePrefix
                val obfuscatePrefix = info.obfuscateNamePrefix
                oldValue = "R.${defType}.${rawPrefix}_${oldValue}"
                newValue = "R.${defType}.${obfuscatePrefix}_${newValue}"
                replaceText = replaceText.replaceWordsByPattern(oldValue, newValue)
            }

            ResDefType.STYLE.defType -> {
                if (oldValue.contains(".")) {
                    oldValue = "R.${defType}.${oldValue.replace(".", "_")}"
                    newValue = "R.${defType}.${newValue}"
                    replaceText = replaceText.replaceWordsByPattern(oldValue, newValue)
                }
            }
        }
        return replaceText
    }

    private fun replaceTextInXml(rawText: String, info: ResInfo): String {
        val oldValue = info.rawName
        val newValue = info.obfuscateName

        return when (info.defType) {
            ResDefType.VALUES.defType -> rawText
            ResDefType.ID.defType -> rawText
            else -> rawText.replaceWords(oldValue, newValue)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy