blob: f54e54708fa815be7b4fba46b12a08ab15c48030 [file] [log] [blame]
LuK133726b79022022-09-05 23:32:37 +02001import groovy.util.Node
2import groovy.util.NodeList
3import groovy.xml.XmlParser
4
LuK13379c4638c2022-09-05 13:37:03 +02005plugins {
6 id("com.android.application")
7 id("kotlin-android")
8}
9
10android {
11 compileSdk = 33
12
13 defaultConfig {
14 applicationId = "org.lineageos.aperture"
15 minSdk = 31
16 targetSdk = 33
17 versionCode = 1
18 versionName = "1.0"
19
20 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
21 }
22
23 buildTypes {
24 named("release") {
25 // Enables code shrinking, obfuscation, and optimization.
26 isMinifyEnabled = true
27
28 // Enables resource shrinking.
29 isShrinkResources = true
30
31 // Includes the default ProGuard rules files.
32 setProguardFiles(
33 listOf(
34 getDefaultProguardFile("proguard-android-optimize.txt"),
35 "proguard-rules.pro"
36 )
37 )
38 }
39 }
40
41 compileOptions {
42 sourceCompatibility = JavaVersion.VERSION_1_8
43 targetCompatibility = JavaVersion.VERSION_1_8
44 }
45
46 kotlinOptions {
47 jvmTarget = "1.8"
48 }
49}
50
51dependencies {
52 implementation("androidx.core:core-ktx:1.8.0")
53 implementation("androidx.appcompat:appcompat:1.5.0")
54 implementation("androidx.constraintlayout:constraintlayout:2.1.4")
55 implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
LuK1337afc94a32022-09-05 23:30:33 +020056 implementation("androidx.preference:preference:1.2.0")
LuK13379c4638c2022-09-05 13:37:03 +020057 implementation("com.google.android.material:material:1.8.0-alpha01")
58
59 // CameraX core library using the camera2 implementation
60 val cameraxVersion = "1.2.0-beta02"
61 // The following line is optional, as the core library is included indirectly by camera-camera2
62 implementation("androidx.camera:camera-core:${cameraxVersion}")
63 implementation("androidx.camera:camera-camera2:${cameraxVersion}")
64 // If you want to additionally use the CameraX Lifecycle library
65 implementation("androidx.camera:camera-lifecycle:${cameraxVersion}")
66 // If you want to additionally use the CameraX VideoCapture library
67 implementation("androidx.camera:camera-video:${cameraxVersion}")
68 // If you want to additionally use the CameraX View class
69 implementation("androidx.camera:camera-view:${cameraxVersion}")
70 // If you want to additionally use the CameraX Extensions library
71 implementation("androidx.camera:camera-extensions:${cameraxVersion}")
72
73 // ZXing
74 implementation("com.google.zxing:core:3.5.0")
75
76 // Coil
77 implementation("io.coil-kt:coil:2.2.0")
78 implementation("io.coil-kt:coil-video:2.2.0")
79}
LuK133726b79022022-09-05 23:32:37 +020080
81tasks.register("generateBp") {
82 val project = project(":app")
83 val configuration = project.configurations["debugRuntimeClasspath"]
84
85 val libsBase = File("${project.projectDir.absolutePath}/libs")
86 libsBase.deleteRecursively()
87
88 val moduleString = { it: ModuleVersionIdentifier -> "${it.group}:${it.name}:${it.version}" }
89 val modulePath =
90 { it: ModuleVersionIdentifier -> "${it.group.replace(".", "/")}/${it.name}/${it.version}" }
91
92 val spaces = { it: Int ->
93 var ret = ""
94 for (i in it downTo 1) {
95 ret += ' '
96 }
97 ret
98 }
99
100 val moduleName = { it: Any ->
101 when (it) {
102 is ModuleVersionIdentifier -> {
103 "${rootProject.name}_${it.group}_${it.name}"
104 }
105 is String -> {
106 if (it.contains(":")) {
107 val (group, artifactId) = it.split(":")
108 "${rootProject.name}_${group}_${artifactId}"
109 } else {
110 "${rootProject.name}_${it}"
111 }
112 }
113 else -> {
114 throw Exception("Invalid `it` type")
115 }
116 }
117 }
118
119 val moduleNameAosp = { it: String ->
120 when (it) {
121 "androidx.constraintlayout:constraintlayout" -> "androidx-constraintlayout_constraintlayout"
122 "com.google.auto.value:auto-value-annotations" -> "auto_value_annotations"
123 "com.google.guava:listenablefuture" -> "guava"
124 "org.jetbrains.kotlin:kotlin-stdlib" -> "kotlin-stdlib"
125 "org.jetbrains.kotlin:kotlin-stdlib-jdk8" -> "kotlin-stdlib-jdk8"
126 "org.jetbrains.kotlinx:kotlinx-coroutines-android" -> "kotlinx-coroutines-android"
127 else -> it.replace(":", "_")
128 }
129 }
130
131 val isAvailableInAosp = { group: String, artifactId: String ->
132 when {
133 group.startsWith("androidx") -> {
134 // We provide our own androidx.camera & lifecycle-common
135 !group.startsWith("androidx.camera") && artifactId != "lifecycle-common"
136 }
137 group.startsWith("org.jetbrains") -> {
138 // kotlin-android-extensions-runtime & kotlin-parcelize-runtime aren't in AOSP
139 !artifactId.startsWith("kotlin-android-extensions-runtime") &&
140 !artifactId.startsWith("kotlin-parcelize-runtime")
141 }
142 group == "com.google.auto.value" -> true
143 group == "com.google.guava" -> true
144 group == "junit" -> true
145 else -> false
146 }
147 }
148
149 // Update app/Android.bp
150 File("${project.projectDir.absolutePath}/Android.bp").let { file ->
151 // Read dependencies
152 val dependencies = "${spaces(8)}// DO NOT EDIT THIS SECTION MANUALLY\n".plus(
153 configuration.allDependencies.joinToString("\n") {
154 if (isAvailableInAosp(it.group!!, it.name)) {
155 "${spaces(8)}\"${moduleNameAosp("${it.group}:${it.name}")}\","
156 } else {
157 "${spaces(8)}\"${moduleName("${it.group}:${it.name}")}\","
158 }
159 }
160 )
161
162 // Replace existing dependencies with newly generated ones
163 file.writeText(
164 file.readText().replace(
165 "static_libs: \\[.*?\\]".toRegex(RegexOption.DOT_MATCHES_ALL),
166 "static_libs: [%s]".format("\n$dependencies\n${spaces(4)}")
167 )
168 )
169 }
170
171 // Update app/libs
172 configuration.resolvedConfiguration.resolvedArtifacts.sortedBy {
173 moduleString(it.moduleVersion.id)
174 }.distinctBy {
175 moduleString(it.moduleVersion.id)
176 }.forEach {
177 val id = it.moduleVersion.id
178
179 // Skip modules that are available in AOSP
180 if (isAvailableInAosp(id.group, it.name)) {
181 return@forEach
182 }
183
184 // Get file path
185 val dirPath = "${libsBase}/${modulePath(id)}"
186 val filePath = "${dirPath}/${it.file.name}"
187
188 // Copy artifact to app/libs
189 it.file.copyTo(File(filePath))
190
191 // Parse dependencies
192 val dependencies =
193 it.file.parentFile.parentFile.walk().first { file -> file.extension == "pom" }
194 .let { file ->
195 val ret = mutableListOf<String>()
196
197 val pom = XmlParser().parse(file)
198 val dependencies = (pom["dependencies"] as NodeList).firstOrNull() as Node?
199
200 dependencies?.children()?.forEach { node ->
201 val dependency = node as Node
202 ret.add(
203 "${
204 (dependency.get("groupId") as NodeList).text()
205 }:${
206 (dependency.get("artifactId") as NodeList).text()
207 }"
208 )
209 }
210
211 ret
212 }
213
214 var targetSdkVersion = 31
215 var minSdkVersion = 14
216
217 // Extract AndroidManifest.xml for AARs
218 if (it.file.extension == "aar") {
219 copy {
220 from(zipTree(filePath).matching { include("/AndroidManifest.xml") }.singleFile)
221 into(dirPath)
222 }
223
224 val androidManifest = XmlParser().parse(File("${dirPath}/AndroidManifest.xml"))
225
226 val usesSdk = (androidManifest["uses-sdk"] as NodeList).first() as Node
227 targetSdkVersion = (usesSdk.get("@targetSdkVersion") as Int?) ?: targetSdkVersion
228 minSdkVersion = (usesSdk.get("@minSdkVersion") as Int?) ?: minSdkVersion
229 }
230
231 // Write Android.bp
232 File("$libsBase/Android.bp").let { file ->
233 // Add autogenerated header if file is empty
234 if (file.length() == 0L) {
235 file.writeText("// DO NOT EDIT THIS FILE MANUALLY")
236 }
237
238 val formatDeps = { addNoDeps: Boolean ->
239 val deps = dependencies.filter { dep ->
240 when {
241 configuration.resolvedConfiguration.resolvedArtifacts.firstOrNull { artifact ->
242 dep == "${artifact.moduleVersion.id.group}:${artifact.moduleVersion.id.name}"
243 } == null -> {
244 val moduleName = if (addNoDeps) {
245 moduleName(id)
246 } else {
247 "${moduleName(id)}-nodeps"
248 }
249 println("$moduleName: Skipping $dep because it's not in resolvedArtifacts")
250 false
251 }
252 dep == "org.jetbrains.kotlin:kotlin-stdlib-common" -> false
253 else -> true
254 }
255
256 }.toMutableList()
257
258 if (addNoDeps) {
259 // Add -nodeps dependency for android_library/java_library_static
260 deps.add(0, "${id.group}_${id.name}-nodeps")
261 }
262
263 var ret = ""
264
265 if (deps.isNotEmpty()) {
266 deps.forEach { dep ->
267 ret += if (dep.contains(":")) {
268 val (group, artifactId) = dep.split(":")
269 if (isAvailableInAosp(group, artifactId)) {
270 "\n${spaces(8)}\"${moduleNameAosp(dep)}\","
271 } else {
272 "\n${spaces(8)}\"${moduleName(dep)}\","
273 }
274 } else {
275 "\n${spaces(8)}\"${moduleName(dep)}\","
276 }
277 }
278 ret += "\n${spaces(4)}"
279 }
280
281 ret
282 }
283
284 when (it.extension) {
285 "aar" -> {
286 file.appendText(
287 """
288
289 android_library_import {
290 name: "${moduleName(id)}-nodeps",
291 aars: ["${modulePath(id)}/${it.file.name}"],
292 sdk_version: "$targetSdkVersion",
293 min_sdk_version: "$minSdkVersion",
294 apex_available: [
295 "//apex_available:platform",
296 "//apex_available:anyapex",
297 ],
298 static_libs: [%s],
299 }
300
301 android_library {
302 name: "${moduleName(id)}",
303 sdk_version: "$targetSdkVersion",
304 min_sdk_version: "$minSdkVersion",
305 apex_available: [
306 "//apex_available:platform",
307 "//apex_available:anyapex",
308 ],
309 manifest: "${modulePath(id)}/AndroidManifest.xml",
310 static_libs: [%s],
311 java_version: "1.7",
312 }
313
314 """.trimIndent().format(formatDeps(false), formatDeps(true))
315 )
316 }
317 "jar" -> {
318 file.appendText(
319 """
320
321 java_import {
322 name: "${moduleName(id)}-nodeps",
323 jars: ["${modulePath(id)}/${it.file.name}"],
324 sdk_version: "$targetSdkVersion",
325 min_sdk_version: "$minSdkVersion",
326 apex_available: [
327 "//apex_available:platform",
328 "//apex_available:anyapex",
329 ],
330 }
331
332 java_library_static {
333 name: "${moduleName(id)}",
334 sdk_version: "$targetSdkVersion",
335 min_sdk_version: "$minSdkVersion",
336 apex_available: [
337 "//apex_available:platform",
338 "//apex_available:anyapex",
339 ],
340 static_libs: [%s],
341 java_version: "1.7",
342 }
343
344 """.trimIndent().format(formatDeps(true))
345 )
346 }
347 else -> throw Exception("Unknown file extension: ${it.extension}")
348 }
349 }
350 }
351}