/* * SPDX-FileCopyrightText: 2026 Sulkta-Coop * SPDX-License-Identifier: GPL-3.0-or-later * * :strawApp — thin Android application shell. Day-2: pulls NewPipeExtractor, * Media3, Ktor-style HTTP-via-OkHttp + kotlinx-serialization JSON for the * search → detail → player → SponsorBlock + RYD flow. We keep our deps in * this module, NOT in the shared libs.versions.toml, so upstream NewPipe * stays cleanly mergeable. */ import com.android.build.api.dsl.ApplicationExtension plugins { alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.compose) alias(libs.plugins.jetbrains.kotlinx.serialization) } kotlin { jvmToolchain(21) } configure { compileSdk { version = release(NEWPIPE_VERSION_SDK_COMPILE_MAJOR) { minorApiLevel = NEWPIPE_VERSION_SDK_COMPILE_MINOR } } namespace = STRAW_APPLICATION_ID defaultConfig { applicationId = STRAW_APPLICATION_ID minSdk { version = release(24) } targetSdk { version = release(NEWPIPE_VERSION_SDK_TARGET) } versionCode = STRAW_VERSION_CODE versionName = STRAW_VERSION_NAME resValue("string", "app_name", "Straw") } buildTypes { // R8 enabled on BOTH variants — we publish the debug APK to // fdroid (com.sulkta.straw.debug) per the existing pipeline, // and audit-flagged Log.d strips depended on R8 actually // running on the variant we ship. Keep rules in // strawApp/proguard-rules.pro cover UniFFI + JNA + // kotlinx-serialization companions. debug { isDebuggable = true applicationIdSuffix = ".debug" resValue("string", "app_name", "Straw debug") isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro", ) } release { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro", ) } } compileOptions { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 } buildFeatures { compose = true buildConfig = true resValues = true } packaging { resources { excludes += setOf( "META-INF/README.md", "META-INF/CHANGES", "META-INF/COPYRIGHT", "META-INF/INDEX.LIST", "META-INF/io.netty.versions.properties", ) } } } dependencies { // Compose + AndroidX core implementation(libs.androidx.activity) implementation(libs.androidx.core) implementation(libs.jetbrains.compose.runtime) implementation(libs.jetbrains.compose.foundation) implementation(libs.jetbrains.compose.material3) implementation(libs.jetbrains.compose.ui) implementation("androidx.compose.material:material-icons-extended:1.7.5") // Lifecycle + ViewModel for Compose implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.10.0") implementation("androidx.lifecycle:lifecycle-runtime-compose:2.10.0") // Coroutines implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0") // Image loading implementation(libs.coil.compose) implementation(libs.coil.network.okhttp) // NewPipeExtractor (JVM/Android-only) + its OkHttp dep // libs.newpipe.extractor — REMOVED in Path C-6. Extractor is now strawcore // (Rust + rustypipe via UniFFI). See rust/strawcore/ + the cargoBuild + // uniffiBindgen Gradle tasks below. implementation(libs.squareup.okhttp) // JSON for SponsorBlock + Return YouTube Dislike clients implementation(libs.kotlinx.serialization.json) // Media3 ExoPlayer implementation("androidx.media3:media3-exoplayer:1.4.1") implementation("androidx.media3:media3-exoplayer-dash:1.4.1") implementation("androidx.media3:media3-exoplayer-hls:1.4.1") implementation("androidx.media3:media3-ui:1.4.1") // Media3 session — MediaSessionService for background audio + lock-screen controls. implementation("androidx.media3:media3-session:1.4.1") // Guava ListenableFuture support for awaiting MediaController connect. implementation("androidx.concurrent:concurrent-futures-ktx:1.2.0") // strawcore — Rust YouTube extractor via UniFFI/JNA. Built by the // cargoBuild + uniffiBindgen tasks below; phase U-2+ exposes search / // streamInfo / channelInfo to replace NewPipeExtractor. implementation("net.java.dev.jna:jna:5.14.0@aar") } // ============================================================================= // Phase U-1 / Path-C-2 — Rust core build glue. // // Two tasks chain into the Android build: // cargoBuild — cross-compiles rust/strawcore for the four Android ABIs // via cargo-ndk and drops the .so files in strawApp/src/main/jniLibs/. // uniffiBindgen — generates the Kotlin bindings from the freshly-built lib // into strawApp/src/main/java/uniffi/strawcore/. // // Both depend on: // - cargo + rustup with the four Android targets installed // - cargo-ndk on PATH // - ANDROID_NDK_HOME pointing at an NDK with the right toolchains // All of that lives in the crafting-table container. // ============================================================================= val rustRoot = file("../rust").absolutePath val jniLibsDir = file("src/main/jniLibs").absolutePath val bindingsDir = file("src/main/java").absolutePath val cargoHome: String = System.getenv("CARGO_HOME") ?: "/caches/cargo" val cargoBin: String = "$cargoHome/bin/cargo" val ndkHome: String = System.getenv("ANDROID_NDK_HOME") ?: System.getenv("ANDROID_NDK_ROOT") ?: "/caches/android-sdk/ndk/27.2.12479018" // Honor CARGO_TARGET_DIR if set (we redirect it to /caches on crafting-table // because the container's writable rootfs hits 100% before the cross-compile // for 4 ABIs finishes). Falls back to the default `/target`. val cargoTargetDir: String = System.getenv("CARGO_TARGET_DIR") ?: "$rustRoot/target" val cargoBuild by tasks.registering(Exec::class) { group = "rust" description = "Cross-compile strawcore for all Android ABIs via cargo-ndk." workingDir = file(rustRoot) environment("ANDROID_NDK_HOME", ndkHome) environment("PATH", "$cargoHome/bin:${System.getenv("PATH") ?: ""}") commandLine = listOf( cargoBin, "ndk", "-t", "arm64-v8a", "-t", "armeabi-v7a", "-t", "x86", "-t", "x86_64", "-o", jniLibsDir, "build", "--release", "-p", "strawcore", ) standardOutput = System.out errorOutput = System.err } val cargoBuildHost by tasks.registering(Exec::class) { group = "rust" description = "Build host-arch debug strawcore so bindgen can read its UniFFI metadata." workingDir = file(rustRoot) environment("PATH", "$cargoHome/bin:${System.getenv("PATH") ?: ""}") commandLine = listOf(cargoBin, "build", "-p", "strawcore") standardOutput = System.out errorOutput = System.err } val uniffiBindgen by tasks.registering(Exec::class) { group = "rust" description = "Generate Kotlin bindings for strawcore via uniffi-bindgen." dependsOn(cargoBuildHost) workingDir = file(rustRoot) environment("PATH", "$cargoHome/bin:${System.getenv("PATH") ?: ""}") commandLine = listOf( cargoBin, "run", "--quiet", "--bin", "uniffi-bindgen", "--", "generate", "--library", "$cargoTargetDir/debug/libstrawcore.so", "--crate", "strawcore", "--language", "kotlin", "--no-format", "--out-dir", bindingsDir, ) standardOutput = System.out errorOutput = System.err } // Make sure Android's JNI-libs merge picks up the freshly built .so files, // and Kotlin compilation can resolve the generated bindings. tasks.matching { it.name.startsWith("merge") && it.name.endsWith("JniLibFolders") } .configureEach { dependsOn(cargoBuild) } tasks.matching { it.name.startsWith("compile") && it.name.endsWith("Kotlin") } .configureEach { dependsOn(uniffiBindgen) }