plugins { id("com.android.application") id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.plugin.serialization") id("org.jetbrains.kotlin.plugin.compose") id("com.google.devtools.ksp") id("io.gitlab.arturbosch.detekt") id("jacoco") } android { namespace = "org.terst.listen" compileSdk = 36 defaultConfig { applicationId = "org.terst.listen" // Deliberately not FeeDreader's minSdk. That value is 36 in its working tree, which // would restrict the app to Android 16 and newer; 29 covers any phone in real use and // is comfortably above Media3's floor of 21. minSdk = 29 targetSdk = 36 versionCode = 1 versionName = "1.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { debug { enableUnitTestCoverage = true } release { isMinifyEnabled = false } } buildFeatures { compose = true } compileOptions { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 } kotlinOptions { jvmTarget = "11" } testOptions { unitTests { // Room's and Media3's Android stubs otherwise throw on every call from a JVM test. isReturnDefaultValues = true } } } dependencies { val composeBom = platform("androidx.compose:compose-bom:2026.06.01") implementation(composeBom) androidTestImplementation(composeBom) implementation("androidx.core:core-ktx:1.17.0") implementation("androidx.activity:activity-compose:1.11.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.4") implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4") implementation("androidx.navigation:navigation-compose:2.9.5") implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") implementation("androidx.compose.material:material-icons-extended") // Media3 is the whole playback stack: ExoPlayer for decode and speed control, // MediaSessionService for lock-screen and Bluetooth transport, and its DownloadManager, // which shares a cache with playback so a partly-downloaded episode streams the remainder // instead of failing. val media3 = "1.8.0" implementation("androidx.media3:media3-exoplayer:$media3") implementation("androidx.media3:media3-session:$media3") implementation("androidx.media3:media3-ui:$media3") implementation("androidx.media3:media3-datasource-okhttp:$media3") implementation("androidx.room:room-runtime:2.8.4") implementation("androidx.room:room-ktx:2.8.4") ksp("androidx.room:room-compiler:2.8.4") implementation("androidx.work:work-runtime-ktx:2.10.5") implementation("androidx.datastore:datastore-preferences:1.1.7") implementation("com.squareup.okhttp3:okhttp:4.12.0") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0") testImplementation("junit:junit:4.13.2") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0") testImplementation("androidx.room:room-testing:2.8.4") // The JVM has no XmlPullParser implementation; the feed parser needs a real one under test. testImplementation("net.sf.kxml:kxml2:2.3.0") testImplementation("xmlpull:xmlpull:1.1.3.1") testImplementation("com.squareup.okhttp3:mockwebserver:4.12.0") androidTestImplementation("androidx.test.ext:junit:1.3.0") androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0") androidTestImplementation("androidx.compose.ui:ui-test-junit4") androidTestImplementation("androidx.room:room-testing:2.8.4") androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0") debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-test-manifest") } detekt { buildUponDefaultConfig = true config.setFrom(files("$rootDir/config/detekt/detekt.yml")) baseline = file("$rootDir/config/detekt/baseline.xml") } jacoco { toolVersion = "0.8.12" } tasks.register("jacocoTestReport") { dependsOn("testDebugUnitTest") reports { xml.required.set(true) html.required.set(true) } // ui/** is excluded for the same reason as FeeDreader: Compose screens are impractical to // unit-test for coverage and including them makes the metric meaningless. val kotlinDebugTree = fileTree("${layout.buildDirectory.get()}/tmp/kotlin-classes/debug") { exclude("**/ui/**", "**/R.class", "**/R$*.class", "**/BuildConfig.*", "**/*_Impl*") } val javaDebugTree = fileTree("${layout.buildDirectory.get()}/intermediates/javac/debug") { exclude("**/ui/**", "**/R.class", "**/R$*.class", "**/BuildConfig.*", "**/*_Impl*") } classDirectories.setFrom(files(kotlinDebugTree, javaDebugTree)) sourceDirectories.setFrom(files("src/main/java")) executionData.setFrom(fileTree(layout.buildDirectory) { include("**/*.exec") }) }