Contentsquare Android Integration
Get Started
Add Contentsquare to your app
Our Android SDK is shipped as an Android library (AAR) which you need to add as a dependency to your Gradle file.
How to include it
For distribution of our API we use Maven Central Repository which is supported by the Android build system by default. To add our SDK (or library), add the following line to your application's dependency list.
[tab] Groovy
implementation "com.contentsquare.android:library:4.18.2"
[tab] Kotlin
implementation("com.contentsquare.android:library:4.18.2")
Start the SDK
You do not need to do anything to start the SDK. Now that the SDK is a dependency of your app, it will autostart itself when your application starts.
Disabling Automatic SDK Initialization
Set the com.contentsquare.android.autostart
flag to false in your AndroidManifest.xml
file.
<application>
...
<meta-data
android:name="com.contentsquare.android.autostart"
android:value="false"
/>
...
</application>
Then, you can start the Contentsquare SDK by calling the start()
method of the Contentsquare class.
Contentsquare.start(context)
This initialize function is called automatically on app startup if the com.contentsquare.android.autostart
flag is not listed in the AndroidManifest.
This function should be called before the first activity is created. Call this function in the onCreate()
method of a custom Application subclass:
import android.app.Application
import com.contentsquare.android.Contentsquare
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
// Start Contentsquare SDK
Contentsquare.start(this)
}
}
Validate SDK integration
Start your application, you should see the SDK print a log like this one:
I/CSLIB: Contentsquare SDK 4.18.2 starting in app: your.application.package t: [timestamp]
If the SDK does not seem to work, it might mean that your app's package name is not tied to any Contentsquare project. In that case, you will have to communicate all variants of your app's identifier to your Contentsquare contact to be added as a project.
Now that the SDK runs in your app, you will want to implement calls to our SDK to track screenviews, transactions, and more. Keep following along this guide to know how.
Sample app
For best implementation practices of our library, explore the Contentsquare for Android sample app.
In-app features
Alongside its tracking capabilities, the SDK embeds some features aimed at Contentsquare users such as Snapshot Capture and SDK Logs.
Enable in-app features
β οΈ In order to enable in-app features within your app, you have to first make sure your app is launched in the background. To do so, simply start it and press the Android home button. Then, follow the appropriate method described as follows.
On a device: scan the QR code
If you have access to the Contentsquare platform, you can open the in-app features modal from the menu and scan the QR code with your phone.
On an emulator: use the ADB command
If you are using an emulator, you can use the ADB command to enable in-app features.
If you have access to the Contentsquare platform, you can open the in-app features modal from the menu and select "Copy this ADB command".
It will look like this:
adb shell am start -W -a android.intent.action.VIEW -d "cs-{{packageName}}://contentsquare.com?activationKey={{uniqueActivationKey}}\&userId={{userId}}"
If you don't have access to the Contentsquare platform, you can ask the Contentsquare team to share the link with you.
Debugging and Logging
Contentsquare provides Logging capabilities that allow you to see the raw event data logged by your app in Android Studio or in the Contentsquare platform. This is very useful for validation purposes during the instrumentation phase of development and can help you discover errors and mistakes in your analytics implementation and confirm that all events are being logged correctly.
Viewing logs in Android Studio
By default, almost all logs are disabled. There is only one log that is always visible to notify that the SDK has started:
I/CSLIB: Contentsquare SDK 4.18.2 starting in app: your.application.package t: [timestamp]
In order to enable all logs, you simply need to activate in-app features. Logging is directly linked to in-app features state: it starts when in-app features is enabled and stops when you disable in-app features.
To view logs:
- Plug your Android Phone to your Computer (or use emulator)
- Start the Android Studio app
- Filter logs on
CSLIB
Viewing logs in the Contentsquare platform
To view logs directly on the platform, you can use Log visualizer. Log visualizer is a helper tool to see SDK logs without logging tools. It requires having platform access for your project and enabling in-app features. See the SDK Log Visualizer Help Center Article for more information.
Snapshot Capture
In order to unlock the full data-visualization capabilities of Contentsquare, the SDK provides a way to capture snapshots of your app screens. These snapshots can only be taken by Contentsquare's users on their device. They are not captured from your end-users device. It means your Personal Data is safe, as long as you use a test user account.
Snapshots are used in the Zoning Analysis module to look at zone-level metrics (Tap rate, Swipe rate...):
Static snapshot
By default, when a snapshot is captured, the SDK uses accessible style related view properties to render them later in the Zoning Module. However, sometimes it is not able to render the screen with fidelity. It can be due to different reasons such as using custom properties to handle style (which the SDK cannot access).
To workaround these constraints, you can choose to switch to static snapshot. In this mode, the SDK will capture a bitmap of the visible screen instead of relying on style properties. The trade-off is that you will be required to capture multiple snapshots for screens that can be scrolled (since only the visible part is captured).
Enable static snapshot
To enable static snapshot, you must have enabled in-app features:
- Long press on the snapshot button to open the Contentsquare settings
- Toggle on the static snapshot option
- Tap on the arrow back to close the Contentsquare settings
- The snapshot button has changed (black background and white camera icon)
Static snapshot is enabled. Simply tap on the button to capture 'static snapshot'.
Privacy
The Contentsquare SDK is compliant with the Play Store Privacy guidelines as well as with the EU General Data Protection Regulation (GDPR).
Consult our Privacy Center and Privacy Policy.
Personally Identifiable Information
User ID
We do not use Advertising ID or any ad related or third-party information to identify the user. The SDK generates a unique user ID (UUID) (random hash) which is specific to users on their device. Contentsquare can't identify a user across devices. We donβt persist the UUID when the app is deleted and re-installed. The SDK generates a new UUID after install or re-install. This user ID is not shared with any third parties.
What is not collected
- Advertising ID or any other ad related user identifier
- Passwords from password fields
- Geo location information
- Information from contacts, emails, or any other user identifiable information.
- Text from labels, buttons, and any other widget which contains text
- Accessibility information from any widget which the user interacts with
- Labels printed on the screen of any kind.
Handling User Consent
Even though Contentsquare only collects usage data on your app, the SDK will consider every new user to be opted-out. To start tracking, the SDK Opt-in API must be called.
You are responsible for creating the UI asking users for their consent and allowing them to manage their privacy settings and then calling the appropriate Contentsquare following functions (opt-in, opt-out, forget me...).
Opt-in
Use the Opt-in API to get user consent. Calling this API will generate a user ID and initiate tracking.
import com.contentsquare.android.Contentsquare
// To opt-in of CS Tracking
Contentsquare.optIn(@NonNull Context context);
Opt-Out
Permanently breaking the link and stopping all data collection.
When this API is called, tracking stops immediately, all settings are reset (Session number, Page number...) and all files and directory regarding to Contentsquare are deleted. This means that the user ID is deleted. The SDK will never track and collect any data from the user's phone unless the Opt-in API is called again.
import com.contentsquare.android.Contentsquare
// To opt-out of CS Tracking
Contentsquare.optOut(@NonNull Context context);
Forget me
Permanently breaking the link between the collected data and actual user.
This resets all settings and deletes all files and directories from the user's device (User ID is deleted). If user is opted in, next time user starts the app, the SDK will re-start its collection mechanisms as if this was the first ever run for a new user, under a new user id. Configurations will be fetched from the server and application tracking will be on.
import com.contentsquare.android.Contentsquare
Contentsquare.forgetMe()
Give me my data
We allow the client to provide to their users their Contentsquare user ID.
This ID is a non binding identifier which can be used to make a data request to Contentsquare. You are able to get an ID only if the user is not Opted-out.
import com.contentsquare.android.Contentsquare
// Get CS User ID
Contentsquare.getUserId()
Stop / Resume Tracking
Although we do not gather any humanly readable text from the user's screens, we understand that there may be some areas that you want to completely exclude from tracking. For this reason we also support stopping and resuming the complete tracking mechanism.
import com.contentsquare.android.Contentsquare
// Stop tracking
Contentsquare.stopTracking()
// Resume tracking
Contentsquare.resumeTracking()
Stop gesture tracking
Gestures are tracked automatically. In specific cases, you might want to stop gesture tracking momentarily for some Activities. To do so, you can use our public API:
Contentsquare.doNotTrack(@NonNull Class<? extends Activity>... activitiesClasses) // will filter out the activities by their classes
In case you still need to track those activities but the automatic gesture tracking was not working we are offering you the possibility to do this manually by capturing and dispatching the GestureEvents through our Public API method:
class MainActivity : AppCompatActivity() {
..........
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
Contentsquare.consumeEvent(ev)
return super.dispatchTouchEvent(ev)
}
}
Disable user tracking across sessions
If you don't want to link the different sessions of a user to the same userID, you can follow these instructions to reset the userID at each app start:
-
Disable SDK auto-start
-
Implement the following:
class MyApplication : Application() { override fun onCreate() { super.onCreate() // Start Contentsquare SDK Contentsquare.start(this) Contentsquare.optOut(this) Contentsquare.optIn(this) } }
- Using the manual start method of the SDK will ensure that opt-out is called right after the start of the SDK (no event tracked in between).
- Calling opt-out will delete the previous userID.
- Calling opt-in will set a new one.
Track Screens
Contentsquare aggregates user behavior and engagement at the screen level. To do so, it is required to track screen transitions by calling a dedicated API. When the API is called, the SDK logs a screen_view
event that identifies the new screen with the screen name provided.
import com.contentsquare.android.Contentsquare;
//Sends Screenview
Contentsquare.send("screen_name")
Screen name handling
The screen name length is not limited on the SDK side. However, the limit is 2083 characters on the server side.
Screenview after app in background
Depending on your implementation, a screenview event might be sent after the app is put in background and then in foreground.
To do so, you will have to call Contentsquare.send(..)
in the onResume
lifecycle method of the activity (or fragment).
Implementation recommendations
General rules
From a functional standpoint, we expect a screenview to be sent:
- When the screen appears
- When a modal/pop-up is closed and the user is back on the screen
- When the app is put in the foreground (after an app hide)
Activities
To trigger a custom screenview each time the activity is visible place the tag in the onResume
method.
Fragments
To avoid double tagging on Fragments, make sure to include the tag in each ViewPager's onPageChangeListener
callback.
Popups
When tracking a popup as an isolated screen, use the API to send a screenview event onShow
. To re-trigger a screenview of the current activity below when the popup is dismissed, make sure to trigger a screenview event with the original Activity screenName on the popup DismissListener
.
Bottom sheets
When tracking a bottom sheet as an isolated screen, use the API to send a screenview event on view displayed. On its dismiss, make sure to trigger a screenview event with the original screen name on BottomSheetBehaviorCallback
state change (Usually when STATE_HIDDEN) to track the complete cycle.
Back navigation and navigation between screens
Make sure that screenview events will be triggered when a user will go to the previous screen. i.e. Home -> Profile -> Home, it is expected to have a screenview event for the Home screen that might be reached with the back navigation button. Trigger screenview event in a custom callback to catch the back events.
Redirecting the user to another screen (authentication, home) when closing the app/re-opening the app
For some apps, you might want to redirect users whenever they hide your app, for example for security purposes (bank apps, password managers, etc...). If that is the case, pay specific attention to the way screen_view
events are sent, in order not to track a screen which is not actually shown users.
How to name screens
As a general rule, keep distinct screen names under 100. As they are used to map your app in Contentsquare, you will want something comprehensive.
Separate words with space, dash or underscore characters
To generate screen names with more than one word, separate them using spaces, dashes or underscores.
For instance, use Home & Living - Home Furnishings
instead of for a sub-category in a retail app.homeLivingHomeFurnishings
Use screen template/layout names
As a general recommendation, use names referring to the screen template/layout rather than referring to the specific content (data). This will help:
- To keep the number of distinct screen names low and therefore make Contentsquare easier to use
- Remove the risk of sending Personal Data to Contentsquare
List of screen types falling into that category: Product detail, Event detail, Conversation/Chat, User profile...
Multiple layouts/states for one screen
In some cases, there will be screen that can have different layouts/states depending on the user context. In this situation, it would be interesting to append the layout/state value to the screen name. Examples:
- Home screen of a travel app adapting its layout on the user context:
State Screen name No trip planned Home - no trip
Trip planned Home - trip planned
Trip about to start Home - upcoming trip
Trip in progress Home - trip in progress
- Product detail screen of an e-commerce app with different layouts depending on the type of product:
State Screen name Default template Product detail
Template with suggested products Product detail - Suggestions
Template with bundled products Product detail - Bundle
Track Transactions
To associate a user's session with their potential purchases (and corresponding revenue), you must send the transaction via a dedicated API. For each transaction, we send:
- Price (mandatory)
- Currency (mandatory)
- Transaction ID (optional)
import com.contentsquare.android.api.Currencies
import com.contentsquare.android.api.model.Transaction
//Sends Transaction
val price = 430.00f
val id = "id"
//Currency
val currency = Currencies.EUR // or currency = "USD"
Contentsquare.send(Transaction.builder(price, currency).id(id).build())
Currency
The currency is conforming to the ISO 4217 standard. The currency can be passed either as "alphanumeric code" or "numeric code".
If the currency passed doesn't match the supported currencies, the SDK will send a currency value of -1
. It will be processed as the default currency of the project.
Track Dynamic Variables
General principles
Usage
Dynamic variables are additional information on the session that can be used to segment sessions.
For example, they can include information on the A/B Test variations displayed to the current user.
Limits
On the server side
- It is possible to save up to 40 distinct dynamic variable keys per screenview. If more are received, only the first 40 keys will be kept.
- If you are using the same key twice on the same screenview, the last value associated with the key will be collected.
On the SDK side
- Every dynamic variable is composed of a pair of key (max. 50 characters) and value (max. 255 characters string or number of type
Long
between 0 and 232 - 1). In case these maximums length are reached, the SDK will automatically trim the exceeding characters. - If key or value are empty, the SDK will instead send the literal string "cs-empty".
Defining dynamic variables
To define and send a dynamic variable, directly use the key and String/Long value:
Contentsquare.send("my key 1", "my value")
Contentsquare.send("my key 2", 10)
Type of the value β The value can be either a whole number or a string. For each case, available features won't be the same in the Contentsquare app:
- For whole numbers, you will be able to do some algebra. Example: sessions with dynamic variable key = "numberOfFriends" and value >= 10
- For strings, auto-completion and Regular Expression will be available. Example: sessions with dynamic variable key = "accountType" and value = "Premium"
Sending a dynamic variable for each session
You may want to send a dynamic variable for each session (like the user's country or store). While triggering the dynamic variable at app launch will cover most cases, it will not be enough. A session can start when the app is put in foreground, after staying in background for more than 30 minutes. See Session definition section for more information.
That is why we also recommend sending such dynamic variable every time the app enters foreground.
You can use the ProcessLifecycleOwner
Class to detect foreground and trigger a dynamic variable.
Track WebViews
To enable WebView tracking, it is required to build a JavaScript Bridge between the content of the WebView and the native SDK. To do so, you will have to implement the Contentsquare WebView JavaScript Tracking Tag in the web pages called in your app WebViews.
π WebView JavaScript Tracking Tag Documentation
Once the WebView Tracking Tag is implemented in the web pages, the following Public API interface needs to be implemented to establish the JavaScript Bridge:
CsWebViewManager.injectEventTrackingInterface(@NonNull WebView webView) // to start tracking the specific WebView
CsWebViewManager.removeEventTrackingInterface(@NonNull WebView webView) // to stop tracking and detaching from the specific WebView
For a better synchronization call methods by following the Activity/Fragment lifecycle callbacks in a symmetrical way:
At the Activity level, call "injectEventTrackingInterface" on the onResume
lifecycle method and "removeEventTrackingInterface" on the onDestroy
lifecycle method.
At the Fragment level, call "injectEventTrackingInterface" on the onViewCreated
and "removeEventTrackingInterface" on the "onDestroyView" lifecycle method.
Validate WebView tracking
For the moment, there is no way of validating the implementation on the native side only.
Validating the implementation on the web side
Once you arrive on the screen with the tracked WebView, you should see an initial confirmation log message:
WebView detected and WebView tracking enabled on native side
Specific logs are then emitted for:
- Page views fired by the WebView Tracking Tag in the same format as for screen views:
Screenview - Screen name: {{page name given}} - Screen number: 11
- Taps and swipes detected by the WebView Tracking Tag in the same format as for defaults taps and swipe but with the target matching an HTML DOM element value:
Tap - Target: {Last view info: div:eq(0)} - Unresponsive: false
Going further: If you still have a doubt, you can look for logs by filtering on WebViewEventProcessor
. Their presence will confirm that it is well implemented on both web and native sides.
Session Replay
In order to implement Session Replay, go to Mobile SDK Session Replay documentation.
Error Analysis π
In order to implement Error Analysis, go to Android SDK Error Analysis documentation.
Use Google Tag Manager
If you are using Firebase to track events such as screenviews or transactions, you can trigger these events for Contentsquare with minimal effort, thanks to Google Tag Manager.
Prerequisites
If you are following this Google Tag Manager integration process, you should already have followed the Google Tag Manager + Firebase setup as described in Google's Tag Manager + Firebase: Getting Started
You also need to integrate the Contentsquare SDK see section: Add Contentsquare to your app.
Screenview events
This section covers how to trigger a Contentsquare screenview event for every Firebase screenview event. Firebase allows you to automatically logs screenviews but also to trigger them manually for an exhaustive coverage of your app screens.
Variables
Contentsquare screenview events require to pass the screen name as a parameter.
Firebase has 2 ways to track screens:
1. Variable for automatically tracked screens
Firebaseβs screen_view
events have an _sc
parameter (for screen class) which we can use as parameter for screens that are automatically tracked.
We will create a variable called Auto Screenview with:
- The Variable Type set to Event Parameter
- The Event Type set to Custom Parameter
- The Event Parameter Key set manually to
_sc
2. Variable for manually tracked screens
Firebaseβs screen_view
events have a _sn
parameter (for screen name) which we can use as parameter for screens that are manually tracked.
We will create a variable called Manual Screenview with:
- The Variable Type set to Event Parameter
- The Event Type set to Custom Parameter
- The Event Parameter Key set manually to
_sn
We can also create variables for our own custom parameters, for example, we can send a screen_view
event like this:
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.SCREEN_VIEW) {
param(FirebaseAnalytics.Param.SCREEN_NAME, "MyScreenName")
param("is_user_logged_in", "true")
}
and create a variable called Is User Logged In with:
- The Variable Type set to Event Parameter
- The Event Type set to Custom Parameter
- The Event Parameter Key set manually to
is_user_logged_in
Check out Configure variables in Tag Manager for more information.
Screenview trigger
We will need to tell Google Tag Manager when to send a Contentsquare screenview, and for that we need to create a Trigger. Firebase sends screenviews as events of name screen_view
, so we need to create a trigger for that event.
- Name your trigger
Screenview Trigger
- Trigger Type should be set to Custom
- Trigger should fire on Some Events
- For the Event, the Event Name should be equal to
screen_view
Create the custom function Tag
In Google Tag Manager, a Tag is what configures what happens when some trigger is activated. It will be automatically linked to a custom class which we will create in a later step.
The Tag Type should be a Function Call.
You will also need to fill in the Class Path field with a class path of your liking, typically related to what the tag is about (here we chose com.myapp.ContentsquareScreenviewTag
), which you will re-use later.
Adding the trigger and the variables to the Tag
Edit the ContentsquareScreenviewTag
Tag:
- Add an argument with
- Key:
auto_screen_name
- Value:
{{Automatic Screenview Name}}
- Key:
- Add an argument with
- Key:
manual_screen_name
- Value:
{{Manual Screenview Name | Is User Logged In}}
or{{Manual Screenview Name}}
- Key:
- Add Screenview Trigger as the Tag's trigger
It should look like this now:
Create the custom function Tag class
In your app code, create a class of the same name as the Class Path
you configured in the GTM interface, for example ContentsquareScreenviewTag
. This is how GTM connects the configuration and the actual class to trigger. This class is used to call the Contentsquare screenview tracking function as follows:
@Keep
class ContentsquareScreenviewTag : com.google.android.gms.tagmanager.CustomTagProvider {
override fun execute(map: MutableMap<String, Any>?) {
val autoScreenName = map?.get("auto_screen_name") as String?
val manualScreenName = map?.get("manual_screen_name") as String?
if (autoScreenName != null) {
Contentsquare.send(autoScreenName)
Log.w("GTM", "-------> $autoScreenName")
}
if (manualScreenName != null) {
Contentsquare.send(manualScreenName)
Log.w("GTM", "-------> $manualScreenName")
}
}
}
Now, every time Firebase sends a screenview event, Contentsquare will send one as well.
Transaction events
A Contentsquare transaction event can also be triggered for every Firebase purchase
event (See Firebase documentation).
To do so, you will need to follow the setup described below.
Variables
We have to create at least two variables in order to be able to build a transaction event, Value and Currency. Optionally, it is also possible to pass a transaction identifier, meaning you would need to create an additional variable in order to be able to use it.
Value (mandatory)
Currency (mandatory)
Transaction ID (optional)
Trigger
Create the following custom trigger.
Tag
Create a Function call Tag called ContentsquareTransactionTag
.
You will also need to fill in the Class Path field with a class path of your liking, typically related to what the tag is about (here we chose com.myapp.ContentsquareTransactionTag
), which you will re-use later.
Pass the two to three variables created earlier as arguments (skip the id
line if you do not want to use it) and set its trigger to the one you just created.
Implementing the function call
We need to create a ContentsquareTransactionTag
class, and have it adhere to the CustomTagProvider
interface to link it to the Google Tag Manager custom Tag of the same name.
Take good note that in order to be able to create and send a Contentsquare Transaction successfully, when you create a Firebase purchase event you will have to at least pass values for VALUE
and CURRENCY
, and can optionally pass a value for TRANSACTION_ID
(see reference here).
@Keep
class ContentsquareTransactionTag : com.google.android.gms.tagmanager.CustomTagProvider {
override fun execute(map: MutableMap<String, Any>?) {
val value = map?.get("value") as String?
val currency = map?.get("currency") as String?
if (value != null && currency != null) {
val transactionBuilder = Transaction.builder(value.toFloat(), currency)
val id = map?.get("id") as String?
if (id != null) {
transactionBuilder.id(id)
}
Contentsquare.send(transactionBuilder.build())
}
}
}
Now, every time Firebase sends an ecommerce_purchase
event, Contentsquare will send one as well.
Dynamic variable events
We can send values from any Firebase event as a Contentsquare dynamic variable:
firebaseAnalytics.logEvent("addToCart") {
param("product_count", 12)
param("product_name", "ProductA")
}
Variables
The addToCart
event has parameters product_count
, product_name
, we will create variables called Product Count and Product Name with:
- The Variable Type set to Event Parameter
- The Event Type set to Custom Parameter
- The Event Parameter Key set manually to
product_count
/product_name
Check out Configure variables in Tag Manager for more information.
Triggers
We need to create a Trigger to tell Google Tag Manager when to send a Contentsquare dynamic variable, so we will create one for the event addToCart
.
- Name your trigger
AddToCartTrigger
- Trigger Type should be set to Custom
- Trigger should fire on Some Events
- For the Event, the Event Name should be equal to
addToCart
Create the custom function Tag
We need to create a function tag AddToCartDynamicVar
- The Tag Type set to Function Call
- The Class Name set to DynamicVarTag (we'll create this class later)
- Add two arguments
- Key:
cs_dynamic_var_key
, Value:Product
- Key:
cs_dynamic_var_value
, Value:{{Product Name}}: {{Product Count}}
- Key:
- The Triggering set to AddToCartTrigger
It should look like this now:
Create the custom function Tag class
In your app code, create a class of the same name as the Class Name
you configured in the GTM interface, for example DynamicVarTag
. This is how GTM connects the configuration and the actual class to trigger. This class is used to call the Contentsquare dynamic variable function as follows:
@Keep
class DynamicVarTag : com.google.android.gms.tagmanager.CustomTagProvider {
override fun execute(parameters: MutableMap<String, Any>) {
val dynamicVarKey = parameters["cs_dynamic_var_key"] as? String ?: return
when (val dynamicVarValue = parameters["cs_dynamic_var_value"]) {
is String -> Contentsquare.send(dynamicVarKey, dynamicVarValue)
is Long -> Contentsquare.send(dynamicVarKey, dynamicVarValue)
}
}
}
Now, every time Firebase sends a addToCart
event, Contentsquare will send a dynamic variable key: "Product", value: "ProductA: 12"
.
Use Tealium
Our partner Tealium has developed a remote command module to integrate with the Contentsquare SDK. This solution leverages the convenience of iQ Tag Management to configure a native Contentsquare implementation without having to add Contentsquare-specific code to your app.
Follow instructions on Tealium's documentation: Remote Command for Contentsquare.
How the SDK works
Initialization
The way our SDK works is by auto-starting with the application launch and attaching to the current process in order to intercept the events and gestures we are interested in.
Configuration
Once started, our SDK fetches it config from our servers, and then depending on the segmentation size (defined by our team when you sign a contract) it will start collecting data from system and user events it detects from the runtime.
Tracking
The SDK monitors the application lifecycle events and the view hierarchy, and generates analytics data from the behavior of the app, the content of the screen and the interaction of the user. These events are then locally stored, and eventually sent to our servers in batches. We then aggregate that data to create usable visual information into our Web Application, which you use to gather insights.
Sending data
Analytics data are sent in batches of maximum 50 events. Requests are triggered when network conditions allow for the server to be reached and:
- The current batch of events has reached 50
- Or the app is put in background
Requests are triggered from a dedicated thread, which has background priority.
Session definition
A session represents a single period of user interaction in the app.
In Contentsquare, a session ends when the user has spent a certain amount of time outside the app. The SDK checks this when an app start
or app show
event is detected. This time spent outside the app, ending a session is set to 30 minutes by default. But it can be changed if it is requested.
If the app is put in the background or killed (intentionally by the user or by the OS) but the user comes back within 30 minutes, it will not end the session. These events are considered to be part of a session.
If the app is put in the background or killed (intentionally by the user or by the OS), it will not end the session. These events are considered to be part of a session.
Collected data points
- The events are in JSON format, which lists the properties and their description in comment.
- Events are sent to the server in batches.
- A batch is a subset of events originating from one session. A session can send events in more than one batch.
- The batch size is defined in a run configuration file specific to each app (50 events by default).
Requests meta data
Each batch has a common header, a set of device specific, and repeatable information and an array of events, which is immutable. The header is composed at the moment of sending, so it has to be constant info ONLY. The array in the payload is data stored at the moment of collection.
The structure of the batches of events will have the following format:
{
pid:8, // int - ProjectId - represented in the mobile config format
uid:"ac1529a7-59f6-49d9-b254-6f0a22f57284", // String - uuid Unique user ID
dt:4, // int - device type (loosely enforced enum - [sdk-phone : 4, sdk-tablet :5])
dma:"Samsung", // String - device manufacturer. This property is optional and if by any chance the device model cannot be determined it will be dropped from the Json object.
dmo:"SG", // String - device model. This property is optional and if by any chance the device model cannot be determined it will be dropped from the Json object.
os:"10.0", // String - os version (Android version name 8.0, 9.0, 10.0)
l:"en_US", // String - Language in iso-639 format https://www.ibabbleon.com/iOS-Language-Codes-ISO-639.html
tz:"Europe/Paris", // String - timezone https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
now:1522159618000, // long - timestamp when the batch was sent
to:{ // object - type origin of the event
an:"appname" // String - application name
st:"sdk-android", // String - type of sdk
sf:"release", // string - sdk flavor/variant [release/debug/god]
},
r:{ // Device - resolution.
w:1080, // int - width
h:1920 // int - height
d: 1.5 // The device density as float. Its values can vary between (0.75 and 10)
},
pl:[] // JSon array payload - list of event objects, see below
}
Events
Events meta data
All events have a common first part (don't confuse it with the header explained above). This common section is data which is common for all events by type but is gathered at the moment the event occurred. This common section is a part of the actual event payload.
{
upt:1522, // long - system update in milliseconds
euid:"euid", // String - event UUID
url:"app-and://identifier/mainPath/secondaryPath?title=screeName", // String - screenName is passed in the title query Parameter
scn:4, // int - the number of screens already shown in the session
c:3, // int - connectivity type [-1 - offline, 0 - error, 1 - wifi, 2 - 2g, 3 - 3g, 4 - 4g]
ci:"verizon", // String - carrier_id when user is on mobile network
o:1, // int - orientation [0 = portrait, 1 = landscape]
vo:{ // object - version origin of the event
sv:"2.1", // string version of the sdk
sb:4, // int - sdk build number
av:"appVersion", // String - application version
af:"appFlavor" // String - application string - [release/debug/god]
ab:1 // int - application build number
},
sn:1, // int - session id (positive int)
t:12894726459435 // long - timestamp of the event (in milliseconds)
}
All event specific properties are appended to this JSON object.
App Start
This event describes the absolute start of the app.
The trigger for this event is the absolute start of the app.
This is the first event sent when the SDK is invoked.
ea:0 // int - event action - defined above
App Show
This event is sent when the user brings the app in the foreground (switching from another app, exiting lock screen, etc.). This event means the app is focused.
ea:1 // int - event action - defined above
App Hide
This event is sent when the user exit (minimizes) the app, or switches to something else . This event means the app is not focused and the session might end, but we have not completely ended the session yet, as the user might return.
ea:2 // int - event action - defined above
Screenview
Everything starts with a View event. This is an event which describes the equivalent of a "page view" in the web. This event is sent when the Track Screen API is called.
ea:4 // int - event action - defined above
st:"title", // String - screen title
sl:34543 // int - (load time) screen load time (diff between last action and this event)
Tap
Single Finger gesture event, when the user is interacting with a loaded screen. This is an event which describes the equivalent of a "click" in the web. This event is defined by the following sequence of touch events:
- Touch Down -> N x Touch Move -> Touch Up
ea:6 // int - event action - defined above
tvp:"[root]>view#id>view2>", // String - target view path
tvid:"btn_ok", // String - target view id
ur: true, // boolean - was this a "responsive" touch event (the target view had a handler)
Long press
Single Finger gesture event, when the user is interacting with a loaded screen.
This event is defined by the following sequence of touch events:
- Touch Down β N x Touch Move β Touch Up
- Duration: > 500ms
- Distance: < 24 dp
ea:8 // int - event action - defined above
tvp:"[root]>view#id>view2>", // String - target view path
tvid:"btn_ok", // String - target view id
Drag (Slow Swipe)
Single Finger gesture event, when the user is interacting with a loaded screen.
This event is defined by the following sequence of touch events:
- Touch Down β N x Touch Move β Touch Up
- Distance: > 48 dp
- Finger Velocity < 100 dp/s
ea:9 // int - event action - defined above
tvp:"[root]>view#id>view2>", // String - target view path
tvid:"btn_ok", // String - target view id
fd: 3, // int - finger direction - [1,2,3,4,5] -> [up, down, left, right, complex_pattern]
tvd:100, // int - target view distance dragged
tvv:100 // int - target view velocity while dragging dp/s
Flick (Fast Swipe)
Single Finger gesture event, when the user is interacting with a loaded screen.
This event is defined by the following sequence of touch events:
- Touch Down β N x Touch Move β Touch Up
- Distance: > 48 dp
- Finger Velocity > 100 dp/s
ea:10 // int - event action - defined above
tvp:"[root]>view#id>view2>", // String - target view graph path
tvid:"btn_ok", // String - target view id
fd: 3, // int - finger direction - [1,2,3,4,5] -> [up, down, left, right, complex_pattern]
tvd:100, // int - target view distance scrolled
tvv:100 // int - target view velocity while scrolling dp/s
Transaction
To track transactions we provide a public API which can send a transaction object (see section Track Transactions). This object must contain the following parameters:
ea:16, // int - event action - defined above
tr:{ // a json object with different properties defining the transaction made
vl: 20.0, // mandatory - float - the value of the transaction
cu: 978, // mandatory - int - the ISO 4217 code of the currency of the transaction
id: "32akjkljklJ575775" // optional - string - an id for the transaction
}
Dynamic variables
To track dynamic variables we provide a public API (see section Dynamic variables).
// Dynamic variable event with a string value
ea:18, // int - event action - defined above
k:"key", // String - Custom key assigned by client.
v:"value" // String - Custom value assigned by client.
// Dynamic variable event with a number value
ea:19, // int - event action - defined above
k:"key", // String - Custom key assigned by client.
v: 2344 // Integer - Custom value assigned by client.
Security
Transmission and hosting
Our server uses HTTPS to makes sure that data is encrypted in transport.
Compatibility
- Programming languages: The Android SDK supports and tracks content on any screen developed in Java or Kotlin.
- Android version: we support Android SDK API Level 19 (Android 4.4 - KitKat) and later
- React Native: See our React Native Bridge Documentation
- Other hybrid apps: we do not officially support other JavaScript frameworks such as Ionic, Cordova, etc.
Java versions
This version has been compiled on Java 11 (Bytecode version 55).
Kotlin versions
Contentsquare Android SDK is always compiled with the latest Kotlin version.
We use Kotlin Compatibility mode flags to be backward compatible with two previous Kotlin versions and one next version.
If you use a Kotlin version lower than the last one, you must exclude the Kotlin stdlib
from our SDK.
implementation ("com.contentsquare.android:library:4.18.2") {
exclude group: "org.jetbrains.kotlin", module: "kotlin-stdlib-jdk8"
}
Dependencies
The Contentsquare SDK has a few dependencies. We try to be up-to-date as much as possible to avoid conflict issue.
Handling conflicts
We may sometimes have conflicts between your app dependencies VS SDK dependencies. In this case, you can exclude from our SDK any dependency we pull and use your version instead.
Example with excluding recyclerview
dependency
If you have a version of the recyclerview
in your app which is older than the one we use, you can use the following to exclude it in your Gradle file.
implementation ("com.contentsquare.android:library:4.18.2") {
exclude group: "androidx.recyclerview", module: "recyclerview"
exclude group: "androidx.swiperefreshlayout", module: "swiperefreshlayout"
}
By doing this, you skip the recyclerview
and swiperefreshlayout
dependencies provided by our SDK.
Dependencies list
Package | Name | Version |
---|---|---|
androidx.appcompat | appcompat | 1.6.1 |
androidx.annotation | annotation | 1.5.0 |
androidx.preference | preference-ktx | 1.2.0 |
androidx.recyclerview | recyclerview | 1.3.0 |
androidx.swiperefreshlayout | swiperefreshlayout | 1.1.0 |
androidx.lifecycle | lifecycle-process | 2.4.1 |
Known limitations and recommendations
Native UI Elements are not supported
Symptom:
- You capture snapshots but main elements of the screen are not appearing.
- SDK does not log any gesture events when interacting with some native UI elements.
Explanation: Native UI Elements such as Bottom Sheets, Dialogs, Menus are currently not supported by the SDK.
Gestures not attached to the expected view (Layout visibility)
Symptom: In some cases, we found that gestures tracked by the SDK are not attached to the right UI element of the screen. These gestures will be attached to an invisible view.
Explanation: The reason to this is that when the SDK attaches the gesture to a UI element, it generates a path regarding the visibility of the view when traversing the view hierarchy. If a view is visible and it matches the coordinates on where the gesture was performed it will be taken into account. Most of the time, the issue is that a layout is still present in the screen even if there is no content.
Best Practice: When designing your application UI, do not let any layout stray along if is not used. If a layout that contains widgets like progress bar, remember to set its visibility to GONE
or INVISIBLE
after its purpose is served.
Impact on performances
We always strive to be non-intrusive, and transparent to the developers of the client app. We apply this rule on the performance as well. These are the technical specifics we can share on performance, if you have any questions feel free to reach out to us.
The performances results were obtained under the following conditions:
Condition | Value |
---|---|
Device model | Pixel 2 |
Android version | 30 (Android 11) |
Property | Value |
---|---|
SDK size | 1036 KB |
Nbr. of methods | 4739 |
Nbr. of fields | 2740 |
RAM usage | <4 MB |
SDK CPU peak on event | <10 % |
Data transmitted over network for a default batch size of 50 events. The size of the batch can be customized if needed. | ~25kb |
Performance test procedure
CPU Usage Test | RAM Usage Test | Memory Leak Test |
---|---|---|
We are profiling our Demo app with and without the SDK dependency by following the same use case. | We are profiling our Demo app with and without the SDK dependency by following the same use case. | We are profiling our demo app and we are using the Adb Monkey tool to create random user input events |
We are measuring the CPU peak for both versions of the app and we are computing the difference | We are measuring the RAM usage peak for both versions of the app and we are computing the difference | During this time we are monitoring the RAM behavior for any anomaly. We are using the Memory Profiler to track/fix the issue |
Troubleshooting
App crashes when capturing snapshot (hardware bitmap)
While capturing a snapshot you may experience a crash in some situations:
E/AndroidRuntime: FATAL EXCEPTION: main
[...]
java.lang.IllegalArgumentException: Software rendering doesn't support hardware bitmaps
If you use Hardware bitmap, or your image library use hardware bitmap by default (example: coil), the default snapshot method does not support it. To overcome the issue, you can enable the Static snapshot method which supports Hardware bitmaps.
Requests are failing
In order for Contentsquare to work, you need to make sure the following endpoints are not blocked by your network (VPN):
Request | Endpoint | Detail |
---|---|---|
Config file | https://mobile-production.content-square.net | See Configuration |
Analytics data (EU) | https://m.csqtrk.net | See Sending data |
Analytics data (US) | https://m-aus1.contentsquare.net | See Sending data |
Session Replay data (EU) | https://ka-aeu1.contentsquare.net | See Session Replay requests |
Session Replay data (US) | https://ka-aus1.contentsquare.net | See Session Replay requests |
Snapshot (EU) | https://s.contentsquare.net | See Snapshot capture |
Snapshot (US) | https://s-aus1.contentsquare.net | See Snapshot capture |
Changelog
Related Links
π Mobile SDK Homepage
π WebView Tracking Tag Documentation
π React Native Bridge Documentation