All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cleveradssolutions.gradleplugin.CASPlugin.kt Maven / Gradle / Ivy

Go to download

CAS Gradle Plugin provides an easy way to integrate and configure CAS.AI Mediation in your android project.

There is a newer version: 3.9.4
Show newest version
package com.cleveradssolutions.gradleplugin

import com.android.build.gradle.AppExtension
import com.android.build.gradle.LibraryExtension
import com.cleveradssolutions.gradleplugin.BuildConfig.TAG
import org.gradle.api.GradleException
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.logging.Logger
import java.io.File


private const val GOOGLE_ADS_APP_ID = "com.google.android.gms.ads.APPLICATION_ID"
private const val GOOGLE_ADS_TEST_APP_ID: String = "ca-app-pub-3940256099942544~3347511713"
private const val PERMISSION_AD_ID: String = "com.google.android.gms.permission.AD_ID"
private const val TOOLS_NODE: String = "tools:node=\""

class CASPlugin : Plugin {

    override fun apply(project: Project) {
        val casExtension =
            project.extensions.create("cas", CASPluginExtension::class.java, AdaptersImpl())

        project.gradle.addListener(CASDependencyManager(project))

        project.afterEvaluate {
            handleAfterEvaluate(it, casExtension)
        }
    }

    private fun handleAfterEvaluate(
        project: Project,
        casExtension: CASPluginExtension
    ) {
        val projectExt = project.extensions
        val appExtension = projectExt.findByType(AppExtension::class.java)
        val libraryExtension = projectExt.findByType(LibraryExtension::class.java)

        if (appExtension == null && libraryExtension == null) {
            throw GradleException("$TAG needs to be applied on Application or Library modules.")
        }

        val casId = findCASAppId(project, casExtension, appExtension)
        val isAppExtension = appExtension != null
        val casSettingsFile: File = CASSettings.findPathToLinkFile(
            project, casId, isAppExtension
        )

        val casTaskName = "generateCASLinkFile"
        val casTask = project.tasks.register(casTaskName, CASResourceTask::class.java) {
            it.usedAppExtension.set(isAppExtension)
            it.casId.set(casId)
            it.casLinkFile.set(casSettingsFile)
            it.projectName.set(project.name)
            it.outputs.upToDateWhen {
                !CASSettings.isSettingsFileExpired(casSettingsFile)
            }
        }

        val preBuildTask = project.tasks.findByName("preBuild")
        if (preBuildTask != null) {
            preBuildTask.dependsOn(casTask)
        } else {
            project.logger.error(
                "$TAG: Required task with name 'preBuild' not found for module {}. Please run '{}' task before build the app.",
                project.name, casTaskName
            )
        }

        var variantNotFound = true
        appExtension?.applicationVariants?.forEach {
            handleVariantManifest(project, it.name, casId, casExtension, casSettingsFile)
            variantNotFound = false
        }
        if (variantNotFound && libraryExtension != null) {
            libraryExtension.libraryVariants.forEach {
                handleVariantManifest(project, it.name, casId, casExtension, casSettingsFile)
                variantNotFound = false
            }
        }

        if (variantNotFound) {
            throw GradleException(
                "$TAG: no build variants found for the module ${project.name}. The plugin requires at least one build variant that uses in Application build.",
            )
        }
    }

    private fun findCASAppId(
        project: Project,
        casExtension: CASPluginExtension,
        appExtension: AppExtension?
    ): String {
        var casId: String?
        var errorMessage: String? = null
        if (appExtension != null) {
            // The defaultConfig.applicationId in Library is not Application ID
            casId = appExtension.defaultConfig.applicationId
            if (!casId.isNullOrEmpty()) {
                if (casExtension.casId != null && casExtension.casId != casId) {
                    project.logger.warn("$TAG ignores the `cas.casId` setting in favor of the `defaultConfig.applicationId`.")
                }
                return casId
            }

            /*
            // Namespace is not same as Application ID
            try {
                // appExtension.namespace introduced in AGP 7.0 version.
                casId = BaseExtension::class.java.getMethod("getNamespace")
                    .invoke(appExtension) as String
                if (casId.isNotEmpty())
                    return casId
            } catch (_: Throwable) {
            }
            */
            errorMessage = """
            CAS ID not set for current module. 
            Please modify the module's build.gradle file and set the CAS ID:
                android {
                    defaultConfig {
                        applicationId = "App CAS Id"
                    }
                }
            If you haven't created an CAS account and registered an app yet, just set any Application bundle name.
            """.trimIndent()
        }

        casId = casExtension.casId
        if (!casId.isNullOrEmpty()) {
            if (errorMessage != null)
                project.logger.error(errorMessage)
            return casId
        }

        throw GradleException(
            errorMessage ?: """
            CAS ID not set for current module. 
            Please modify the module's build.gradle file and set the CAS ID:
                cas.casId = "App CAS Id"
            If you haven't created an CAS account and registered an app yet, just set any Application bundle name.
            """.trimIndent()
        )
    }

    private fun handleVariantManifest(
        project: Project,
        variantName: String,
        casId: String,
        casExtension: CASPluginExtension,
        casSettingsFile: File
    ) {
        val taskNameSuffix = variantName.titleCaseFirstChar()
        var processManifestTaskName = "process${taskNameSuffix}MainManifest"
        var task = project.tasks.findByName(processManifestTaskName)
        if (task == null) {
            processManifestTaskName = "process${taskNameSuffix}Manifest"
            task = project.tasks.findByName(processManifestTaskName)
        }
        if (task == null) {
            throw GradleException(
                "$TAG: Required task with name '$processManifestTaskName' not found in module ${project.name}"
            )
        }
        val useAdvertisingIdPermission = casExtension.run {
            useAdvertisingId && (includeOptimalAds || !includeFamiliesAds)
        }
        val useGoogleAppId = casExtension.run {
            configureGoogleAds && (adapters.googleAds
                    || includeFamiliesAds
                    || includeOptimalAds)
        }

        task.inputs.properties(
            mapOf(
                "casId" to casId,
                "casLinkFile" to casSettingsFile,
                "configureGoogleAds" to casExtension.configureGoogleAds,
                "useAdvertisingId" to casExtension.useAdvertisingId,
                "includeFamiliesAds" to casExtension.includeFamiliesAds,
                "includeOptimalAds" to casExtension.includeOptimalAds
            )
        )

        task.doLast {
            val isValidLinkFile =
                CASSettings.tryRefreshCASSettings(it.logger, casId, casSettingsFile)

            val manifestFile = it.outputs.files.firstOrNull { file ->
                file.name == "AndroidManifest.xml"
            }
            if (manifestFile?.exists() != true) {
                it.logger.error(
                    "$TAG: Merged AndroidManifest.xml not found! {}",
                    manifestFile
                )
                return@doLast
            }
            var manifest = manifestFile.readText()
            try {
                manifest = processAdIdInManifest(manifest, useAdvertisingIdPermission)
            } catch (e: Throwable) {
                it.logger.error("$TAG: Process Manifest", e)
            }
            try {
                if (useGoogleAppId && isValidLinkFile) {
                    manifest = processGoogleAppIdInManifest(
                        it.logger, manifest, casSettingsFile
                    )
                }
            } catch (e: Throwable) {
                it.logger.error("$TAG: Process Manifest", e)
            }

            manifestFile.writeText(manifest)
        }
    }

    private fun processAdIdInManifest(manifest: String, applyAdId: Boolean): String {
        if (applyAdId) {
            val pos = manifest.indexOf(PERMISSION_AD_ID)
            if (pos > -1) {
                val nameEnd = pos + PERMISSION_AD_ID.length
                val permissionEnd = manifest.indexOf('>', nameEnd)
                val toolsStart = manifest.indexOf(TOOLS_NODE, nameEnd)
                if (toolsStart > -1 && toolsStart < permissionEnd) {
                    val toolsEnd = manifest.indexOf('"', toolsStart + TOOLS_NODE.length)
                    return manifest.removeRange(toolsStart..toolsEnd)
                }
            } else {
                return ManifestUtils.addPermission(manifest, PERMISSION_AD_ID)
            }
        } else {
            val pos = manifest.indexOf(PERMISSION_AD_ID)
            if (pos > -1) {
                return ManifestUtils.removePermission(manifest, pos)
            }
        }
        return manifest
    }

    private fun processGoogleAppIdInManifest(
        logger: Logger,
        manifest: String,
        casSettingsFile: File
    ): String {
        // If the Google App ID exists in the manifest,
        // but there is no Google App ID in the CAS settings,
        // do nothing.
        val adMobAppId = CASSettings.getCASSettings(casSettingsFile)?.adMobAppId
        val pos = manifest.indexOf(GOOGLE_ADS_APP_ID)
        return if (pos < 0) {
            // Metadata not found in manifest
            val appId = adMobAppId ?: GOOGLE_ADS_TEST_APP_ID
            logger.info("$TAG apply Google App Id: {}", appId)
            ManifestUtils.addMetaData(
                manifest,
                GOOGLE_ADS_APP_ID,
                appId
            )
        } else if (adMobAppId != null) {
            logger.info("$TAG apply Google App Id: {}", adMobAppId)
            ManifestUtils.updateMetaData(
                manifest,
                adMobAppId,
                pos
            )
        } else {
            manifest
        }
    }

    private fun String.titleCaseFirstChar(): String {
        if (isEmpty()) return this
        val char = get(0)
        return if (char.isLowerCase()) char.titlecaseChar() + substring(1) else this
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy