import groovy.json.JsonOutput import groovy.json.JsonSlurper plugins { id("com.android.application") id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") } val apkKeyPassword = System.getenv("APK_KEY_PASSWORD") val apkStorePassword = System.getenv("APK_STORE_PASSWORD") android { namespace = "com.gopeed.gopeed" compileSdk = flutter.compileSdkVersion ndkVersion = flutter.ndkVersion compileOptions { isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = JavaVersion.VERSION_17.toString() } defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId = "com.gopeed.gopeed" // You can update the following values to match your application needs. // For more information, see: https://flutter.dev/to/review-gradle-config. minSdk = flutter.minSdkVersion targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName } signingConfigs { create("release") { keyAlias = "upload" keyPassword = apkKeyPassword storeFile = file("upload-keystore.jks") storePassword = apkStorePassword } } buildTypes { release { signingConfig = if (!apkKeyPassword.isNullOrEmpty() && !apkStorePassword.isNullOrEmpty()) { signingConfigs.getByName("release") } else { signingConfigs.getByName("debug") } } } packaging { jniLibs { // Go and Flutter native libraries are large and compress well. Gopeed is // primarily distributed as APKs, so preserve the smaller legacy layout. useLegacyPackaging = true } } } flutter { source = "../.." } dependencies { // Generated by gomobile from bind/mobile before an Android build. implementation(files("libs/libgopeed.aar")) coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") } // shadcn_flutter 0.0.53 unconditionally declares every Geist font and all // country_flags assets. Android uses platform fonts below and Gopeed does not use // PhoneInput/CountryFlag, so keep those transitive assets out of release APKs. val pruneReleaseFlutterAssets = tasks.register("pruneReleaseFlutterAssets") { dependsOn("compileFlutterBuildRelease") val generatedFlutterAssets = layout.buildDirectory.dir("intermediates/flutter/release/flutter_assets") doLast { val flutterAssets = generatedFlutterAssets.get().asFile if (!flutterAssets.isDirectory) { throw GradleException("Generated Flutter assets not found: $flutterAssets") } val dartSources = fileTree(file("../../lib")) { include("**/*.dart") } val unsupportedUsage = Regex("\\b(CountryFlag|PhoneInput)\\b|Geist(?:Sans|Mono)") val appTheme = file("../../lib/shared/theme/app_theme.dart").canonicalFile val allowedWindowsFontReference = "fontFamily: 'packages/shadcn_flutter/GeistSans'," val usageFile = dartSources.files.firstOrNull { sourceFile -> val source = sourceFile.readText() val guardedSource = if (sourceFile.canonicalFile == appTheme) { source.replaceFirst(allowedWindowsFontReference, "") } else { source } unsupportedUsage.containsMatchIn(guardedSource) } if (usageFile != null) { throw GradleException( "Android release asset pruning is incompatible with ${usageFile.relativeTo(projectDir)}", ) } listOf( flutterAssets.resolve("packages/country_flags/res/si"), flutterAssets.resolve("packages/shadcn_flutter/lib/fonts"), ).forEach { assetDirectory -> if (assetDirectory.exists() && !assetDirectory.deleteRecursively()) { throw GradleException("Unable to prune Flutter assets: $assetDirectory") } } val fontManifest = flutterAssets.resolve("FontManifest.json") if (!fontManifest.isFile) { throw GradleException("Flutter font manifest not found: $fontManifest") } val fonts = JsonSlurper().parse(fontManifest) as List<*> val optimizedFonts = fonts.filterNot { entry -> val family = (entry as? Map<*, *>)?.get("family") family == "packages/shadcn_flutter/GeistSans" || family == "packages/shadcn_flutter/GeistMono" } fontManifest.writeText(JsonOutput.toJson(optimizedFonts)) } } tasks.matching { it.name == "copyFlutterAssetsRelease" }.configureEach { dependsOn(pruneReleaseFlutterAssets) }