1import com.android.build.api.variant.AndroidComponentsExtension 2import org.apache.tools.ant.taskdefs.condition.Os 3import org.gradle.api.Plugin 4import org.gradle.api.Project 5import org.w3c.dom.Document 6import org.w3c.dom.Element 7 8import javax.xml.parsers.DocumentBuilder 9import javax.xml.parsers.DocumentBuilderFactory 10import javax.xml.transform.TransformerFactory 11import javax.xml.transform.dom.DOMSource 12import javax.xml.transform.stream.StreamResult 13 14/** 15 * Utility plugin to fix some common resource issues with gradle: 16 * - add support for androidprv attributes 17 */ 18class ResourceFixerPlugin implements Plugin<Project> { 19 20 @Override 21 void apply(Project project) { 22 23 def extension = project.getExtensions().getByType(AndroidComponentsExtension.class); 24 def allVariants = [] 25 26 def androidTop = project.extensions.extraProperties.get("ANDROID_TOP"); 27 String buildTools = project.extensions.extraProperties.get("BUILD_TOOLS_VERSION") 28 buildTools = buildTools.replace(' ', '-') 29 def aapt = "$androidTop/out/gradle/MockSdk/build-tools/$buildTools/aapt2" 30 31 // Store all variant names 32 extension.onVariants(extension.selector().all(), variant -> { 33 allVariants.add(variant.name) 34 allVariants.add("${variant.name}AndroidTest") 35 allVariants.add("${variant.name}UnitTest") 36 }) 37 38 // After the project is evaluated, update the mergeResource task 39 project.afterEvaluate { 40 allVariants.forEach(variant -> { 41 def taskName = "merge${variant.capitalize()}Resources"; 42 def mergeTask = project.tasks.findByName(taskName); 43 if (mergeTask == null) { 44 System.out.println("Task not found " + taskName); 45 return 46 } 47 mergeTask.doLast { 48 processResources( 49 new File(project.buildDir, "intermediates/incremental/${variant}/${taskName}/merged.dir"), 50 new File(project.buildDir, "intermediates/merged_res/${variant}"), 51 new File(aapt)) 52 } 53 }) 54 } 55 } 56 57 void processResources(File xmlDir, File outputDir, File aapt) { 58 for (File values: xmlDir.listFiles()) { 59 if (values.getName().startsWith("values") && values.isDirectory()) { 60 for (File xml : values.listFiles()) { 61 if (xml.isFile() && xml.getName().endsWith(".xml") 62 && xml.getText().contains("androidprv:")) { 63 processAndroidPrv(xml, outputDir, aapt); 64 } 65 } 66 } 67 } 68 } 69 70 private void processAndroidPrv(File xmlFile, File outputDir, File aapt) { 71 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 72 DocumentBuilder db = dbf.newDocumentBuilder(); 73 Document doc = db.parse(xmlFile); 74 75 Element root = doc.getDocumentElement(); 76 if (root.hasAttribute("xmlns:androidprv")) { 77 // This file is already processed 78 System.out.println("Skipping " + xmlFile.absolutePath); 79 return 80 } 81 root.setAttribute("xmlns:androidprv", "http://schemas.android.com/apk/prv/res/android"); 82 83 // Update the file 84 TransformerFactory.newInstance().newTransformer() 85 .transform(new DOMSource(doc), new StreamResult(xmlFile)) 86 87 // recompile 88 String command = aapt.getAbsolutePath() + 89 " compile " + 90 xmlFile.getAbsolutePath() + 91 " -o " + 92 outputDir.getAbsolutePath() 93 def proc = command.execute() 94 def sout = new StringBuilder(), serr = new StringBuilder() 95 proc.consumeProcessOutput(sout, serr) 96 proc.waitForOrKill(5000) 97 System.out.println("Processed " + xmlFile.absolutePath + " " + sout + " " + serr); 98 } 99} 100