<lambda>null1 import com.android.build.api.variant.AndroidComponentsExtension
2 import com.android.build.api.variant.ComponentIdentity
3 import com.android.build.gradle.tasks.MergeResources
4 import org.gradle.api.Plugin
5 import org.gradle.api.Project
6 import org.gradle.api.artifacts.component.ProjectComponentIdentifier
7 import org.gradle.configurationcache.extensions.capitalized
8 
9 /**
10  * Plugin that verifies that correct order is used for Google and AOSP SystemUI resources. When
11  * there are multiple modules with the same resource, AGP will use the order of the dependencies
12  * declaration.
13  *
14  * See https://developer.android.com/build/dependencies#dependency-order for more details
15  */
16 abstract class VerifySystemUiResourceOrderPlugin : Plugin<Project> {
17 
18     override fun apply(project: Project) {
19         project.extensions.configure(AndroidComponentsExtension::class.java) {
20             onVariants { variant ->
21                 project.afterEvaluate {
22                     val capitalizedVariantName = variant.name.capitalized()
23                     val mergeTask =
24                         project.tasks.named(
25                             "merge${capitalizedVariantName}Resources",
26                             MergeResources::class.java
27                         )
28 
29                     mergeTask.configure { doLast { verifyOrder(variant) } }
30                 }
31             }
32         }
33     }
34 
35     private fun MergeResources.verifyOrder(variant: ComponentIdentity) {
36         val projectPaths = librariesProjectPaths
37 
38         // The lower the index, the higher is the priority
39         val googleResourcesPriority = projectPaths.indexOf(GOOGLE_RESOURCES_PROJECT)
40         val aospResourcesPriority = projectPaths.indexOf(AOSP_RESOURCES_PROJECT)
41 
42         if (variant.isGoogleSpecific()) {
43             if (googleResourcesPriority == INDEX_NOT_FOUND) {
44                 throw IllegalArgumentException(
45                     "Project ${projectPath.get()} doesn't have $GOOGLE_RESOURCES_PROJECT dependency"
46                 )
47             }
48             if (aospResourcesPriority == INDEX_NOT_FOUND) {
49                 throw IllegalArgumentException(
50                     "Project ${projectPath.get()} doesn't have $AOSP_RESOURCES_PROJECT dependency"
51                 )
52             }
53 
54             if (googleResourcesPriority > aospResourcesPriority) {
55                 val prioritiesDescription =
56                     "'$GOOGLE_RESOURCES_PROJECT' index: $googleResourcesPriority, " +
57                         "'$AOSP_RESOURCES_PROJECT' index: $aospResourcesPriority"
58 
59                 throw IllegalArgumentException(
60                     "Invalid resource dependencies order, expected Google resources " +
61                         "($GOOGLE_RESOURCES_PROJECT) to have higher priority " +
62                         "(earlier in the list) than AOSP resources " +
63                         "($AOSP_RESOURCES_PROJECT) for task ${this.name}.\n\n" +
64                         prioritiesDescription
65                 )
66             }
67         }
68     }
69 
70     private val MergeResources.librariesProjectPaths: List<String>
71         get() =
72             resourcesComputer.libraries
73                 .get()
74                 .map { it.id.componentIdentifier }
75                 .filterIsInstance<ProjectComponentIdentifier>()
76                 .map { it.projectPath }
77 
78     private fun ComponentIdentity.isGoogleSpecific(): Boolean =
79         name.contains("google", ignoreCase = true) || name.contains("titan", ignoreCase = true)
80 
81     private companion object {
82         private const val GOOGLE_RESOURCES_PROJECT = ":sysuig-resources"
83         private const val AOSP_RESOURCES_PROJECT = ":SystemUI-res"
84         private const val INDEX_NOT_FOUND = -1
85     }
86 }
87