Android 用Gradle Task 插件 在编译之前做点前置准备工作自动生成资源res/java file

前提条件:
现在要做一个sdk,而这个sdk项目要适用于公司所有的App, 然后sdk的主题色要来自外部的app,所以的那个集成的时候我想根据app层的主题配置文件生成sdk所需要的theme主题色,而某个app可能有多个渠道版本,所以对同一个app来说,又分为多个theme主题色,这时候,我想知道并且根据当前运行的flovar在sdk library中生成相应的theme主题色,那么如何知道当前运行的flavor是哪个呢?或者说怎么当前运行的flavor?查阅相关资料,然后关于如何获取当前Flavor,网上千篇一律的都是这个答案:
https://stackoverflow.com/questions/30621183/how-to-get-current-flavor-in-gradle
然后我试了试,别不是网上说的那样,根本无法得到正确的输出,我得到的是这样的答案,一直是null:

然后我想了想,还是只能靠自己,于是我选择了另一条路,我去了解了Gradle的生命周期, 关于Gradle 生命周期及Task全解,感谢下面这个文章博主的干货给了我很大的帮助,
https://www.shangmayuan.com/a/41cac886af394513986270f7.html
经过一天的努力,终于我把上面所提到的Sdk中想要解决的问题完美的达到了我的目标。

Main module:

[app]: build.gradle

apply from: '../mySDK/auto-generating-build-utils.gradle'
dependencies {
   ...
   //generate multiple color files by flavors .
   //android.productFlavors.all { flavor ->
   //     preBuild.dependsOn autoGenerateFileTask(flavor.name)
   // }

  //generate single color file by currently running flavor.
afterEvaluate {
//    android.productFlavors.all { flavor ->
//        def flavorName = "${flavor.name}"
//        def capitalizeTaskPrefix = "${flavor.name}".capitalize()
        def flavorName = "main"
        def capitalizeTaskPrefix = "".capitalize()

        def buildConfigDebugTask = tasks.findByName("pre${capitalizeTaskPrefix}DebugBuild")
        def buildConfigReleaseTask = tasks.findByName("pre${capitalizeTaskPrefix}ReleaseBuild")
        if (buildConfigDebugTask != null) {
            println "chatFlavor:pre${capitalizeTaskPrefix}DebugBuild"
            buildConfigDebugTask.doLast {
                generateChatConfigTask("${flavorName}")
            }
        } else {
            println "chatFlavor:pre${capitalizeTaskPrefix}DebugBuild not found"
        }
        if (buildConfigReleaseTask != null) {
            println "chatFlavor:pre${capitalizeTaskPrefix}ReleaseBuild"
            buildConfigReleaseTask.doLast {
                generateChatConfigTask("${flavorName}")
            }
        } else {
            println "chatFlavor:pre${capitalizeTaskPrefix}ReleaseBuild not found"
        }
//    }
}
...
}

local.properties

## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file should *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=C\:\\android\\sdk
# example: /assets/chat_theme/chatStyle.json
chatThemeAssetsPath=/assets/my_sdk_res/themeStyle.json
auto-generating-build-utils.gradle

import groovy.json.JsonSlurper
import groovy.json.JsonOutput
import groovy.xml.MarkupBuilder

import java.util.regex.Pattern


ext.autoGenerateFileTask = { projectFlavor ->
    def myProjectName = "chatSDK"
    println projectFlavor
    return tasks.create("generateChatThemeTask$projectFlavor", DefaultTask) {
        // style_default.json is parsed in baseUILib and we will only maintain that json as default
        group = "autoGenerateThemeRelated"
        // Retailers' customized colors
        def localProperties = new Properties()
        def dis = null
        try {
            def localFile = new File("$projectDir/src/main/chat_config.properties")
            println "chat_config_path==========>$localFile"
            if (localFile != null && localFile.exists()) {
                dis = localFile.newDataInputStream()
                localProperties.load(dis)
            } else {
                println "chat_config_path==========>localProperties load failure}"
            }
        } catch (Exception e) {
            println "chat_config_path==========>Exception occurred}"
            e.printStackTrace()
        } finally {
            if (dis != null) {
                dis.close()
            }
        }
        // field definitions
        def matchString = "//.*"
        def inputFile
        def jsonFileString
        def patternComment
        def matcherComment
        def newContent
        def json
        def chatThemeAssetsPath = localProperties.getProperty("chatSDK.chatThemeAssetsPath")
        println "chatSDK.chatThemeAssetsPath==>$projectFlavor:$chatThemeAssetsPath"
        if (chatThemeAssetsPath == null || "" == chatThemeAssetsPath || "null".equalsIgnoreCase(chatThemeAssetsPath)) {
            throw new NullPointerException(" 'chatSDK.chatThemeAssetsPath' must be configured in  '$projectDir/src/main/chat_config.properties' example:\n\n chatSDK.chatThemeAssetsPath=/assets/chat_theme/themeStyle.json\n");
        }
        inputFile = new File("$projectDir/src/$projectFlavor$chatThemeAssetsPath")
        println "chatSDK.chatThemeAssetsPathAbsolutely==>$inputFile.absolutePath"
        jsonFileString = inputFile.text
        patternComment = Pattern.compile(matchString)
        matcherComment = patternComment.matcher(jsonFileString)
        newContent = matcherComment.replaceAll("")
        json = new JsonSlurper().parseText(newContent)
        //do generating color xml.
        generateColorXmlTask(json, projectFlavor, myProjectName)
        //do generating entity class.
        generateEntityTask(localProperties, json, projectFlavor, myProjectName)

        // do generating event entity class
        generateEventEntityTask(localProperties, projectFlavor, myProjectName)
    }
}

private void generateColorXmlTask(Object jsonObject, String flavor, String myProjectName) {
    def sw
    def xml
    def file
    // colors.xml for Brand, will generate color file in GoBank flavor
    sw = new StringWriter()
    xml = new MarkupBuilder(sw)
    xml.setDoubleQuotes(true)
    xml.resources() {
        jsonObject.colors.each {
            k, v ->
                def colorV = swapAlphaColor(v)
                if (colorV.contains("#")) {
                    color(name: "${k}", "${colorV}")
                } else {
                    color(name: "${k}", "@color/" + "${colorV}")
                }
        }
    }

    def desPath = "${rootProject.file("$myProjectName").absolutePath}/src/main"
    def fileDirectoryRes = new File("$desPath/res/values")
    if (!fileDirectoryRes.exists()) {
        fileDirectoryRes.mkdirs()
    }
    file = new File("$desPath/res/values/generated_chat_colors.xml")
    file.write(sw.toString())
}

private String swapAlphaColor(String color) {
    if (color.length() == 9 && color.contains("#")) {
        def swapStringColor = color.substring(1)
        return swapSubstrings(swapStringColor, 7)
    } else {
        return color
    }
}

private String swapSubstrings(String colorValue, Integer index) {
    return "#" + colorValue.substring(index - 1) + colorValue.substring(0, index - 1)
}

private void generateEntityTask(Properties localProperties, Object jsonObject, String projectFlavor, String myProjectName) {
    def desPath = "${rootProject.file("$myProjectName").absolutePath}/src/main/java/com/patrick/chatsdk/theme/model"
    File genDir = new File(desPath)
    if (!genDir.exists()) {
        genDir.mkdirs()
    }
    def autoGenerateTips = "auto-generated code. DO NOT MODIFY!"
    def packageName = "com.patrick.chatsdk.theme.model;"
    def fileHeader = "/**\n*$autoGenerateTips\n*/\npackage $packageName\n\n"
//-------------- auto generating ChatColors.java -------------------
    String chatColorFileName = "ChatColors"
    StringBuilder chatColorContent = new StringBuilder()
    chatColorContent.append("$fileHeader")
    chatColorContent.append("public class $chatColorFileName {\n")
    jsonObject.colors.each {
        k, v ->
            chatColorContent.append("    public final String $k = \"$v\";\n")
    }
    chatColorContent.append("}")
    FileWriter colorFw = new FileWriter(new File("$desPath/${chatColorFileName}.java"))
    colorFw.write(chatColorContent.toString())
    colorFw.flush()

//-------------- auto generating ChatDimens.java -------------------
    String chatDimensFileName = "ChatDimens"
    StringBuilder chatDimensContent = new StringBuilder()
    chatDimensContent.append("$fileHeader")
    chatDimensContent.append("public class $chatDimensFileName {\n")
    jsonObject.dimens.each {
        k, v ->
            chatDimensContent.append("    public final Double $k = $v;\n")
    }
    chatDimensContent.append("}")
    FileWriter dimensFw = new FileWriter(new File("$desPath/${chatDimensFileName}.java"))
    dimensFw.write(chatDimensContent.toString())
    dimensFw.flush()

//-------------- auto generating ChatFonts.java -------------------
    //example:chatSDK.chatFontsDirName=/assets/font/
    //Implementation 1:
//    def assetsDir = "$projectDir/src/$flavor/assets"
//    def defaultFontsDir = new File("$assetsDir/fonts")
//    if (!defaultFontsDir.exists()) {
//        defaultFontsDir = new File("$assetsDir/font")
//    }
//    println "chatSDK.defaultFontsDir==>${defaultFontsDir}[$flavor]"
//    if (!defaultFontsDir.exists()) {
//        def chatFontsDirName = localProperties.getProperty("chatSDK.chatFontsDirName")
//        defaultFontsDir = "$projectDir/src/$flavor$chatFontsDirName"
//        println "chatSDK.chatFontsDirName==>${defaultFontsDir}[$flavor]"
//    }
//    if (defaultFontsDir == null || "" == defaultFontsDir || "null".equalsIgnoreCase(defaultFontsDir)) {
//        throw new NullPointerException(" There is no font files found in ${defaultFontsDir},\n\n either 'chatSDK.chatFontsDirName' must be configured in  '$projectDir/src/main/chat_config.properties' example:\n\n chatSDK.chatFontsDirName=/assets/font/ \n");
//    }
    //Implementation 2:
    def defaultFontsDir = localProperties.getProperty("chatSDK.chatFontsDirName")
    if (defaultFontsDir == null || "" == defaultFontsDir || "null".equalsIgnoreCase(defaultFontsDir)) {
        defaultFontsDir = "font"
    }
    println "chatSDK.defaultFontsDir==>$defaultFontsDir"
    //check first.
    def jsonSlurper = new JsonSlurper()
    def firstFontStyleJson = ""
    jsonObject.fonts.eachWithIndex {
        keyValue, index ->
            if (index == 0) {
                def firstFontEntry = JsonOutput.toJson(keyValue)
                def firstFontValue = jsonSlurper.parseText(firstFontEntry).value
                def firstFontGroovy = JsonOutput.toJson(firstFontValue)
                firstFontStyleJson = jsonSlurper.parseText(firstFontGroovy)
                println "chatSDK.firstFontFileJsonStyle==>${firstFontStyleJson.family}|${firstFontStyleJson.weight}|${firstFontStyleJson.size}"
                return true
            }
            println "chatSDK.testLoopBreak==>index=$index"
    }
    println "\nchatSDK.checkValidFont==>phrase_1"
    def validFormatJson = checkValidFont(projectFlavor, "assets", defaultFontsDir, firstFontStyleJson.family, firstFontStyleJson.weight, "_", 0)
    if ("" == validFormatJson) {
        println "\nchatSDK.checkValidFont==>phrase_2"
        validFormatJson = checkValidFont(projectFlavor, "res", defaultFontsDir, firstFontStyleJson.family, firstFontStyleJson.weight, "_", 0)
    }
    println "chatSDK.checkValidFont==>found in valid font format json '$validFormatJson'\n"
    //start generating.
    def validFormatJsonObject = jsonSlurper.parseText(validFormatJson)
    String chatFontsFileName = "ChatFonts"
    StringBuilder chatFontsContent = new StringBuilder()
    chatFontsContent.append("$fileHeader")
    chatFontsContent.append("public class $chatFontsFileName {\n")
    def fontStyleGroovy
    def fontStyleJson
    def canonicalFamilyChanged
    def canonicalWeightChanged
    def canonicalName
    def isCapital = Boolean.valueOf(validFormatJsonObject.isCapital)
    jsonObject.fonts.each {
        k, v ->
            fontStyleGroovy = JsonOutput.toJson(v)
            fontStyleJson = jsonSlurper.parseText(fontStyleGroovy)
            canonicalFamilyChanged = capitalChange(fontStyleJson.family, isCapital)
            canonicalWeightChanged = capitalChange(fontStyleJson.weight, isCapital)
            canonicalName = "${canonicalFamilyChanged}${validFormatJsonObject.splitSymbol}${canonicalWeightChanged}"
            chatFontsContent.append("    public final ChatThemeFont $k = new ChatThemeFont(\"$defaultFontsDir/${canonicalName}.ttf\", ${fontStyleJson.size}f);\n")
    }
    chatFontsContent.append("}")
    FileWriter fontsFw = new FileWriter(new File("$desPath/${chatFontsFileName}.java"))
    fontsFw.write(chatFontsContent.toString())
    fontsFw.flush()
}

ext.validFormatJson = ""
ext.tempFileName = ""

private String checkValidFont(String projectFlavor, String assetsDir, String fontDir, String family, String weight, String splitSymbol, int index) {
    ext.tempFileName = "${family}$splitSymbol${weight}.ttf"
    def fontCheckingPath = "$projectDir/src/$projectFlavor/$assetsDir/$fontDir/${ext.tempFileName}"
    def validFile = new File(fontCheckingPath)
    println "chatSDK.checkValidFont path==>${validFile.getCanonicalPath()} |exists=${validFile.exists()}"
    if (!validFile.exists()) {//checked: ax_bx.ttf
        if (index == 0) {
            //check: ax-bx.ttf
            checkValidFont(projectFlavor, assetsDir, fontDir, family, weight, "-", 1)
        } else if (index == 1) {
            //check: Ax_Bx.ttf
            checkValidFont("main", assetsDir, fontDir, family, weight, "_", 2)
        } else if (index == 2) {
            //check: Ax-Bx.ttf
            checkValidFont("main", assetsDir, fontDir, family, weight, "-", 3)
        } else if (index == 3) {
            ext.validFormatJson = ""
        }
    } else {
        def isCapitalStr = ext.tempFileName.matches("^[A-Z].*\$")
        if (validFile.getCanonicalPath().contains("${ext.tempFileName}")) {
            println "\nchatSDK.checkValidFont==>path contains=${ext.tempFileName}"
            ext.validFormatJson = "{\"isCapital\":\"${isCapitalStr}\",\"splitSymbol\":\"$splitSymbol\",\"canonicalName\":\"${ext.tempFileName}\"}"
        } else {
            def canonicalFamilyChanged = capitalChange(family, !isCapitalStr)
            def canonicalWeightChanged = capitalChange(weight, !isCapitalStr)
            def canonicalName = "${canonicalFamilyChanged}$splitSymbol${canonicalWeightChanged}"
            println "\nchatSDK.checkValidFont==>path not contains='${ext.tempFileName}' ,the correct one should be '$canonicalName'.ttf"
            ext.validFormatJson = "{\"isCapital\":\"${!isCapitalStr}\",\"splitSymbol\":\"$splitSymbol\",\"canonicalName\":\"$canonicalName\"}"
        }
    }
    return ext.validFormatJson
}

private String capitalChange(String targetStr, boolean upperCase) {
    def targetStrFirst = targetStr.substring(0, 1)
    def targetStrAfter = targetStr.substring(1)
    if (upperCase) {
        return "${targetStrFirst.toUpperCase()}$targetStrAfter"
    } else {
        return "${targetStrFirst.toLowerCase()}$targetStrAfter"
    }
}

private void generateEventEntityTask(Properties localProperties, String projectFlavor, String myProjectName) {

    def chatEventAssetsPath = localProperties.getProperty("chatSDK.chatEventAssetsPath")
    if (chatEventAssetsPath == null || "" == chatEventAssetsPath || "null".equalsIgnoreCase(chatEventAssetsPath)) {
        throw new NullPointerException(" 'chatSDK.chatEventAssetsPath' must be configured in  '$projectDir/src/main/chat_config.properties' example:\n\n chatSDK.chatEventAssetsPath=/assets/chat_theme/chatEvent.json\n");
    }
    def eventInputFile = new File("$projectDir/src/$projectFlavor$chatEventAssetsPath")
    println "chatSDK.chatEventAssetsPathAbsolutely==>$eventInputFile.absolutePath"

    def eventJsonFileString = eventInputFile.text
    def matchString = "//.*"
    def patternComment = Pattern.compile(matchString)
    def matcherComment = patternComment.matcher(eventJsonFileString)
    def eventNewContent = matcherComment.replaceAll("")
    def eventJson = new JsonSlurper().parseText(eventNewContent)

    def desPath = "${rootProject.file("$myProjectName").absolutePath}/src/main/java/com/patrick/chatsdk/event"
    File genDir = new File(desPath)
    if (!genDir.exists()) {
        genDir.mkdirs()
    }

    def autoGenerateTips = "auto-generated code. event part  DO NOT MODIFY!"
    def packageName = "com.patrick.chatsdk.event;"
    def fileHeader = "/**\n*$autoGenerateTips\n*/\npackage $packageName\n\n"

    String chatEventFileName = "ChatEventKey"
    StringBuilder chatEventContent = new StringBuilder()
    chatEventContent.append("$fileHeader")
    chatEventContent.append("public class $chatEventFileName {\n")

    eventJson.eventStateKey.each {
        k, v ->
            chatEventContent.append("    public final String $k = \"$v\";\n")
    }

    eventJson.eventKey.each {
        k, v ->
            chatEventContent.append("    public final String $k = \"$v\";\n")
    }

    chatEventContent.append("}")

    FileWriter eventFw = new FileWriter(new File("$desPath/${chatEventFileName}.java"))
    eventFw.write(chatEventContent.toString())
    eventFw.flush()
}

-----------------------------End-----------------------------

我也是有底线的,感谢您的耐心阅读,欢迎支持与点赞。

你可能感兴趣的:(Android 用Gradle Task 插件 在编译之前做点前置准备工作自动生成资源res/java file)