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" } android { namespace = "com.pilotvault.flyapp" compileSdk = 35 // DJI MSDK V4 ships prebuilt native (.so) libraries; pin a known-good NDK. ndkVersion = flutter.ndkVersion compileOptions { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } kotlinOptions { jvmTarget = JavaVersion.VERSION_1_8 } defaultConfig { applicationId = "com.pilotvault.flyapp" // DJI MSDK V4 requires Android 5.0+ (API 21). minSdkVersion = flutter.minSdkVersion targetSdk = 34 versionCode = flutter.versionCode versionName = flutter.versionName // DJI's SDK + Secneo Helper class loading require MultiDex. multiDexEnabled = true // DJI provides prebuilt .so files only for these ABIs. ndk { abiFilters "armeabi-v7a", "arm64-v8a" } // The DJI App Key is injected into AndroidManifest.xml at build time. // It is deliberately not stored in the repo — see android/gradle.properties // for where to put it (~/.gradle/gradle.properties, or -P on the command // line). Warn loudly when it is missing: the build still succeeds, but the // resulting APK fails DJI SDK registration once it is on the aircraft. // println, not logger.warn: `flutter build` filters Gradle's warn-level // output, so a logger.warn here is never seen by the person building. def djiAppKey = project.findProperty("PILOTVAULT_FLY_API_KEY") if (!djiAppKey) { println( "\n**************************************************************\n" + "WARNING: PILOTVAULT_FLY_API_KEY is not set.\n" + "The APK will build, but DJI SDK registration will FAIL at\n" + "runtime. Set the key in ~/.gradle/gradle.properties - see\n" + "android/gradle.properties for details.\n" + "**************************************************************\n") } manifestPlaceholders["PILOTVAULT_FLY_API_KEY"] = (djiAppKey ?: "PASTE_YOUR_DJI_APP_KEY_HERE") } buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig = signingConfigs.debug // DJI requires its ProGuard rules when minify is enabled. proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" } } // DJI native libraries must not be stripped, and several bundled assets/ // resources collide across the SDK modules and must be de-duplicated. packaging { jniLibs { keepDebugSymbols += [ "**/libdjivideo.so", "**/libSDKRelativeJNI.so", "**/libFlyForbid.so", "**/libduml_vision_bokeh.so", "**/libyuv2.so", "**/libGroudStation.so", "**/libFRCorkscrew.so", "**/libUpgradeVerify.so", "**/libFR.so", "**/libDJIFlySafeCore.so", "**/libdjifs_jni.so", "**/libsfjni.so", "**/libDJICommonJNI.so", "**/libDJICSDKCommon.so", "**/libDJIUpgradeCore.so", "**/libDJIUpgradeJNI.so", "**/libDJIWaypointV2Core.so", "**/libdjiwaypointv2.so", "**/libDJIMOP.so", "**/libDJISDKLOGJNI.so", ] } resources { excludes += [ "META-INF/rxjava.properties", "META-INF/proguard/*", "META-INF/INDEX.LIST", "META-INF/DEPENDENCIES", "assets/location_map_gps_locked.png", "assets/location_map_gps_3d.png", ] pickFirsts += [ "lib/**/libstlport_shared.so", "lib/**/libRoadLineRebuildAPI.so", "lib/**/libGNaviUtils.so", "lib/**/libGNaviMapex.so", "lib/**/libGNaviMap.so", "lib/**/libGNaviSearch.so", ] } } } flutter { source = "../.." } dependencies { // DJI Mobile SDK V4 (core). 'provided' contains compile-time-only stubs. implementation "com.dji:dji-sdk:4.18" compileOnly "com.dji:dji-sdk-provided:4.18" implementation "androidx.multidex:multidex:2.0.1" implementation "androidx.core:core-ktx:1.13.1" // The DJI SDK bundles layouts that reference AppCompat (srcCompat) and // ConstraintLayout attributes, so these must be on the resource classpath. implementation "androidx.appcompat:appcompat:1.6.1" implementation "androidx.constraintlayout:constraintlayout:2.1.4" // DJI's SDK publishes an event bus dependency used internally by some callbacks. implementation "com.squareup:otto:1.3.8" // Required by the FlySafe / GEO modules pulled in by the SDK. implementation "androidx.recyclerview:recyclerview:1.3.2" } // --- Keep transitive AndroidX on the AGP-8.6 / compileSdk-35 track ----------- // The newer Flutter plugins (url_launcher / path_provider / flutter_svg) pull // androidx.core 1.17 and androidx.browser 1.9, whose aar metadata demands AGP // 8.9.1 + compileSdk 36. The DJI MSDK V4 toolchain is pinned to AGP 8.6 / SDK 35 // (bumping it risks the fragile native build), so force these artifacts back to // versions that build against SDK 35. Their APIs used by the plugins are stable // across this range. configurations.all { resolutionStrategy { force "androidx.core:core:1.13.1" force "androidx.core:core-ktx:1.13.1" force "androidx.browser:browser:1.8.0" } } // --- Gradle 8 task-validation workaround ------------------------------------ // Flutter's `compileFlutterBuild` task declares an output directory // that overlaps the Android source sets, so Gradle 8's execution-time // validation flags several AGP tasks (mergeShaders, checkAarMetadata, …) as // consuming that output without a declared dependency, failing the build. // Wire the dependency in explicitly. Safe: none of these tasks are inputs to // the Flutter compile task, so no dependency cycle is introduced. afterEvaluate { def flutterTaskFor = { String name -> for (v in ["Debug", "Release", "Profile"]) { if (name.contains(v)) return tasks.findByName("compileFlutterBuild${v}") } return null } // Merge* source-set tasks (shaders / assets / jniLibs) — public AGP type. tasks.withType(com.android.build.gradle.tasks.MergeSourceSetFolders).configureEach { t -> def ft = flutterTaskFor(t.name) if (ft != null) t.dependsOn(ft) } // Other AGP tasks that read the merged inputs. tasks.matching { it.name ==~ /^(check|process|package|bundle|lintVitalAnalyze|lintAnalyze)(Debug|Release|Profile).*/ }.configureEach { t -> def ft = flutterTaskFor(t.name) if (ft != null) t.dependsOn(ft) } }