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