- Change dependency in your app script to
implementation("io.piano.android:cxense:VERSION")
- Remove Jitpack from your app script, if you don't have any other dependencies from this repository
- Update all imports:
import com.cxense.cxensesdk.XXXXX
->import io.piano.android.cxense.XXXXX
Since 2.0 SDK is written in Kotlin. Also we use Timber library for logging.
There are breaking changes in API:
- All methods
setXX(value)
in event's builders renamed toXX(value)
. For example:setEventId("id")
->eventId("id")
- We changed parameters order
UserIdentity(id, type)
->UserIdentity(type, id)
- We changed parameters order
cxenseSdk.getUserExternalData(id, type, callback)
->cxenseSdk.getUserExternalData(type, id, callback)
- Java 8 / Kotlin
- Android 5.0 (API level 21) or higher
- Gradle 6.0 or newer (use latest version if possible)
Add in your app script: Kotlin script
dependencies {
...
implementation("io.piano.android:cxense:VERSION")
}
Groovy script
dependencies {
...
implementation "io.piano.android:cxense:VERSION"
}
Before SDK can be used in the application it require to be configured. Configuration can be easily provided by utilizing instance of special class called CxenseConfiguration
. It contains set of methods vital for SDK's execution. It requires to set CredentialsProvider
, which provides username and API key or persistedId (see Pixel API for /dmp/push). You can provide it by using special set-method of 'CxenseConfiguration' class:
CxenseSdk.getInstance().configuration.apply {
credentialsProvider = object : CredentialsProvider {
override fun getUsername(): String = BuildConfig.USERNAME // load it from secured store
override fun getApiKey(): String = BuildConfig.API_KEY // load it from secured store
override fun getDmpPushPersistentId(): String = BuildConfig.PERSISTED_ID // fill if you want to use it instead username/api key
}
}
Also you can create class, which implements CredentialsProvider and loads these values from any source (for example, from Firebase Remote config).
SDK doesn't cache these values and ask CredentialsProvider at each request to API. It allows to change values dynamically.
User name and API key can be obtained from Cxense.com portal.
If DmpPushPersistentId is set, all performance events will be pushed through DMP pixel automatically. They are pushed through "/dmp/push/" API directly by default.
If you didn't specify username and API key, you can only send PageView/DMP events (with DmpPushPersistentId) and execute persisted queries viaexecutePersistedQuery
(with any other persisted id).
Configuration
class provides control over other multiple options through which you can modify SDK's behaviour. All available for configuration options are listed below:
Property | Required | Default value | Description |
---|---|---|---|
CredentialsProvider | Yes | Class, which provides: email of the user which has API access, API key of the user with API access or identifier of persistent query which points to "/dmp/push" API. | |
dispatchPeriod | 300 sec | Defines amount of seconds between tries which dispatch loop will perform in attempt to send reported events. Must be 10 seconds or more. | |
outdatePeriod | 7 days | Defines amount of seconds during which all dispatched events will be stored in local database after successful sending. Must be 10 minutes or more. | |
minimumNetworkStatus | none | Defines minimum network conditions upon which dispatch loop can send events. | |
dispatchMode | online | Defines dispatch mode of dispatch loop. | |
isAutoMetaInfoTrackingEnabled | true | Shows whether automatic meta-information tracking is enabled or not. Under meta-information following items are meant: app name, app version, sdk version. In case this flag is enabled, all specified parameters will be send as events' custom parameters. | |
consentSettings | Options that indicate consent on data processing. | ||
sendEventsAtPush | false | Try to send all not submitted events at every pushEvents call (including passed as call parameters) |
To override default SDK's settings just set your values to any of these fields. We recommend to put code related to SDK's configuration modification to 'onCreate()' method in custom application class.
Piano DMP & Content SDK allows tracking events of these types:
- page view
- performance
Page view event is collection of data that describes the visit (time and length of visit; previous, current and next page URL; etc) and the visitor (browser, OS, location, IP address, etc). It is also reffered as 'traffic event'. Performance event describes what the user did while visiting the page.
Page view events are aggregated by Piano Insight. All collected page view events are available for analysis in Insight's web interface.
Use PageViewEvent
class to track page view events in your application. Instances of the class can be easily created using following PageViewEvent.Builder
:
// SiteId - identifier of the site for which current event will be reported
val builder = PageViewEvent.Builder("[put your SiteId here]")
PageViewEventBuilder
class provide set of methods through which you can easily configure data that will be applied to page view instance. Here is example of event's configuration (pay attention that methods of the build can be chained):
builder.location("https://www.google.ru") // The URL of the page
// User profile parameters can be set like this.
// This method automatically prefixes specified key by required 'cp_u_' prefix
.addCustomUserParameters(CustomParameter("xyz-favorite-song", "Hotel California"))
// Custom parameters can be specified for event like this.
// This method automatically prefixes specified key by required 'cp_' prefix
.addCustomParameters(CustomParameter("xyz-user-timezone", "GMT0"))
Result instance of page view event can be created and scheduled to closest dispatch loop's iteration using following code:
CxenseSdk.getInstance().pushEvents(builder.build())
Builder's
build()
method can throwIllegalStateException
to indicate problems with provided parameters. More on error handling in "Error handling" part.
To specify current user external ids PageViewEvent.Builder
class provides special function in its API - addExternalUserId
Here how it can be used:
builder.addExternalUserIds(ExternalUserId("xyz", "qef4thyt"), ExternalUserId("xyz", "qaz1dfgh"))
You can set up to five different external ids for current per event.
The SDK can track active time for page view events. For example - how long user had read specific article in the application. That can be easily done by using following function with event's name:
val event = builder.eventId("my-unique-id").build()
...
CxenseSdk.getInstance().trackActiveTime(event.eventId)
This event id is used internally only as key
The SDK allows you tracking content views by leveraging page view events. If your content do not have own URL (streamed content for example), but can be identified through some alphanumerical string, than you can track it's views too. Just set your content id to PageViewEvent.Builder
instance:
builder.contentId("897983476897356")
contentId
&location
properties are mutually exclusive. Please use either content identifier or page's address.
Performance events are events that means that user have performed some action in the application like 'Add item to the cart'. Performance events are aggregated by Piano DMP and all collected events of that type are available for analysis in DMP's web interface.
Use PerformanceEvent
class to track performance events in your application. Instances of the class can be easily created using PerformanceEvent.Builder
:
val builder = PerformanceEvent.Builder("[put your SiteId here]", "xyz-sample", "click", listOf(UserIdentity("xyz", "abcdefg12345")))
PerformanceEvent.Builder
class provide set of methods through which you can easily configure data that will be applied on performance event. Here is example of event's configuration (pay attention that method's invocations can be chained):
// Custom parameters on performance events have different structure than custom parameters of page view events.
// They have more complex structure and can be configured through 'DMPCustomParameter' class usage.
builder.addCustomParameters(CustomParameter("val", "0.34"), CustomParameter("campaign", "ad"))
// Identifier of segments are also can be provided to event's data.
.addSegments("123", "456")
Also, reported page view events can be attached to performance events. That is how that can be implemented:
val event = ... // get instance of PageViewEvent that must be attached to PerformanceEvent
CxenseSdk.getInstance().pushEvents(event)
// while constructing new instance of PerformanceEvent using PerformanceEvent.Builder just use following method to associate pv event with perf event:
...
.prnd(event.rnd)
...
To schedule event's sending on closest dispatch loop's iteration, use following method for performance events too:
CxenseSdk.getInstance().pushEvents(...)
Piano DMP & Content SDK automatically tracks meta information (like name and version) of application in which it is used by default. This will help you in understanding which version of the application had generated events and will help you in distinguishing traffic came from application from traffic came from web (if both platforms are supported in your product). Automatic tracking uses custom parameters to deliver meta information and can be disabled by setting to false following property of your
CxenseConfiguration
object:CxenseSdk.getInstance().configuration.autoMetaInfoTrackingEnabled = false
val cxenseSdk = CxenseSdk.getInstance()
// Get default user id. Current value is Advertising ID
val defaultUserId = cxenseSdk.defaultUserId
// Get current user id
val currentUserId = cxenseSdk.userId
// Set custom user id
cxenseSdk.userId = customUserId
If user consent is required in your application you should set consentRequired
to enable checking consents before event processing. Add additional consent flags after requesting it from user.
CxenseSdk.getInstance().configuration.apply {
consentSettings
.consentRequired(true)
.pvAllowed(true)
.segmentAllowed(true)
consentSettings.version = 2 // required for deviceAllowed and geoAllowed
}
Pay attention that if given consent options may affect not only data processing on backend, but also affect SDK's functionality.
Possible consents (only if consentRequired
is true):
Option | Description |
---|---|
pvAllowed | Allows page view tracking, DMP event tracking and browsing habit collection to understand a user’s interests and profile. |
recsAllowed | Allows personalisation of content recommendations and suggested content based on user interests and browsing habits |
segmentAllowed | Allows audience segmentation, processing of browsing habits and first party data to include users in specific audience segments. |
deviceAllowed | Covers any data, such as user-agent or browser information, that can be used to identify what device the user is using. If consent is not given, certain activities that require device information will be limited, since the data will not be present. |
geoAllowed | Сovers looking up geolocation data on end-users. Practically, this covers geolocation via IP lookups to some precision. It also will reject the use of latitude and longitude parameters in page view requests. |
adAllowed | Allows targeting advertising based on browsing habits and audience segmentation. |
CxenseSdk.getInstance().getUserSegmentIds(listOf(UserIdentity(type, id), UserIdentity(type2, id2)), listOf(siteGroupId, siteGroupId2), object : LoadCallback<List<String>> {
override fun onSuccess(data: List<String>) {
// do something with data
}
override fun onError(throwable: Throwable) {
// do something with error
}
})
CxenseSdk.getInstance().getUser(UserIdentity(type, id), object : LoadCallback<User> {
override fun onSuccess(data: User) {
// do something with data
}
override fun onError(throwable: Throwable) {
// do something with error
}
})
Configure context for widget
// there is the only required parameter for a context: URL
val widgetContext = WidgetContext.Builder("YOUR_URL")
// override other optional properties
.referrer(...)
.keywords(...)
.neighbors(...)
.parameters(...)
.build()
Fetch recommendations
CxenseSdk.getInstance().loadWidgetRecommendations(widgetId, widgetContext, callback = object : LoadCallback<List<WidgetItem>> {
override fun onSuccess(data: List<WidgetItem>) {
// work with loaded items
}
override fun onError(throwable: Throwable) {
// process error
}
})
Track item click
val cxenseSdk = CxenseSdk.getInstance()
// for item
cxenseSdk.trackClick(item, object: LoadCallback<Unit> {
override fun onSuccess(data: Unit) {
// success response from server
}
override fun onError(throwable: Throwable) {
// process error
}
})
// for custom url
cxenseSdk.trackClick(url, object: LoadCallback<Unit> {
override fun onSuccess(data: Unit) {
// success response from server
}
override fun onError(throwable: Throwable) {
// process error
}
})
Get event sending queue status
CxenseSdk.getInstance().queueStatus
Set callback for each dispatch
CxenseSdk.getInstance().setDispatchEventsCallback { statuses ->
statuses.filter { it.exception != null }.forEach {
Log.e(
TAG,
String.format(Locale.getDefault(), "Error at sending event with id '%s'", it.eventId),
it.exception
)
}
}
Force flush event queue
CxenseSdk.getInstance().flushEventQueue()
val id = "john.doe@example.com"
val type = "xyz"
val cxenseSdk = CxenseSdk.getInstance()
// read external data for all users with type
cxenseSdk.getUserExternalData(type, callback = object : LoadCallback<List<UserExternalData>> {
override fun onSuccess(data: List<UserExternalData>) {
// do something with data
}
override fun onError(throwable: Throwable) {
// do something with error
}
})
// update external data for user
val userExternalData = UserExternalData.Builder(UserIdentity(type, id))
.addExternalItems(ExternalItem("group1", "item1"), ExternalItem("group2", "item2"), ExternalItem("group3", "item3"))
.build()
cxenseSdk.setUserExternalData(userExternalData, object : LoadCallback<Unit> {
override fun onSuccess(data: Unit) {
// do something at success (data not available)
}
override fun onError(throwable: Throwable) {
// do something with error
}
})
// delete external data for user
cxenseSdk.deleteUserExternalData(UserIdentity(type, id), object : LoadCallback<Unit> {
override fun onSuccess(data: Unit) {
// do something at success (data not available)
}
override fun onError(throwable: Throwable) {
// do something with error
}
})
Updating external data will overwrite any existing data associated with the user. Hence, if the intention is to add a new key-value to the user without erasing the existing information, the client should first read the currently stored data, add the new data to the already stored data, and upload the new profile consisting of both the previously stored information,and the new information.
Piano DMP & Content SDK provides also support of Persisted Queries. It contains two methods in its public API that are work with the queries:
/**
* Executes persisted query. You can find some popular endpoints in {@link CxenseConstants}
*
* @param url API endpoint
* @param persistentQueryId query id
* @param data data for sending as request body
* @param callback callback for response data
* @param <T> response type
*/
@JvmOverloads
fun <T : Any> executePersistedQuery(
url: String,
persistentQueryId: String,
data: Any? = null,
callback: LoadCallback<T>
)
It is generic API and you can use it with any API endpoint, that supports persisted queries,if you provide correct response object definition. SDK includes urls and definitions for popular API endpoints.
Sample usage:
We have persisted query for /profile/user/segment
with request body {"identities":[{"id":"0","type":"cx"}],"siteGroupIds":["1234567890123456789"]}
and mutable fields: identities
val data = UserSegmentRequest(listOf(UserIdentity(type, id)), null)
CxenseSdk.getInstance().executePersistedQuery(CxenseConstants.ENDPOINT_USER_SEGMENTS, "some_persistent_id", data, object : LoadCallback<SegmentsResponse> {
override fun onSuccess(data: SegmentsResponse) {
// do something with segmentsResponse.ids
}
override fun onError(throwable: Throwable) {
// do something with error
}
})