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.

The 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