diff --git a/native/android/app/build.gradle.kts b/native/android/app/build.gradle.kts index d8b1424..cce8efd 100644 --- a/native/android/app/build.gradle.kts +++ b/native/android/app/build.gradle.kts @@ -1,4 +1,7 @@ import java.util.Properties +import java.util.zip.ZipEntry +import java.util.zip.ZipFile +import java.util.zip.ZipOutputStream plugins { id("com.android.application") @@ -20,13 +23,18 @@ android { applicationId = "com.matchlivetv.match_live_tv" minSdk = 24 targetSdk = 36 - versionCode = 29 - versionName = "2.0.8-native" + versionCode = 30 + versionName = "2.0.9-native" val apiBaseUrl = project.findProperty("API_BASE_URL") as String? ?: "https://www.matchlivetv.it" buildConfigField("String", "API_BASE_URL", "\"$apiBaseUrl\"") testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + // Anche in defaultConfig: AGP estrae simboli se le .so non sono già stripped. + ndk { + debugSymbolLevel = "SYMBOL_TABLE" + } } signingConfigs { @@ -47,7 +55,8 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro", ) - // Simboli .so per crash/ANR su Play Console (evita l'avviso "native code senza debug symbols") + // Le .so AndroidX arrivano già stripped: AGP non genera metadata. + // Il task injectNativeDebugSymbolsInReleaseBundle le incorpora nell'AAB. ndk { debugSymbolLevel = "SYMBOL_TABLE" } @@ -112,3 +121,81 @@ dependencies { androidTestImplementation("androidx.test:core:1.6.1") androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0") } + +// Play Console: le .so prebuilt (AndroidX) sono già stripped → AGP non aggiunge +// BUNDLE-METADATA/com.android.tools.build.debugsymbols. Incorporiamo un zip ABI/.so +// nell'intermediary bundle prima della firma. +val injectNativeDebugSymbolsInReleaseBundle by tasks.registering { + description = "Embed native-debug-symbols.zip into the release AAB intermediary" + val mergeNative = tasks.named("mergeReleaseNativeLibs") + val packageBundle = tasks.named("packageReleaseBundle") + dependsOn(mergeNative, packageBundle) + inputs.dir( + layout.buildDirectory.dir( + "intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib", + ), + ) + inputs.file( + layout.buildDirectory.file( + "intermediates/intermediary_bundle/release/packageReleaseBundle/intermediary-bundle.aab", + ), + ) + outputs.file( + layout.buildDirectory.file( + "intermediates/intermediary_bundle/release/packageReleaseBundle/intermediary-bundle.aab", + ), + ) + doLast { + val libsDir = layout.buildDirectory + .dir("intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib") + .get() + .asFile + val aabFile = layout.buildDirectory + .file("intermediates/intermediary_bundle/release/packageReleaseBundle/intermediary-bundle.aab") + .get() + .asFile + require(libsDir.isDirectory) { "Native libs missing: $libsDir" } + require(aabFile.isFile) { "Intermediary AAB missing: $aabFile" } + + val workDir = layout.buildDirectory.dir("tmp/native-debug-symbols-inject").get().asFile + workDir.deleteRecursively() + workDir.mkdirs() + val symbolsZip = workDir.resolve("native-debug-symbols.zip") + ZipOutputStream(symbolsZip.outputStream().buffered()).use { zos -> + libsDir.walkTopDown().filter { it.isFile && it.extension == "so" }.forEach { so -> + val rel = so.relativeTo(libsDir).invariantSeparatorsPath + zos.putNextEntry(ZipEntry(rel)) + so.inputStream().use { it.copyTo(zos) } + zos.closeEntry() + } + } + + val metaPath = "BUNDLE-METADATA/com.android.tools.build.debugsymbols/native-debug-symbols.zip" + val outAab = workDir.resolve("intermediary-with-symbols.aab") + ZipFile(aabFile).use { src -> + ZipOutputStream(outAab.outputStream().buffered()).use { dst -> + val entries = src.entries() + while (entries.hasMoreElements()) { + val entry = entries.nextElement() + if (entry.name == metaPath) continue + val newEntry = ZipEntry(entry.name) + newEntry.time = entry.time + // Deflate all entries when rewriting; STORED copy is fragile across ZipFile. + newEntry.method = ZipEntry.DEFLATED + dst.putNextEntry(newEntry) + src.getInputStream(entry).use { it.copyTo(dst) } + dst.closeEntry() + } + dst.putNextEntry(ZipEntry(metaPath)) + symbolsZip.inputStream().use { it.copyTo(dst) } + dst.closeEntry() + } + } + outAab.copyTo(aabFile, overwrite = true) + logger.lifecycle("Injected $metaPath (${symbolsZip.length()} bytes) into ${aabFile.name}") + } +} + +tasks.matching { it.name == "signReleaseBundle" }.configureEach { + dependsOn(injectNativeDebugSymbolsInReleaseBundle) +}