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.
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
}
}