blob: f233da4fa40c4b24bcba2fbb0da0182789e06826 [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")
Tommy Webb636f2252023-01-27 13:33:42 -050057 implementation("androidx.exifinterface:exifinterface:1.3.6")
LuK13379c4638c2022-09-05 13:37:03 +020058 implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
LuK1337afc94a32022-09-05 23:30:33 +020059 implementation("androidx.preference:preference:1.2.0")
LuK1337a0548a12023-01-25 19:51:14 +010060 implementation("com.google.android.material:material:1.9.0-alpha01")
LuK13379c4638c2022-09-05 13:37:03 +020061
62 // CameraX core library using the camera2 implementation
LuK1337a28ad622023-02-22 20:29:48 +010063 val cameraxVersion = "1.3.0-alpha04"
LuK13379c4638c2022-09-05 13:37:03 +020064 // The following line is optional, as the core library is included indirectly by camera-camera2
65 implementation("androidx.camera:camera-core:${cameraxVersion}")
66 implementation("androidx.camera:camera-camera2:${cameraxVersion}")
67 // If you want to additionally use the CameraX Lifecycle library
68 implementation("androidx.camera:camera-lifecycle:${cameraxVersion}")
69 // If you want to additionally use the CameraX VideoCapture library
70 implementation("androidx.camera:camera-video:${cameraxVersion}")
71 // If you want to additionally use the CameraX View class
72 implementation("androidx.camera:camera-view:${cameraxVersion}")
73 // If you want to additionally use the CameraX Extensions library
74 implementation("androidx.camera:camera-extensions:${cameraxVersion}")
75
Sebastiano Barezzi6b71eb52022-11-10 00:05:27 +010076 // Media3
77 val media3Version = "1.0.0-beta02"
78 // For media playback using ExoPlayer
79 implementation("androidx.media3:media3-exoplayer:$media3Version")
80 // For building media playback UIs
81 implementation("androidx.media3:media3-ui:$media3Version")
82
LuK13379c4638c2022-09-05 13:37:03 +020083 // ZXing
84 implementation("com.google.zxing:core:3.5.0")
85
86 // Coil
LuK13375c420fa2022-10-10 13:22:45 +020087 implementation("io.coil-kt:coil:2.2.2")
88 implementation("io.coil-kt:coil-video:2.2.2")
LuK13379c4638c2022-09-05 13:37:03 +020089}
LuK133726b79022022-09-05 23:32:37 +020090
91tasks.register("generateBp") {
92 val project = project(":app")
93 val configuration = project.configurations["debugRuntimeClasspath"]
94
95 val libsBase = File("${project.projectDir.absolutePath}/libs")
96 libsBase.deleteRecursively()
97
98 val moduleString = { it: ModuleVersionIdentifier -> "${it.group}:${it.name}:${it.version}" }
99 val modulePath =
100 { it: ModuleVersionIdentifier -> "${it.group.replace(".", "/")}/${it.name}/${it.version}" }
101
102 val spaces = { it: Int ->
103 var ret = ""
104 for (i in it downTo 1) {
105 ret += ' '
106 }
107 ret
108 }
109
110 val moduleName = { it: Any ->
111 when (it) {
112 is ModuleVersionIdentifier -> {
113 "${rootProject.name}_${it.group}_${it.name}"
114 }
115 is String -> {
116 if (it.contains(":")) {
117 val (group, artifactId) = it.split(":")
118 "${rootProject.name}_${group}_${artifactId}"
119 } else {
120 "${rootProject.name}_${it}"
121 }
122 }
123 else -> {
124 throw Exception("Invalid `it` type")
125 }
126 }
127 }
128
129 val moduleNameAosp = { it: String ->
130 when (it) {
131 "androidx.constraintlayout:constraintlayout" -> "androidx-constraintlayout_constraintlayout"
132 "com.google.auto.value:auto-value-annotations" -> "auto_value_annotations"
Sebastiano Barezzi6b71eb52022-11-10 00:05:27 +0100133 "com.google.guava:guava" -> "guava"
LuK133726b79022022-09-05 23:32:37 +0200134 "com.google.guava:listenablefuture" -> "guava"
135 "org.jetbrains.kotlin:kotlin-stdlib" -> "kotlin-stdlib"
136 "org.jetbrains.kotlin:kotlin-stdlib-jdk8" -> "kotlin-stdlib-jdk8"
137 "org.jetbrains.kotlinx:kotlinx-coroutines-android" -> "kotlinx-coroutines-android"
138 else -> it.replace(":", "_")
139 }
140 }
141
142 val isAvailableInAosp = { group: String, artifactId: String ->
143 when {
144 group.startsWith("androidx") -> {
Sebastiano Barezzi6b71eb52022-11-10 00:05:27 +0100145 // We provide our own androidx.{camera,media3} & lifecycle-common
146 !group.startsWith("androidx.camera") &&
147 !group.startsWith("androidx.media3") &&
148 artifactId != "lifecycle-common"
LuK133726b79022022-09-05 23:32:37 +0200149 }
LuK13375c420fa2022-10-10 13:22:45 +0200150 group.startsWith("org.jetbrains") -> true
LuK133726b79022022-09-05 23:32:37 +0200151 group == "com.google.auto.value" -> true
152 group == "com.google.guava" -> true
153 group == "junit" -> true
154 else -> false
155 }
156 }
157
158 // Update app/Android.bp
159 File("${project.projectDir.absolutePath}/Android.bp").let { file ->
160 // Read dependencies
161 val dependencies = "${spaces(8)}// DO NOT EDIT THIS SECTION MANUALLY\n".plus(
162 configuration.allDependencies.joinToString("\n") {
163 if (isAvailableInAosp(it.group!!, it.name)) {
164 "${spaces(8)}\"${moduleNameAosp("${it.group}:${it.name}")}\","
165 } else {
166 "${spaces(8)}\"${moduleName("${it.group}:${it.name}")}\","
167 }
168 }
169 )
170
171 // Replace existing dependencies with newly generated ones
172 file.writeText(
173 file.readText().replace(
174 "static_libs: \\[.*?\\]".toRegex(RegexOption.DOT_MATCHES_ALL),
175 "static_libs: [%s]".format("\n$dependencies\n${spaces(4)}")
176 )
177 )
178 }
179
180 // Update app/libs
181 configuration.resolvedConfiguration.resolvedArtifacts.sortedBy {
182 moduleString(it.moduleVersion.id)
183 }.distinctBy {
184 moduleString(it.moduleVersion.id)
185 }.forEach {
186 val id = it.moduleVersion.id
187
188 // Skip modules that are available in AOSP
189 if (isAvailableInAosp(id.group, it.name)) {
190 return@forEach
191 }
192
193 // Get file path
194 val dirPath = "${libsBase}/${modulePath(id)}"
195 val filePath = "${dirPath}/${it.file.name}"
196
197 // Copy artifact to app/libs
198 it.file.copyTo(File(filePath))
199
200 // Parse dependencies
201 val dependencies =
Sebastiano Barezzifa391d02022-10-31 14:36:20 +0100202 it.file.parentFile.parentFile.walk().filter { file -> file.extension == "pom" }
203 .map { file ->
LuK133726b79022022-09-05 23:32:37 +0200204 val ret = mutableListOf<String>()
205
206 val pom = XmlParser().parse(file)
207 val dependencies = (pom["dependencies"] as NodeList).firstOrNull() as Node?
208
209 dependencies?.children()?.forEach { node ->
210 val dependency = node as Node
211 ret.add(
212 "${
213 (dependency.get("groupId") as NodeList).text()
214 }:${
215 (dependency.get("artifactId") as NodeList).text()
216 }"
217 )
218 }
219
220 ret
Sebastiano Barezzifa391d02022-10-31 14:36:20 +0100221 }.flatten()
LuK133726b79022022-09-05 23:32:37 +0200222
LuK13376c560fa2022-10-31 22:40:11 +0100223 var targetSdkVersion = android.defaultConfig.targetSdk
LuK133726b79022022-09-05 23:32:37 +0200224 var minSdkVersion = 14
225
226 // Extract AndroidManifest.xml for AARs
227 if (it.file.extension == "aar") {
228 copy {
229 from(zipTree(filePath).matching { include("/AndroidManifest.xml") }.singleFile)
230 into(dirPath)
231 }
232
233 val androidManifest = XmlParser().parse(File("${dirPath}/AndroidManifest.xml"))
234
235 val usesSdk = (androidManifest["uses-sdk"] as NodeList).first() as Node
236 targetSdkVersion = (usesSdk.get("@targetSdkVersion") as Int?) ?: targetSdkVersion
237 minSdkVersion = (usesSdk.get("@minSdkVersion") as Int?) ?: minSdkVersion
238 }
239
240 // Write Android.bp
241 File("$libsBase/Android.bp").let { file ->
242 // Add autogenerated header if file is empty
243 if (file.length() == 0L) {
244 file.writeText("// DO NOT EDIT THIS FILE MANUALLY")
245 }
246
247 val formatDeps = { addNoDeps: Boolean ->
248 val deps = dependencies.filter { dep ->
249 when {
250 configuration.resolvedConfiguration.resolvedArtifacts.firstOrNull { artifact ->
251 dep == "${artifact.moduleVersion.id.group}:${artifact.moduleVersion.id.name}"
252 } == null -> {
253 val moduleName = if (addNoDeps) {
254 moduleName(id)
255 } else {
256 "${moduleName(id)}-nodeps"
257 }
258 println("$moduleName: Skipping $dep because it's not in resolvedArtifacts")
259 false
260 }
261 dep == "org.jetbrains.kotlin:kotlin-stdlib-common" -> false
262 else -> true
263 }
LuK133747cafc32023-01-25 23:20:18 +0100264 }.distinct().toMutableList()
LuK133726b79022022-09-05 23:32:37 +0200265
266 if (addNoDeps) {
267 // Add -nodeps dependency for android_library/java_library_static
268 deps.add(0, "${id.group}_${id.name}-nodeps")
269 }
270
271 var ret = ""
272
273 if (deps.isNotEmpty()) {
274 deps.forEach { dep ->
275 ret += if (dep.contains(":")) {
276 val (group, artifactId) = dep.split(":")
277 if (isAvailableInAosp(group, artifactId)) {
278 "\n${spaces(8)}\"${moduleNameAosp(dep)}\","
279 } else {
280 "\n${spaces(8)}\"${moduleName(dep)}\","
281 }
282 } else {
283 "\n${spaces(8)}\"${moduleName(dep)}\","
284 }
285 }
286 ret += "\n${spaces(4)}"
287 }
288
289 ret
290 }
291
292 when (it.extension) {
293 "aar" -> {
294 file.appendText(
295 """
296
297 android_library_import {
298 name: "${moduleName(id)}-nodeps",
299 aars: ["${modulePath(id)}/${it.file.name}"],
300 sdk_version: "$targetSdkVersion",
301 min_sdk_version: "$minSdkVersion",
302 apex_available: [
303 "//apex_available:platform",
304 "//apex_available:anyapex",
305 ],
306 static_libs: [%s],
307 }
308
309 android_library {
310 name: "${moduleName(id)}",
311 sdk_version: "$targetSdkVersion",
312 min_sdk_version: "$minSdkVersion",
313 apex_available: [
314 "//apex_available:platform",
315 "//apex_available:anyapex",
316 ],
317 manifest: "${modulePath(id)}/AndroidManifest.xml",
318 static_libs: [%s],
319 java_version: "1.7",
320 }
321
322 """.trimIndent().format(formatDeps(false), formatDeps(true))
323 )
324 }
325 "jar" -> {
326 file.appendText(
327 """
328
329 java_import {
330 name: "${moduleName(id)}-nodeps",
331 jars: ["${modulePath(id)}/${it.file.name}"],
332 sdk_version: "$targetSdkVersion",
333 min_sdk_version: "$minSdkVersion",
334 apex_available: [
335 "//apex_available:platform",
336 "//apex_available:anyapex",
337 ],
338 }
339
340 java_library_static {
341 name: "${moduleName(id)}",
342 sdk_version: "$targetSdkVersion",
343 min_sdk_version: "$minSdkVersion",
344 apex_available: [
345 "//apex_available:platform",
346 "//apex_available:anyapex",
347 ],
348 static_libs: [%s],
349 java_version: "1.7",
350 }
351
352 """.trimIndent().format(formatDeps(true))
353 )
354 }
355 else -> throw Exception("Unknown file extension: ${it.extension}")
356 }
357 }
358 }
359}