com.cleveradssolutions.gradleplugin.CASPlugin.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-plugin Show documentation
Show all versions of gradle-plugin Show documentation
CAS Gradle Plugin provides an easy way to integrate and configure CAS.AI Mediation in your android project.
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
}
}