Creating a Module
Core SDK Dependency ๐
All modules in the Chartboost ecosystem must depend upon the Core SDK. This is to ensure that the module you develop can be initialized by the Core SDK. Include the following dependency to your module:
com.chartboost:chartboost-core-sdk:1.0.0
Required Module API Implementations ๐
Module
๐
The moduleโs initialization entry point must conform to the Module
interface so that the module is initializable by the Core SDK.
Your module implementation should gracefully handle situations where multiple initialization attempts are made. If your module is a single initialization type of implementation, it is recommended to synchronize your initialization method to prevent multithreaded access and to maintain an initialization state.
The interfaces are listed in the example below, however, we recommended that you double check the Core SDK API Reference.
/**
* This interface is to be implemented by modules that can be initialized by [ChartboostCore].
*/
interface Module {
/**
* The ID of the module. This is recommended to be unique.
*/
val moduleId: String
/**
* The version of the module. This is recommended to be a semantic version e.g. 1.0.0.
*/
val moduleVersion: String
/**
* Updates the module with JSON data from the server. Publisher is recommended to
* initialize via the constructor with module-specific parameters rather than using this function.
* When creating a module, please make sure it's possible to send a JSONObject configuration
* object to set up the properties of this module.
*/
fun updateProperties(
context: Context,
configuration: JSONObject,
)
/**
* Initialize the module.
*
* @param context The [Context] to use for initialization.
* @param moduleConfiguration Configuration data for initializing the module.
*
* @return Result.success if the module was initialized successfully, Result.failure(Exception) otherwise.
*/
suspend fun initialize(
context: Context,
moduleConfiguration: ModuleConfiguration,
): Result<Unit>
}
-------------------------------
/**
* Configuration object for initializing Chartboost Core modules.
*
* @property chartboostApplicationIdentifier The Chartboost application identifier.
*/
data class ModuleConfiguration(val chartboostApplicationIdentifier: String)
ConsentAdapter
๐
For custom CMP adapters, the moduleโs initialization entry point must conform to the ConsentAdapter
interface so that the module is recognized as a CMP adapter and initializable by the Core SDK.
In addition to conforming to the Module
interface, the module must also conform to all of the CMP-specific methods, properties, and callbacks.
The interfaces are listed in the example below, however, we recommended that you double check the Core SDK API Reference.
/**
* The consent management platform adapter to communicate between Chartboost Core and the CMP.
*/
interface ConsentAdapter {
companion object {
private const val IAB_PREFIX = "IAB"
}
/**
* This tells the publisher whether or not to show a consent dialog.
*/
val shouldCollectConsent: Boolean
/**
* Map of consent standards (eg. GDPR, USP, etc) to the consent value (eg. GRANTED, 1YN-, etc).
* See [DefaultConsentKey] and [DefaultConsentValue] for other
* common examples.
*/
val consents: Map<ConsentKey, ConsentValue>
/**
* If any consent standards or status changes happen, notify this listener.
*/
var listener: ConsentAdapterListener?
/**
* Show a consent dialog.
*
* @param activity The activity to attach the consent dialog.
* @param dialogType The type of dialog to show.
*
* @return Success Result or a Result with an Exception.
*/
suspend fun showConsentDialog(
activity: Activity,
dialogType: ConsentDialogType,
): Result<Unit>
/**
* Grant user consent without showing a dialog.
*
* @param context The Android Context.
* @param statusSource How this consent status was retrieved.
*
* @return Success Result or a Result with an Exception.
*/
suspend fun grantConsent(
context: Context,
statusSource: ConsentSource,
): Result<Unit>
/**
* Deny user consent without showing a dialog.
*
* @param context The Android Context.
* @param statusSource How this consent status was retrieved.
*
* @return Success Result or a Result with an Exception.
*/
suspend fun denyConsent(
context: Context,
statusSource: ConsentSource,
): Result<Unit>
/**
* Reset user consent without showing a dialog. This usually also resets the underlying
* consent management platform and re-initializes.
*
* @param context The Android Context.
*
* @return Success Result or a Result with an Exception.
*/
suspend fun resetConsent(context: Context): Result<Unit>
Using Environment Information ๐
The ChartboostCore
public API provides three predefined environment information objects for different purposes:
AdvertisingEnvironment
AnalyticsEnvironment
AttributionEnvironment
These environments provide information based upon an intended purpose and are grouped to provide module creators guidance on what information is permissible for a given purpose. These environments are static, and can be accessed before the Core SDK is initialized.
The environment properties located in the ChartboostCore
class are listed below.
/**
* An environment that contains information intended for advertising purposes.
*/
interface AdvertisingEnvironment {
/**
* The bundle identifier of this application.
*/
val bundleIdentifier: String?
/**
* The device locale.
*/
val deviceLocale: String?
/**
* The make or manufacturer of the device.
*/
val deviceMake: String
/**
* The model of the device. This is usually a consumer product name.
*/
val deviceModel: String
/**
* The operating system name. Always "Android".
*/
val osName: String
/**
* The operating system version.
*/
val osVersion: String
/**
* The height of the screen in pixels.
*/
val screenHeightPixels: Int?
/**
* The logical density of the display. Used to convert pixels to density-independent pixels. See
* https://developer.android.com/reference/android/util/DisplayMetrics#density
* for more information.
*/
val screenScale: Float?
/**
* The width of the screen in pixels.
*/
val screenWidthPixels: Int?
/**
* Gets the advertising identifier.
*
* @return The advertising identifier if available.
*/
suspend fun getAdvertisingIdentifier(): String?
/**
* Gets whether or not limit ad tracking is enabled.
*
* @return true if LAT is enabled and false otherwise.
*/
suspend fun getLimitAdTrackingEnabled(): Boolean?
/**
* Add an observer to listen for changes of [ObservableEnvironmentProperty].
*/
fun addObserver(observer: EnvironmentObserver)
/**
* Remove an EnvironmentObserver.
*/
fun removeObserver(observer: EnvironmentObserver)
}
----------------------------------
/**
* An environment that contains information intended for attribution purposes.
*/
interface AttributionEnvironment {
/**
* Gets the advertising identifier.
*
* @return The advertising identifier if available.
*/
suspend fun getAdvertisingIdentifier(): String?
/**
* Gets the WebView user agent.
*
* @return The user agent.
*/
suspend fun getUserAgent(): String?
}
----------------------------------
/**
* An environment that contains information intended for analytics purposes.
*/
interface AnalyticsEnvironment {
/**
* The current app session duration in seconds.
*/
val appSessionDurationSeconds: Double
/**
* The Chartboost-specified unique app session identifier for this app launch.
*/
val appSessionIdentifier: String?
/**
* The version of the app.
*/
val appVersion: String?
/**
* The wrapper or framework used to create this app. ie. Unity.
*/
val frameworkName: String?
/**
* The version of the framework used to create this app.
*/
val frameworkVersion: String?
/**
* Whether or not the publisher reported that the current user is underage as per COPPA.
*/
val isUserUnderage: Boolean?
/**
* The type of network that is currently connected, if any.
*/
val networkConnectionType: NetworkConnectionType
/**
* The publisher-specified player identifier. This is typically used for rewarding users in the
* rewarded ad format and also for user tracking.
*/
val playerIdentifier: String?
/**
* The publisher-specified app identifier. This is usually what a publisher uses to identify
* their app.
*/
val publisherAppIdentifier: String?
/**
* The publisher-specified session identifier. This is recommended to be a unique string
* for every app launch.
*/
val publisherSessionIdentifier: String?
/**
* A number [0.0, 1.0] to signify the current volume of the device.
*/
val volume: Double?
/**
* Gets the WebView user agent.
*
* @return The user agent.
*/
suspend fun getUserAgent(): String?
/**
* Gets the advertising identifier for vendors.
*
* @return The vendor identifier if possible.
*/
suspend fun getVendorIdentifier(): String?
/**
* Gets whether the advertising identifier for vendors is sourced from the developer or from
* the app.
*
* @return A [VendorIdScope] or [VendorIdScope.UNKNOWN] if not specified.
*/
suspend fun getVendorIdentifierScope(): VendorIdScope
/**
* The bundle identifier of this application.
*/
val bundleIdentifier: String?
/**
* The device locale.
*/
val deviceLocale: String?
/**
* The make or manufacturer of the device.
*/
val deviceMake: String
/**
* The model of the device. This is usually a consumer product name.
*/
val deviceModel: String
/**
* The operating system name. Always "Android".
*/
val osName: String
/**
* The operating system version.
*/
val osVersion: String
/**
* The height of the screen in pixels.
*/
val screenHeight: Int?
/**
* The logical density of the display. Used to convert pixels to density-independent pixels. See
* https://developer.android.com/reference/android/util/DisplayMetrics#density
* for more information.
*/
val screenScale: Float?
/**
* The width of the screen in pixels.
*/
val screenWidth: Int?
/**
* Gets the advertising identifier.
*
* @return The advertising identifier if available.
*/
suspend fun getAdvertisingIdentifier(): String?
/**
* Gets whether or not limit ad tracking is enabled.
*
* @return true if LAT is enabled and false otherwise.
*/
suspend fun getLimitAdTrackingEnabled(): Boolean?
/**
* Add an observer to listen for changes of [ObservableEnvironmentProperty].
*/
fun addObserver(observer: EnvironmentObserver)
/**
* Remove an EnvironmentObserver.
*/
fun removeObserver(observer: EnvironmentObserver)
}
Subscribing to Publisher Metadata Updates ๐
Publisher metadata can be set or updated by the Publisher at any point in the appโs lifecycle. If your module requires reacting to changes in these values in real time, you can subscribe to these changes by calling the AnalyticsEnvironment.addObserver()
method with your EnvironmentObserver
object.
Module
and EnvironmentObserver
do not need to explicitly subscribe as they are automatically subscribed at initialization time.
Using Consent Information ๐
Modules do not need to handle their own user consent collection, and can instead rely upon the Core SDKโs consent mediation to provide that information.
To query the consent values from the currently initialized CMP, access the ConsentManagementPlatform
interface from the ChartboostCore.consent
property.
Consent information is valid only if a CMP adapter was successfully initialized by the Core SDK.
In the event that no CMP adapter was successfully initialized, the current consent information will be unavailable, and modules should assume that consent has not been given.
Handling Underage Users ๐
CMPs do not provide COPPA related or child-directed APIs as COPPA relates to whether a user is underage or an app is child-directed, not whether the user has given consent for various use cases.
This information is captured by the isUserUnderage
property in AnalyticsEnvironment
, and is set by the Publisher via PublisherMetadata.setIsUserUnderage()
.
Querying Current Consent ๐
To obtain the consent, query the ConsentManagementPlatform.consents
property for the map of current consent states.
You can then use one of the following keys in the table below to access the desired consent value if it exists. Each platform will have its own convenience constants as well.
Note that if a consent value does not exist for the specified key, it likely means that the CMP does not support that consent standard.
Consent Standard Key | Description |
---|---|
ccpa_opt_in | CCPA opt-in |
gdpr_consent_given | GDPR consent |
IABGPP_HDR_GppString | IAB GPP string |
IABTCF_TCString | IAB TCF string |
IABUSPrivacy_String | IAB USP string |
Subscribing to Consent Changes ๐
Consent may change at any given time, and it is recommended to subscribe to consent changes if your module needs to react in real time to those consent changes.
Module
and EnvironmentObserver
do not need to explicitly subscribe as they are automatically subscribed at initialization time.
To subscribe to consent changes, register your observer using the ConsentManagementPlatform.addObserver()
call.
The Core SDK also provides a 500ms debouncing logic for CMP consent changes so that observers are not inundated with callbacks in a short period of time.
Usage Example ๐
// Querying TCF, USP, GPP strings from ChartboostCore.consent.
val consents = ChartboostCore.consent.consents
val tcfString = consents[DefaultConsentKey.TCF]
val uspString = consents[DefaultConsentKey.USP]
val gppString = consents[DefaultConsentKey.GPP]
// Querying isUserUnderage from AnalyticsEnvironment.
val isUserUnderage = ChartboostCore.consent.analyticsEnvironment.isUserUnderage
// Subscribing to consent changes.
ChartboostCore.consent.addObserver(object : ConsentObserver {
override fun onConsentModuleReady(initialConsents: Map<ConsentKey, ConsentValue>) {
// The initial consent info is now ready in $fullConsents
}
override fun onConsentChange(fullConsents: Map<ConsentKey, ConsentValue>,
modifiedKeys: Set<ConsentKey>,
) {
// React to consent changes
Log.d("Consent", "Consent changed for $modifiedKeys")
}
})
Handling Multiple Initializations ๐
It is possible that the moduleโs initialization method may be invoked multiple times.
This usually occurs when the Core SDK attempts to initialize the module, and the module reports back a failure. The Core SDK will attempt to retry module initialization on an exponential backoff up to the maximum number of attempts.
As a module developer, it is good practice to add safeguards to your module initialization implementation so that you can guard against the multiple initialization edge cases such as:
- Initializing an already initialized module.
- Cleaning up state after failed module initializations.
Module API Access Before Module Initialization ๐
Any public API may be called by a Publisher before the module has initialized.
It is good practice to identify which public APIs can function without the module being initialized vs APIs that cannot function without being initialized. For the public APIs that cannot function without being initialized, be sure to put in a fast fail initialization check.