godot/platform/android/java/app/build.gradle

// Gradle build config for Godot Engine's Android port.
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
}

apply from: 'config.gradle'

allprojects {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
        maven { url "https://plugins.gradle.org/m2/" }
        maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots/"}

        // Godot user plugins custom maven repos
        String[] mavenRepos = getGodotPluginsMavenRepos()
        if (mavenRepos != null && mavenRepos.size() > 0) {
            for (String repoUrl : mavenRepos) {
                maven {
                    url repoUrl
                }
            }
        }
    }
}

configurations {
    // Initializes a placeholder for the devImplementation dependency configuration.
    devImplementation {}
    // Initializes a placeholder for the monoImplementation dependency configuration.
    monoImplementation {}
}

dependencies {
    implementation "androidx.fragment:fragment:$versions.fragmentVersion"
    implementation "androidx.core:core-splashscreen:$versions.splashscreenVersion"

    if (rootProject.findProject(":lib")) {
        implementation project(":lib")
    } else if (rootProject.findProject(":godot:lib")) {
        implementation project(":godot:lib")
    } else {
        // Godot gradle build mode. In this scenario this project is the only one around and the Godot
        // library is available through the pre-generated godot-lib.*.aar android archive files.
        debugImplementation fileTree(dir: 'libs/debug', include: ['**/*.jar', '*.aar'])
        devImplementation fileTree(dir: 'libs/dev', include: ['**/*.jar', '*.aar'])
        releaseImplementation fileTree(dir: 'libs/release', include: ['**/*.jar', '*.aar'])
    }

    // Godot user plugins remote dependencies
    String[] remoteDeps = getGodotPluginsRemoteBinaries()
    if (remoteDeps != null && remoteDeps.size() > 0) {
        for (String dep : remoteDeps) {
            implementation dep
        }
    }

    // Godot user plugins local dependencies
    String[] pluginsBinaries = getGodotPluginsLocalBinaries()
    if (pluginsBinaries != null && pluginsBinaries.size() > 0) {
        implementation files(pluginsBinaries)
    }

    // Automatically pick up local dependencies in res://addons
    String addonsDirectory = getAddonsDirectory()
    if (addonsDirectory != null && !addonsDirectory.isBlank()) {
        implementation fileTree(dir: "$addonsDirectory", include: ['*.jar', '*.aar'])
    }

    // .NET dependencies
    String jar = '../../../../modules/mono/thirdparty/libSystem.Security.Cryptography.Native.Android.jar'
    if (file(jar).exists()) {
        monoImplementation files(jar)
    }
}

android {
    compileSdkVersion versions.compileSdk
    buildToolsVersion versions.buildTools
    ndkVersion versions.ndkVersion

    compileOptions {
        sourceCompatibility versions.javaVersion
        targetCompatibility versions.javaVersion
    }

    kotlinOptions {
        jvmTarget = versions.javaVersion
    }

    assetPacks = [":assetPacks:installTime"]

    namespace = 'com.godot.game'

    defaultConfig {
        // The default ignore pattern for the 'assets' directory includes hidden files and directories which are used by Godot projects.
        aaptOptions {
            ignoreAssetsPattern "!.svn:!.git:!.gitignore:!.ds_store:!*.scc:<dir>_*:!CVS:!thumbs.db:!picasa.ini:!*~"
        }

        ndk {
            String[] export_abi_list = getExportEnabledABIs()
            abiFilters export_abi_list
        }

        manifestPlaceholders = [godotEditorVersion: getGodotEditorVersion()]

        // Feel free to modify the application id to your own.
        applicationId getExportPackageName()
        versionCode getExportVersionCode()
        versionName getExportVersionName()
        minSdkVersion getExportMinSdkVersion()
        targetSdkVersion getExportTargetSdkVersion()

        missingDimensionStrategy 'products', 'template'
    }

    lintOptions {
        abortOnError false
        disable 'MissingTranslation', 'UnusedResources'
    }

    ndkVersion versions.ndkVersion

    packagingOptions {
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/NOTICE'

        // 'doNotStrip' is enabled for development within Android Studio
        if (shouldNotStrip()) {
            doNotStrip '**/*.so'
        }

        jniLibs {
            // Setting this to true causes AGP to package compressed native libraries when building the app
            // For more background, see:
            // - https://developer.android.com/build/releases/past-releases/agp-3-6-0-release-notes#extractNativeLibs
            // - https://stackoverflow.com/a/44704840
            useLegacyPackaging shouldUseLegacyPackaging()
        }

        // Always select Godot's version of libc++_shared.so in case deps have their own
        pickFirst 'lib/x86/libc++_shared.so'
        pickFirst 'lib/x86_64/libc++_shared.so'
        pickFirst 'lib/armeabi-v7a/libc++_shared.so'
        pickFirst 'lib/arm64-v8a/libc++_shared.so'
    }

    signingConfigs {
        debug {
            if (hasCustomDebugKeystore()) {
                storeFile new File(getDebugKeystoreFile())
                storePassword getDebugKeystorePassword()
                keyAlias getDebugKeyAlias()
                keyPassword getDebugKeystorePassword()
            }
        }

        release {
            File keystoreFile = new File(getReleaseKeystoreFile())
            if (keystoreFile.isFile()) {
                storeFile keystoreFile
                storePassword getReleaseKeystorePassword()
                keyAlias getReleaseKeyAlias()
                keyPassword getReleaseKeystorePassword()
            }
        }
    }

    buildFeatures {
        buildConfig = true
    }

    buildTypes {

        debug {
            // Signing and zip-aligning are skipped for prebuilt builds, but
            // performed for Godot gradle builds.
            zipAlignEnabled shouldZipAlign()
            if (shouldSign()) {
                signingConfig signingConfigs.debug
            } else {
                signingConfig null
            }
        }

        dev {
            initWith debug
            // Signing and zip-aligning are skipped for prebuilt builds, but
            // performed for Godot gradle builds.
            zipAlignEnabled shouldZipAlign()
            if (shouldSign()) {
                signingConfig signingConfigs.debug
            } else {
                signingConfig null
            }
        }

        release {
            // Signing and zip-aligning are skipped for prebuilt builds, but
            // performed for Godot gradle builds.
            zipAlignEnabled shouldZipAlign()
            if (shouldSign()) {
                signingConfig signingConfigs.release
            } else {
                signingConfig null
            }
        }
    }

    flavorDimensions 'edition'

    productFlavors {
        standard {}
        mono {}
    }

    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            res.srcDirs = ['res']
            aidl.srcDirs = ['aidl']
            assets.srcDirs = ['assets']
        }
        debug.jniLibs.srcDirs = ['libs/debug', 'libs/debug/vulkan_validation_layers']
        dev.jniLibs.srcDirs = ['libs/dev']
        release.jniLibs.srcDirs = ['libs/release']
    }

    applicationVariants.all { variant ->
        variant.outputs.all { output ->
            String filenameSuffix = variant.flavorName == "mono" ? variant.name : variant.buildType.name
            output.outputFileName = "android_${filenameSuffix}.apk"
        }
    }
}

task copyAndRenameBinary(type: Copy) {
    // The 'doNotTrackState' is added to disable gradle's up-to-date checks for output files
    // and directories. Otherwise this check may cause permissions access failures on Windows
    // machines.
    doNotTrackState("No need for up-to-date checks for the copy-and-rename operation")

    String exportPath = getExportPath()
    String exportFilename = getExportFilename()
    String exportEdition = getExportEdition()
    String exportBuildType = getExportBuildType()
    String exportBuildTypeCapitalized = exportBuildType.capitalize()
    String exportFormat = getExportFormat()

    boolean isAab = exportFormat == "aab"
    boolean isMono = exportEdition == "mono"
    String filenameSuffix = exportBuildType
    if (isMono) {
        filenameSuffix = isAab ? "${exportEdition}-${exportBuildType}" : "${exportEdition}${exportBuildTypeCapitalized}"
    }

    String sourceFilename = isAab ? "build-${filenameSuffix}.aab" : "android_${filenameSuffix}.apk"
    String sourceFilepath = isAab ? "$buildDir/outputs/bundle/${exportEdition}${exportBuildTypeCapitalized}/$sourceFilename" : "$buildDir/outputs/apk/$exportEdition/$exportBuildType/$sourceFilename"

    from sourceFilepath
    into exportPath
    rename sourceFilename, exportFilename
}

/**
 * Used to validate the version of the Java SDK used for the Godot gradle builds.
 */
task validateJavaVersion {
    if (JavaVersion.current() != versions.javaVersion) {
        throw new GradleException("Invalid Java version ${JavaVersion.current()}. Version ${versions.javaVersion} is the required Java version for Godot gradle builds.")
    }
}

/*
When they're scheduled to run, the copy*AARToAppModule tasks generate dependencies for the 'app'
module, so we're ensuring the ':app:preBuild' task is set to run after those tasks.
 */
if (rootProject.tasks.findByPath("copyDebugAARToAppModule") != null) {
    preBuild.mustRunAfter(rootProject.tasks.named("copyDebugAARToAppModule"))
}
if (rootProject.tasks.findByPath("copyDevAARToAppModule") != null) {
    preBuild.mustRunAfter(rootProject.tasks.named("copyDevAARToAppModule"))
}
if (rootProject.tasks.findByPath("copyReleaseAARToAppModule") != null) {
    preBuild.mustRunAfter(rootProject.tasks.named("copyReleaseAARToAppModule"))
}