Czech Digital Measurement Android Simplified SDK
From Engineering Client Portal
Overview
The Nielsen SDK is one of multiple framework SDKs that Nielsen provides to enable measuring linear (live) and on-demand TV viewing using TVs, mobile devices, etc. The App SDK is the framework for mobile application developers to integrate Nielsen Measurement into their media player applications. It supports a variety of Nielsen Measurement Products like Digital in TV Ratings, Digital Content Ratings (DCR & DTVR), Digital Ad Ratings (DAR), Digital Audio. Nielsen SDKs are also equipped to measure static content and can track key life cycle events of an application like:
- Application launch events and how long app was running
- Time of viewing a sub section / page in the application.
Prerequisites
To start using the App SDK, the following details are required:
- App ID (appid): Unique ID assigned to the player/site and configured by product.
If you do not have any of these prerequisites or if you have any questions, please see Contact list for Czech Republic.
Simplified SDK API
As part of making the SDK more user friendly and reduce the number of app integration touch points, Nielsen has designed a simple interface to pass metadata to the sdk while reducing the number of API calls. The new trackevent()
API has been implemented as a wrapper for the existing SDK and will be responsible for handling new API calls, performing validation, and translation of new API calls to the existing Nielsen App SDK API methods. Applications which are already integrated with the existing SDK API, are unaffected by this new API.
Existing API has a number of methods used for reporting player and application state changes to the SDK. Order of calls is important for the SDK in the existing API. In the new enhanced API all these calls will be replaced with one API call that will get one dictionary object with many key-value pairs, where any value could be another complex dictionary object. All the data provided in the existing API in separate calls will be provided in one single call. SDK will analyse the data received in the dictionary object, compare it with the data received previously and generate a sequence of calls for the existing API.
For Android, a new wrapper class for AppSdk will be introduced (AppSdkTrackEvent). This class will be responsible for handling and translating new API calls into calls of the existing Nielsen App SDK API methods. A new public API will be introduced in this class, that accepts a JSONObject parameter.
Implementation
This guide covers implementation steps for Android using Android Studio.
Step 1 : Get SDK
For AppSDK versions, release dates and release notes - refer to Android AppSDK release notes.
Always keep latest and greatest AppSDK version inside your app.
The Nielsen AppSDK can either be
- integrated within an application through the use of Gradle - read Gradle implementation guide - Recommended solution that will keep your SDK copy latest
- or downloaded directly as jar file - download ZIP package at Nielsen Downloads. Package contains SDK appsdk.jar as well as sample applications. You will have to update SDK manually always when new version of AppSDK is released.
Step 2 : Setting up your Development Environment
Configuring Android Development Environment
1) (if using downloaded package) - Ensure to unzip the Nielsen App SDK sample app and copy the AppSdk.jar into the app/libs folder on the App’s project. Add it as dependency.
dependencies {
...
// Nielsen AppSDK
implementation files('libs/appsdk.jar')
}
2) Add the following permissions on the project’s AndroidManifest.xml file.
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:required="false" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
3) Add Google Play Services lib into dependencies as Nielsen AppSDK uses the following packages/classes from the Google Play service.
Steps: Android Studio -> File -> Project Structure ->(In module selection) select App -> Dependencies (tab) -> Click “+” button and select “com.google.android.gms:play-services”
.
Ensure it is added in build.gradle (App level) file.
dependencies {
...
implementation 'com.google.gms:google-services:+'
}
Requiered Google Play Service Classes and Packages :
- com.google.android.gms.ads.identifier.AdvertisingIdClient;
- com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
- com.google.android.gms.common.ConnectionResult;
- com.google.android.gms.common.GooglePlayServicesUtil;
- com.google.android.gms.common.GooglePlayServicesRepairableException;
- com.google.android.gms.common.GooglePlayServicesNotAvailableException;
4) Once the files are in place, import com.nielsen.app.sdk to the java source code and start accessing the public interface.
import com.nielsen.app.sdk.*;
Notes :
- The Nielsen App SDK (located in the com.nielsen.app.sdk package) class is the primary application interface to the Nielsen App SDK on Android.
- The Nielsen App SDK class is defined as the only public class belonging to the com.nielsen.app.sdk package.
- Nielsen App SDK is compatible with Android OS versions 2.3+.
- Clients can control / configure the protocol to be used – HTTPS or HTTP to suit their needs.
- For video players : The Android OS hosting the App SDK should use a media player supporting HLS streaming (Android 3.0 and later will support it natively). If the player application uses a 3rd party media player implementing its own HLS, then the minimum Android version will be limited to version 2.3, since the SDK depends on Google Play support to work properly.
- App SDK checks to see if there is a Google service available and updated. If not available or updated, App SDK will not use this service when executing its functions and will make reference to missing imports and the app will not be compiled.
Step 3 : SDK Initialization
The latest version of the Nielsen App SDK allows instantiating multiple instances of the SDK object, which can be used simultaneously without any issue. A maximum of four SDK instances per appid are supported. When a fifth SDK instance is launched, the SDK will return “nil” from initWithAppInfo:delegate:
The following table contains the list of arguments that should be passed during initialization via the AppInfo JSON schema.
Parameter / Argument | Description | Source | Required? | Example |
---|---|---|---|---|
appid | Unique id for the application assigned by Nielsen.
It is GUID data type. |
Nielsen-specified | Yes | PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX |
appname | Name of the application | Client-defined | No | "Nielsen Sample App" |
appversion | Current version of the app used | Client-defined | No | "1.0.2" |
sfcode | Nielsen collection facility to which the SDK should connect. | Nielsen-specified | Yes | "cz" |
nol_devDebug | Enables Nielsen console logging. Only required for testing | Nielsen-specified | Optional | "DEBUG" |
Sample SDK Initialization Code
Initialization of App SDK object through a JSON object
import android.content.Context;
import com.nielsen.app.sdk.IAppNotifier;
import com.nielsen.app.sdk.NielsenEventTracker;
import org.json.JSONException;
import org.json.JSONObject;
public class NielsenInit {
private NielsenEventTracker mEventTracker = null;
public NielsenEventTracker initEventTracker(Context mContext, IAppNotifier appNotifier){
try {
//Initialising the NielsenEventTracker class by passing app information which returns the instance of NielsenEventTracker.
JSONObject appInformation = new JSONObject()
.put("appid", "PDA7D5EE6-B1B8-4123-9277-2A788XXXXXXX")
.put("appversion", "1.0")
.put("appname", "Android Test app")
.put("sfcode", "cz")
.put("nol_devDebug", "DEBUG");
mEventTracker = new NielsenEventTracker(mContext, appInformation, appNotifier);
} catch (JSONException e) {
e.printStackTrace();
}
return mEventTracker;
}
}
APP SDK Error & Event Codes
To view the Error and Event codes for iOS and Android, please review the App SDK Event Code Reference page.
Step 4 : Simplified API Syntax
In the new simplified API, all calls are replaced with one API call that will get one dictionary object with many key-value pairs, where any value could be another complex dictionary object. All the data provided in the older API in separate calls will be provided in one single call.
Main API call for the new NielsenEventTracker API:
mEventTracker.trackEvent(data);
TrackEvent JSON Metadata
Parameter “data” is a JSON object with many key-value pairs that holds all information required by SDK.
Format of input object is the following:
{
"event": <event identifier>,
"type": <type of metadata>,
"metadata":{
"content": <content metadata object>,
"ad": <ad metadata object>,
"static": <static metadata object>
},
"playheadPosition":<playhead value>
}
Passed parametres
Parameter | Description | Supported values | Example |
---|---|---|---|
event | Event identifier |
|
"event":"playhead"
|
type | Determines the metadata object
that should be used for crediting. |
|
"type":"content"
|
metadata | Object that holds metadata values of specific types Detailed in tables below |
Object
|
"metadata":{
"content": <content_metadata object>,
"ad": <ad_metadata object>,
"static": <static_metadata object>
},
|
playheadPosition | Playhead value | String
|
For LiveStream : Position value is Unix timestamp (seconds since Jan-1-1970 UTC)
For VOD : Position value is playhead:
|
Event identifiers
The New API method supports the following event types:
Key | Description |
---|---|
playhead |
It is used to pass content, ad or static metadata, the current playhead value, UNIX timestamp (seconds since Jan-1-1970 UTC) or id3 payload, ott information to the SDK. |
pause |
This event should be used to in the following cases: application enters background, any application interruptions, content playback is paused. (Pause is detected by SDK automatically only if time gap between commands in more than 30 minutes.) |
complete |
It is called when session is completed or ends. |
adStop |
Should be called at the end of each ad. This event type is required to handle the case when advertisements could not be distinguished, as its assetId is the same. |
Content Metadata
Content metadata should remain constant throughout the entirety of an episode/clip including when ads play. For detailed information of metadata and custom variables see specitication of Content Metadata for Czech Republic. Send content metadata for every playheadposition update.
When content is playing, pass only metadata for content - like
JSONObject metadata= new JSONObject()
.put("content", content_metadataObject)
.put("ad", "")
.put("static", "");
Ad Metadata
The ad metadata (if applicable) should be passed for each individual ad, if ads are available during or before the stream begins. For detailed information of metadata and custom variables see specitication of Content Metadata for Czech Republic.
When ad is playing, pass metadata for ad and its content as well - like
JSONObject metadata= new JSONObject()
.put("content", content_metadataObject)
.put("ad", ad_metadataObject)
.put("static", "");
Static Metadata
Note : static is currently used in Czech Republic only for Browser implementations, not apps. Please leave metadata empty - like
JSONObject metadata= new JSONObject()
.....
.put("static", "");
Putting it all together (example for ad)
// content metadata - we should pass content dictionary also in Ad video.
JSONObject content_metadataObject = new JSONObject()
.put("assetid", "assetid_example")
.put("type", "content")
.put("program", "ProgramName")
.put("title", "Bunny in Woods")
.put("length", "579")
.put("mediaUrl", "") // empty
.put("airdate", "20171013 20:00:00")
.put("isfullepisode", "y")
.put("crossId1", "IDEC")
.put("nol_c1", "p1,") // empty for VOD
.put("nol_c2", "p2,TV ident")
.put("segB", "Program type")
.put("segC", "") // empty
.put("adloadtype", "1")
.put("hasAds", "1");
// ad metadata
JSONObject ad_metadataObject = new JSONObject()
.put("assetid", "assetid_example_postroll")
.put("type", "postroll")
.put("length", "30")
.put("title", "Nielsen postroll")
.put("nol_c4", "p4,ASMEAcode")
.put("nol_c5", "p5,AtributForAd")
.put("nol_c6", "p6,postroll");
// all metadatas in one object
JSONObject metaData = new JSONObject()
.put("content", content_metadataObject)
.put("ad", ad_metadataObject)
.put("static", "");
// dataObject for TrackEvent
JSONObject trackEventData = new JSONObject()
.put("metadata", metaData)
.put("event", "playhead")
.put("type", "ad")
.put("playheadPosition", "0"); // change every second 0,1,2,3...
]
// fire TrackEvent
mEventTracker.trackEvent(trackEventData);
Step 5 : API Call sequence
Use Case 1: Content has no Advertisements
Playlist : single content, no ads.
A Sample API sequence follow this flow:
Playlist | API call TrackEvent | Description |
---|---|---|
1. Video start | event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 1 | 1st call playhead |
2. Video playing .. | event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 2,3,4.. etc | playhead every second |
3. Video ends | event complete, type : content, metadata : content_metaDataObject, playheadPosition : lastPlayheadValue | call complete |
Use Case 2: Content has Advertisements
Playlist : ad preroll - content - ad midroll - content continues - ad postroll.
A Sample API sequence follow this flow:
Playlist | API call TrackEvent | Description |
---|---|---|
1. Ad Preroll start | event playhead, type : ad, metadata : ad_preroll_metaDataObject + content_metaDataObject, playheadPosition : 1 | 1st call playhead |
2. Ad Preroll playing .. | event playhead, type : ad, metadata : ad_preroll_metaDataObject + content_metaDataObject, playheadPosition : 2,3,4.. etc | playhead every second |
3. Ad Preroll ends | event adStop, type : ad, metadata : ad_preroll_metaDataObject + content_metaDataObject, playheadPosition : lastPosition | call adStop at the end of add |
4. Content start | event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 1 | 1st call playhead |
5. Content playing .. | event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 2,3,4.. etc | playhead every second |
6. Content interupted by midroll | event playhead, type : content, metadata : content_metaDataObject, playheadPosition : lastPlayheadValue | last playhead |
7. Ad Midroll start | event playhead, type : ad, metadata : ad_midroll_metaDataObject + content_metaDataObject, playheadPosition : 1 | 1st call playhead |
8. Ad Midroll playing .. | event playhead, type : ad, metadata : ad_midroll_metaDataObject + content_metaDataObject, playheadPosition : 2,3,4.. etc | playhead every second |
9. Ad Midroll ends | event adStop, type : ad, metadata : ad_midroll_metaDataObject + content_metaDataObject, playheadPosition : lastPosition | call adStop at the end of add |
10. Content continues after midroll | event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 1 | 1st call playhead |
11. Content playing .. | event playhead, type : content, metadata : content_metaDataObject, playheadPosition : 2,3,4.. etc | playhead every second |
12. Content ends | event complete, type : content, metadata : content_metaDataObject, playheadPosition : lastPlayheadValue | call complete for content |
13. Ad Postroll start | event playhead, type : ad, metadata : ad_postroll_metaDataObject + content_metaDataObject, playheadPosition : 1 | 1st call playhead |
14. Ad Postroll playing .. | event playhead, type : ad, metadata : ad_postroll_metaDataObject + content_metaDataObject, playheadPosition : 2,3,4.. etc | playhead every second |
15. Ad Postroll ends | event adStop, type : ad, metadata : ad_postroll_metaDataObject + content_metaDataObject, playheadPosition : lastPosition | call adStop at the end of add |
Use Case 3: Advertisement Pool
where there are more ads in each ad pool.
Playlist : ad preroll1 - preroll 2 - content - ad midroll 1 - ad midroll 2 - content continues - ad postroll 1 - ad postroll 2.
Use adldx parametr to assign position of ad in ad pool (starting from 0)
Ad in pool MetaData Example
JSONObject adMetadata = new JSONObject()
.put("assetid", "assetid_example_postroll")
.put("type", "postroll")
.put("length", "30")
.put("title", "Nielsen postroll")
.put("nol_c4", "p4,ASMEAcode")
.put("nol_c5", "p5,AtributForAd")
.put("nol_c6", "p6,postroll")
.put("adldx" : “0” ); // position in ad pool - 0,1,2..
Reset the playhead position to 0 for each ad.
Call the adStop event at the end of each ad or increment the adldx
The Simplified SDK will can automatically detect the change from ad to content, or even ad to ad if the assetID changes; however, there could be situations where the same ad is played back to back. You can either increment/change the adldx value, and/or call adStop at the end of each Ad.
Step 6 : Handling Foreground and Background states
Foreground/Background state measurement is a requirement of Nielsen AppSDK implementation.
Call API call pause when app is entering Background.
Handling Foreground and Background states may be implemented in multiple ways for Android :
1) ForeGround/Background Measurement via AndroidManifest
Enable the Nielsen SDK to measure background/foreground state by making the relevant update to the AndroidManifest. The simplest way to measure the app background/foreground state is to add the following application tag to the Manifest XML. Integrating this into the Manifest XML will enable the SDK to measure app state directly. This approach is supported for Android 4.0 and up only; it requires that the application class is not in use for some other purpose.
<application android:name="com.nielsen.app.sdk.AppSdkApplication">
2) Using the Android SdkBgFbDetectionUtility Class
Integrate Nielsen’s SdkBgFgDetectionUtility class within your Custom Application Class. For developers who are already using the application class, it is recommended that background/foreground state is implemented using the SdkBgFgDetectionUtility class. The SdkBgFgDetectionUtility class is compatible with Android 4+ and has been made available to Nielsen clients.
3) Manual Background/ForeGround State Management
In cases where the developer is not able to use the AndroidManifest.xml solution nor the Nielsen provided SdkBgFgDetectionUtility class the developer will need to manually identify the change of state through the application and call the respective API (appInForeground() or appInBackground()) to inform the SDK regarding the change of state from background to foreground or foreground to background.
The SDK is informed about app state using the below methods.
AppLaunchMeasurementManager.appInForeground(getApplicationContext());
AppLaunchMeasurementManager.appInBackground(getApplicationContext());
Within the lifecycle of individual activities, onResume() and onPause() are best suited to providing indication of the app state.
Step 7 : Privacy and Opt-Out
A user can opt-out if they would prefer not to participate in any Nielsen online measurement research.
Users must have access to "About Nielsen Measurement" web page - see below, or have similar text available within the native app.
Include link or text to
- app settings screen
- or to your Privacy Policy / EULA section
- or as a button near the link to the app's Privacy Policy.
In addition, the following text must be included in your app store description.
Please note: This app features Nielsen’s proprietary measurement software which contributes to market research, like Nielsen’s TV Ratings. Please see https://priv-policy.imrworldwide.com/priv/browser/cz/cs/optout.html for more information
SDK will be sending the data pings to census even though SDK is opted out (In earlier releases all the traffic from SDK to census will be ceased). However, all the outgoing pings will have the parameter uoo=true using which backend can ignore this data.
The Nielsen SDK automatically leverages the Android's Opt out of Ads Personalization setting. The user is opted out of demographic measurement if the OS-level "Opt out of Ads Personalization" ("Limit Ad Tracking" for iOS) setting is enabled. As a publisher, you cannot override this setting.
1. Get Url of privacy page
URL for the Nielsen Privacy web page should be retrieved using API call userOptOutURLString()
mEventTracker.userOptOutURLString();
If the App SDK returns NULL in the optOutURL, handle the exception gracefully and retry later. Example of returned Url is https://priv-policy.imrworldwide.com/priv/mobile/cz/cs/optout.html followed by unique hash.
2. Open URL in webView
Recieved URL should be opened in 'WebView' / External browser. App should provide a UI control like 'close' or 'back' button to close the 'WebView' / External browser.
3. Get current Opt-Out status (voluntarily)
To retrieve the current Opt-Out status of a device, use the getOptOutStatus() method.
mEventTracker.getOptOutStatus();
if returns false = no OptOut, if true = OptOut is active.
Step 8 : Test your player by your self
Guide
- Connect your PC and test device (tablet or phone) via same router.
- PC side: run Proxy sw (like Charles) and get local IP
- Test device: modify Wifi setting to pass throug Proxy IP from add 2
- Test device: run your player, launch video
- PC side: filter trafic by "imr" and confirm presence of GN pings
for Android see https://youtu.be/zOKOinb-zdc, for iOS see https://youtu.be/Gk0YQttiXRI
Example of GN ping
http://secure-eu-cert.imrworldwide.com/cgi-bin/gn?prd=dcr&ci=de-205177
&ch=de-205177_c01_P&asn=defChnAsset&tl=Bunny%2520in%2520Woods&prv=1&c6=vc,c01
&ca=de-205177_c01_assetid&cg=ProgramName&c13=asid,T885AAE54-AFE4-431D-B60B-FF4C75FCA357
&c32=segA,NA&c33=segB,CustomSegmentValueB&c34=segC,CustomSegmentValueC
&c15=apn,CZ%20demo%20player&sup=1&segment2=-1&segment1=cze&forward=0&ad=1
&cr=4_00_99_V1_00000&c9=devid,d6df553edcc70bf62812069af7252d5e905399c711378b6ee0a1f0a9381fdec6
&enc=true&c1=nuid,62ef4e4aeb2c3d390c224e3aaf686ec063895e9dd9a64f4d2b84059e1148e29c&at=view
&rt=video&c16=sdkv,aa.5.1.1&c27=cln,0&crs=0&lat=&lon=&c29=plid,T885AAE54-AFE4-431D-B60B-FF4C75FCA357
&c30=bldv,aa.5.1.1.18&st=dcr&c7=osgrp,DROID&c8=devgrp,TAB&c10=plt,MBL&c40=adbid,
&c14=osver,Android6.0.1&c26=dmap,1&dd=&hrd=&wkd=&c35=adrsid,&c36=cref1,IDEC&c37=cref2,
&c11=agg,1&c12=apv,1.0.1&c51=adl,0&c52=noad,0&sd=596&devtypid=asus-Nexus-7&pc=NA&c53=fef,y
&c54=oad,20171013%2020%3A00%3A00&c55=cref3,&c57=adldf,1&ai=assetid&c3=st%252Cc
&c64=starttm,1500474035&adid=assetid&c58=isLive,false&c59=sesid,1500474032&c61=createtm,1500474037
&c63=pipMode,&c60=agfall,p0%252C~~p1%252Ccontent%2520-%2520ID%2520for%2520content%2520matching%2520TVIDENT~~p2%252Ccontent%2520-%2520Reserved%2520for%2520future%2520use~~st%252Cc~~~~~~&c62=sendTime,1500474123
&c68=bndlid,com.example.kunalbhatia.hlsexomy&nodeTM=&logTM=&c73=phtype,Tablet&c74=dvcnm,Google+Nexus+7
&c76=adbsnid,&df=-1&c77=adsuprt,1&evdata=&c71=ottflg,0&c72=otttyp,&sessionId=&c44=progen,&davty=0&si=
&c66=mediaurl,&uoo=&vtoff=86&rnd=1500474122131
Step 9: Provide your app for certification
Once ready please send your application to Nielsen local staff for verification - see Contact list for Czech Republic.
Step 10: Going Live
After the integration has been certified (but not prior that), disable debug logging by deleting {nol_sdkDebug: 'DEBUG'} from initialization call - see Step 3.