All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
main.com.xml.guard.utils.ProjectExt.kt Maven / Gradle / Ivy
package com.xml.guard.utils
import com.android.build.gradle.BaseExtension
import com.xml.guard.model.ClassInfo
import groovy.util.Node
import groovy.util.NodeList
import groovy.xml.XmlParser
import org.gradle.api.Project
import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency
import java.io.File
/**
* User: ljx
* Date: 2022/3/6
* Time: 22:54
*/
val whiteList by lazy {
listOf(
"layout", "data", "merge", "ViewStub", "include",
"LinearLayout", "RelativeLayout", "FrameLayout", "AbsoluteLayout",
"Button", "TextView", "View", "ImageView", "EditText", "ProgressBar",
"androidx.constraintlayout.widget.ConstraintLayout",
"androidx.core.widget.NestedScrollView",
"androidx.constraintlayout.widget.Group",
"androidx.constraintlayout.widget.Guideline",
"androidx.appcompat.widget.Toolbar",
"com.google.android.material.button.MaterialButton",
"GridLayout", "GridView",
)
}
fun Project.findPackage(): String {
if (AgpVersion.versionCompare("4.2.0") >= 0) {
val namespace = (extensions.getByName("android") as BaseExtension).namespace
if (namespace != null) {
return namespace
}
}
val rootNode = XmlParser(false, false).parse(manifestFile())
return rootNode.attribute("package").toString()
}
// 返回 java/kotlin 代码目录,可能有多个
fun Project.javaDirs(variantName: String): List {
val sourceSet = (extensions.getByName("android") as BaseExtension).sourceSets
val nameSet = mutableSetOf()
nameSet.add("main")
if (isAndroidProject()) {
nameSet.addAll(variantName.splitWords())
}
val javaDirs = mutableListOf()
sourceSet.names.forEach { name ->
if (nameSet.contains(name)) {
sourceSet.getByName(name).java.srcDirs.mapNotNullTo(javaDirs) {
if (it.exists()) it else null
}
}
}
return javaDirs
}
// 返回 aidl 代码目录,可能有多个
fun Project.aidlDirs(variantName: String): List {
val sourceSet = (extensions.getByName("android") as BaseExtension).sourceSets
val nameSet = mutableSetOf()
nameSet.add("main")
if (isAndroidProject()) {
nameSet.addAll(variantName.splitWords())
}
val javaDirs = mutableListOf()
sourceSet.names.forEach { name ->
if (nameSet.contains(name)) {
sourceSet.getByName(name).aidl.srcDirs.mapNotNullTo(javaDirs) {
if (it.exists()) it else null
}
}
}
return javaDirs
}
fun Project.findLayoutDirs(variantName: String) = findXmlDirs(variantName, "layout")
fun Project.findXmlDirs(variantName: String, vararg dirName: String): ArrayList {
return resDirs(variantName).flatMapTo(ArrayList()) { dir ->
dir.listFiles { file, name ->
// 过滤 res 目录下 xxx 目录
file.isDirectory && dirName.any { name.startsWith(it) }
}?.toList() ?: emptyList()
}
}
// 返回 res 目录,可能有多个
fun Project.resDirs(variantName: String): List {
val sourceSet = (extensions.getByName("android") as BaseExtension).sourceSets
val nameSet = mutableSetOf()
nameSet.add("main")
if (isAndroidProject()) {
nameSet.addAll(variantName.splitWords())
}
val resDirs = mutableListOf()
sourceSet.names.forEach { name ->
if (nameSet.contains(name)) {
sourceSet.getByName(name).res.srcDirs.mapNotNullTo(resDirs) {
if (it.exists()) it else null
}
}
}
return resDirs
}
// 返回 manifest 文件目录,有且仅有一个
fun Project.manifestFile(): File {
val sourceSet = (extensions.getByName("android") as BaseExtension).sourceSets
return sourceSet.getByName("main").manifest.srcFile
}
// 查找依赖的 Android Project,也就是子 module,包括间接依赖的子 module
fun Project.findDependencyAndroidProject(
projects: MutableList,
names: List = mutableListOf("api", "implementation", "compileOnly")
) {
names.forEach { name ->
val dependencyProjects = configurations.getByName(name).dependencies
.filterIsInstance()
.filter { it.dependencyProject.isAndroidProject() }
.map { it.dependencyProject }
projects.addAll(dependencyProjects)
dependencyProjects.forEach {
it.findDependencyAndroidProject(projects, names)
}
}
}
fun Project.isAndroidProject() =
plugins.hasPlugin("com.android.application")
|| plugins.hasPlugin("com.android.library")
// 查找 dir 所在的 Project,dir 不存在,返回 null
fun Project.findLocationProject(dir: String, variantName: String): Project? {
val packageName = dir.replace(".", File.separator)
val javaDirs = javaDirs(variantName)
if (javaDirs.any { File(it, packageName).exists() }) {
return this
}
val dependencyProjects = mutableListOf()
findDependencyAndroidProject(dependencyProjects)
dependencyProjects.forEach {
val project = it.findLocationProject(dir, variantName)
if (project != null) return project
}
return null
}
fun findClassByLayoutXml(text: String, packageName: String): List {
val classInfoList = mutableListOf()
val childrenList = XmlParser(false, false).parseText(text).breadthFirst()
val destAttributes =
mutableListOf("tools:context", "app:layout_behavior", "app:layoutManager", "android:name")
for (children in childrenList) {
val childNode = children as? Node ?: continue
destAttributes.forEach { attributeName ->
val attributeValue = childNode.attribute(attributeName)?.toString()
if (!attributeValue.isNullOrBlank()) {
val classname =
if (attributeValue.startsWith(".")) "$packageName$attributeValue" else attributeValue
classInfoList.add(ClassInfo(classname))
}
}
val nodeName = childNode.name().toString()
if (nodeName !in whiteList) {
if (nodeName == "variable" || nodeName == "import") {
val typeValue = childNode.attribute("type").toString()
classInfoList.add(ClassInfo(typeValue, fromImportNode = nodeName == "import"))
} else {
classInfoList.add(ClassInfo(nodeName))
}
}
}
return classInfoList
}
fun findFragmentInfoList(text: String): List {
val classInfoList = mutableListOf()
val rootNode = XmlParser(false, false).parseText(text)
for (children in rootNode.children()) {
val childNode = children as? Node ?: continue
val childName = childNode.name()
if ("fragment" == childName) {
val classPath = childNode.attribute("android:name").toString()
classInfoList.add(ClassInfo(classPath, childNode.children().isNotEmpty()))
}
}
return classInfoList
}
// 在 manifest 文件里,查找四大组件及 Application,返回文件的 package 属性,即包名
fun findClassByManifest(text: String, packageName: String): List {
val classInfoList = mutableListOf()
val rootNode = XmlParser(false, false).parseText(text)
val nodeList = rootNode.get("application") as? NodeList ?: return classInfoList
val applicationNode = nodeList.firstOrNull() as? Node ?: return classInfoList
val application = applicationNode.attribute("android:name")?.toString()
if (application != null) {
val classPath = if (application.startsWith(".")) "$packageName$application" else application
classInfoList.add(ClassInfo(classPath))
}
for (children in applicationNode.children()) {
val childNode = children as? Node ?: continue
val childName = childNode.name()
if ("activity" == childName || "service" == childName ||
"receiver" == childName || "provider" == childName
) {
val name = childNode.attribute("android:name").toString()
val classPath = if (name.startsWith(".")) "$packageName$name" else name
classInfoList.add(ClassInfo(classPath))
}
}
return classInfoList
}