DCR Denmark Video Android SDK: Difference between revisions

From Engineering Client Portal

 
(25 intermediate revisions by 3 users not shown)
Line 22: Line 22:
|}
|}


__TOC__
== Step 1: Setting up your Android Development Environment  ==
==  Implementation ==
<br />
This guide covers implementation steps for Android Studio utilizing the Standard Nielsen SDK for DCR.
1) Ensure to unzip the Nielsen App SDK zip file and copy the "AppSdk.jar" into the app/libs folder on the App’s project. Add it as dependency.<br />
{| class="wikitable" style="background-color:#c6f5c5;"
2) Add the following permissions on the project’s "AndroidManifest.xml" file.<br />
|-
| If you are building an app for the 'kids category' please review the [https://engineeringportal.nielsen.com//docs/DCR_Video_Android_SDK#Special_Note_Regarding_Apps_in_the_Kids_Category Opt Out Requirement].
|}
=== How to obtain the NielsenAppApi ===
The Nielsen AppSDK can either be downloaded directly or can be integrated directly within an application through the use of Gradle. We recommend using the Gradle-based integration whenever possible to ensure you maintain the most recent changes and enhancements to the Nielsen libraries.
* [[Digital_Measurement_Android_Artifactory_Guide|Select to obtain Gradle implementation guide]]
* [[Special:Downloads|Select to Download Directly]]
 
== Setting up your Development Environment  ==
 
=== Configuring Android Development Environment ===
*The Nielsen App SDK (located in the [https://engineeringportal.nielsen.com/docs/Special:Downloads Downloads section] of the website) 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.'''
The requirement for the Java ''AppSdk.jar'' library and the ''libAppSdk.so'' native library will depend on the type of host application that will make use of them.
* '''For Video player applications'''
** 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.
Once SDK is downloaded ensure to unzip the Nielsen SDK and copy the AppSdk.jar in your app (Android Studio) libs folder, then right click the AppSdk.jar and select '''Add As Library'''.
Ensure the AppSdk.jar file is added in 'build.grade (App Level) file.
* App SDK 1.2 provides support for x86, mips, and armeabi-7a architecture.
==== Google Play Services ====
Add the Google Play Services in the project,
Steps: Android Studio -> File -> Project Structure ->(In module selection) select App -> Dependencies (tab) -> Click "+" button and select <code>"com.google.android.gms:play-services"</code>.
Ensure it is added in build.gradle (App level) file
==== Manifest File ====
* Add the following permissions on the project’s ''AndroidManifest.xml'' file.
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/></syntaxhighlight>
<uses-permission android:name="android.permission.INTERNET"/></syntaxhighlight>
For more details to handle runtime permissions in Android versions, please visit [https://developer.android.com/training/permissions/requesting.html]. 
3) Add Google Play Services lib into dependencies as Nielsen AppSDK uses the following packages/classes from the Google Play service.
 
Libraries:
* In <code>AndroidManifest.xml </code>under <application> node add the following metadata
* com.google.android.gms:play-services
 
Requiered Google Play Service CLasses and Packages :
<syntaxhighlight lang="java"><meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version"/></syntaxhighlight>
 
* 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.
 
==== Library ====
Nielsen App SDK uses the following packages/classes from the Google Play service.
* google-play-services_lib
 
==== Classes/package ====
* com.google.android.gms.ads.identifier.AdvertisingIdClient;
* com.google.android.gms.ads.identifier.AdvertisingIdClient;
* com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
* com.google.android.gms.ads.identifier.AdvertisingIdClient.Info;
Line 84: Line 41:
* com.google.android.gms.common.GooglePlayServicesNotAvailableException;
* com.google.android.gms.common.GooglePlayServicesNotAvailableException;


== SDK Initialization ==
4) Once the files are in place, import com.nielsen.app.sdk to the java source code and start accessing the public interface.<br />
The latest version of the Nielsen App SDK allows instantiating multiple instances of the SDK object, which can be used simultaneously without any issue. The sharedInstance API that creates a singleton object was deprecated prior to version 5.1.1. (Version 4.0 for Android)
<syntaxhighlight lang="java">import com.nielsen.app.sdk.*;</syntaxhighlight><br />
<br />
 
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.
*The Android OS hosting the App SDK should use a media player supporting HLS streaming (Android 4.0 and later will support it natively).
*If the player application uses a 3rd party media player implementing its own HLS/MPEG-DASH stack, then the minimum Android version will be limited to version 2.3, since the SDK depends on Google Play support to work properly.
 
== Step 2: Create SDK Instance ==
The latest version of the Nielsen App SDK allows instantiating multiple instances of the SDK object when needed, which can then be used simultaneously. '''For the general use case where only one video is played at the same time in the App, a single instance of SDK object can then be used to play back and measure all watched streams one after another.'''


The following table contains the list of arguments that can be passed via the AppInfo JSON schema.
The following table contains the list of arguments that can be passed via the AppInfo JSON schema.
{| class="wikitable"
{| class="wikitable"
|-
|-
! Parameter / Argument !! Description !! Source !! Required? !! Example
! Parameter / Argument !! Description !! Source !! Required !! Example
|-
|-
| appid || Unique Nielsen ID for the application. The ID is a GUID data type. If you did not receive your App ID, let us know and we will provide you. || Nielsen-specified || Yes || PXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
| appid || Unique id for the application assigned by Nielsen. It is GUID data type.|| Nielsen-specified || || "PXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
|-
| appname || Name of the application || '''value is automatically populated through App Name included in the App Resource File''' ||  || "Nielsen Sample App"
|-
| appversion || Current version of the app used || '''value is automatically populated through App Name included in the App Resource File''' ||  || "1.0.2"
|-
|-
| nol_devDebug || Enables Nielsen console logging. Only required for testing
| nol_devDebug || Enables Nielsen console logging. Only required for testing
|| Nielsen-specified || Optional || "DEBUG"
|| Nielsen-specified || || "DEBUG"
|}
|}
<br />


== Debug flag for development environment ==
1) AppSDK() is no longer a singleton object and should be created as below.
Player application developers / integrators can use Debug flag to check whether an App SDK API call made is successful. To activate the Debug flag,
<syntaxhighlight lang="java">
Pass the argument <code>@"nol_devDebug":@"INFO"</code>, in the JSON string . The permitted values are:
try{
 
* '''INFO''': Displays the API calls and the input data from the application (validate player name, app ID, etc.). It can be used as certification Aid.
* '''WARN''': Indicates potential integration / configuration errors or SDK issues.
* '''ERROR''': Indicates important integration errors or non-recoverable SDK issues.
* '''DEBUG''': Debug logs, used by the developers to debug more complex issues.
 
Once the flag is active, it logs each API call made and the data passed. The log created by this flag is minimal.
<blockquote>'''Note''': DO NOT activate the Debug flag in a production environment.</blockquote>
 
==== Sample SDK Initialization Code ====
[[AppSDK()]] is no longer a singleton object and should be initialized as below.
 
'''Initialization of App SDK object through a JSON object'''
<syntaxhighlight lang="java">try
{
   // Prepare AppSdk configuration object (JSONObject)
   // Prepare AppSdk configuration object (JSONObject)
   JSONObject appSdkConfig = new JSONObject()
   JSONObject appSdkConfig = new JSONObject()
           .put("appid", "PDA7D5EE6-B1B8-XXXX-XXXX-2A788BCXXXCA")
           .put("appid", "PXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
           .put("nol_devDebug", "DEBUG"); // only for debug builds
           .put("nol_devDebug", "DEBUG"); // only for debug builds


// Pass appSdkConfig to the AppSdk constructor
        // Pass appSdkConfig to the AppSdk constructor
mAppSdk = new AppSdk(appContext, appSdkConfig, appSdkListener);
        mAppSdk = new AppSdk(appContext, appSdkConfig, this);
}
catch (JSONException e){
        Log.e(TAG, "Couldn’t prepare JSONObject for appSdkConfig", e);
}
}
catch (JSONException e)
</syntaxhighlight>
{
2) implement IAppNotifier into your activity like
   Log.e(TAG, "Couldn’t prepare JSONObject for appSdkConfig", e);
<syntaxhighlight lang="java">public class MainActivity extends AppCompatActivity implements IAppNotifier
</syntaxhighlight>
3) implement callback
<syntaxhighlight lang="java">@Override
public void onAppSdkEvent(long timestamp, int code, String description){
   Log.d(TAG, "SDK callback onAppSdkEvent " + description);
}
}
</syntaxhighlight>
</syntaxhighlight>
Here, <code>appContext</code> is the App context object and <code>appSdkConfig</code> is JSON object for holding the parameters (<code>appid</code>) the App passes to the Nielsen App SDK via a JSON string. The appid is obtained from Nielsen operational support and is unique to the app.


So whole Activity will look like
<syntaxhighlight lang="java">
package com.example.josefvancura.nlsdemotmp;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.nielsen.app.sdk.*;
import org.json.JSONException;
import org.json.JSONObject;
public class MainActivity extends AppCompatActivity implements IAppNotifier {
    private AppSdk mAppSdk = null;
    private String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


The integration of Nielsen App SDK will depend on type of client app.<br />
        Context context = getApplicationContext();
* Ensure that SDK files (AppSdk.jar and libAppSdk.so [App SDK 1.2 Only]) are included under the App’s project and the App SDK is linked to the App (the setting to link App SDK to the App can be found on property page of the App’s project).


<!-- == Initializing the Nielsen AppSDK to measure the Viewability ==
        try{
The integrator to support the viewability metrics in the application has to provide a tag value of the player view to let Nielsen AppSDK know that there is a player that needs to be tracked. It’s called the ‘containerId’ and it should be passed in application info dictionary as string while initializing the Nielsen AppSDK.
            // Prepare AppSdk configuration object (JSONObject)
            JSONObject appSdkConfig = new JSONObject()
                    .put("appid", "PXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
                    .put("nol_devDebug", "DEBUG"); // only for debug builds


==== Android ====
            // Pass appSdkConfig to the AppSdk constructor
{| class="wikitable"
            mAppSdk = new AppSdk(context, appSdkConfig, this ); // Notifier - activity implements IAppNotifier, callback in onAppSdkEvent()
|-
 
! # !! Parameter Name !! Description !! Supported Values !! Example
        }
|-
        catch (JSONException e){
| 1 || containerId ||View ID of the UI element used as player view in application. getId() method of View class can be used to get this value. ||A positive integer used to identify the view. || 2131558561
            Log.e(TAG, "Couldn’t prepare JSONObject for appSdkConfig", e);
|}
        }


==== iOS ====
    }
{| class="wikitable"
|-
! # !! Parameter Name !! Description !! Supported Values !! Example
|-
| 1 || containerId ||The tag of the UIView that represents the Player View ||The string value representing the NSInteger value with maximum value of NSIntegerMax that is related on 32- or 64-bit applications. || "100" <br> "2131558561"
|}


For iOS it is required to link additional frameworks that are needed for viewability engine:<br>
    @Override
<code>JavaScriptCore.framework</code> <br>
    public void onAppSdkEvent(long timestamp, int code, String description) {
<code>WebKit.framework</code>
        Log.d(TAG, "SDK callback onAppSdkEvent " + description);
    }


The Nielsen AppSDK uses a tracking WebView (TWV) approach.  For more information on Viewability, please refer to [https://engineeringportal.nielsen.com/docs/Implementing_Viewability_with_AppSDK Implementing Viewability with AppSDK.]
}
-->
</syntaxhighlight>


== APP SDK Error & Event Codes ==
==== APP SDK Error & Event Codes ====
To view the Error and Event codes for iOS and Android, please review the [[APP SDK Event Codes|App SDK Event Code]] Reference page.
To view the Error and Event codes for iOS and Android, please review the [[APP SDK Event Codes|App SDK Event Code]] Reference page.


== Create Metadata Objects ==
==== Life cycle of SDK instance ====
Life cycle of SDK instance includes four general states:
# ""Initial state"" – The SDK is not initialized and hence, not ready to process playing information. Once the SDK is moved out of this state, it needs instantiation of the new SDK instance in order to get the instance in the ""Initial state"".
# ""Idle state"" – The SDK is initialized and is ready to process playing information. Once Initialized, the SDK instance is not processing any data, but is listening for the play event to occur.
# ""Processing state"" – The SDK instance is processing playing information. API calls "play" and "loadMetadata" move the SDK instance into this state. In this state, the SDK instance will be able to process the API calls (see below)
# ""Disabled state"" – The SDK instance is disabled and is not processing playing information. SDK instance moves into this state in one of the following scenarios.
## Initialization fails
## <code>appDisableApi</code> is called
<syntaxhighlight lang="objective-c">
@property (assign) BOOL appDisableApi;
</syntaxhighlight>
 
== Step 3: Create Metadata Objects ==
The parameters passed must be either a JSON formatted string or a NSDictionary object. The JSON passed in the SDK must be well-formed.
The parameters passed must be either a JSON formatted string or a NSDictionary object. The JSON passed in the SDK must be well-formed.
* NSDictionary object
* NSDictionary object
Line 221: Line 219:
||
||
|-style="background-color:#d0f6f8;"
|stationId || Unique Gracenote Station ID generated by Nielsen that uniquely identifies a live channel
||
provided by Nielsen
||
|-style="background-color:#d0f6f8;"
|islivestn || Indicates if a stream is playing on a live channel
||
:
* "y" playing on a live channel
* "n" otherwise i.e. VoD
||
|-style="background-color:#d0f6f8;"
|pbstarttm || Playback Start Time (UTC): Unix timestamp in seconds matching the broadcast time for Content and Ad when a user starts and joins a live stream (seconds since Jan-1-1970 UTC)
||
i.e. "1631098029" for live stream and ""(empty string) for a VoD.
||
|-
| subbrand || vcid/sub-brand/Channel  ID – value is automatically populated through provided AppID. In order to override the sub-brand configured to the App ID, value can be passed here (e.g. multiple sub-brands/Channels in the App)
||
provided by Nielsen
||
|}
|}


Line 234: Line 258:
|-
|-
| assetid || unique ID assigned to Ad || custom<br>(no [[Special Characters]]) || ✓
| assetid || unique ID assigned to Ad || custom<br>(no [[Special Characters]]) || ✓
|-style="background-color:#d0f6f8;"
|stationId || Unique Gracenote Station ID generated by Nielsen that uniquely identifies a live channel
||
provided by Nielsen
||
|-style="background-color:#d0f6f8;"
|islivestn || Indicates if a stream is playing on a live channel
||
:
* "y" playing on a live channel
* "n" otherwise i.e. VoD
||
|-style="background-color:#d0f6f8;"
|pbstarttm || Playback Start Time (UTC): Unix timestamp in seconds matching the broadcast time for Content and Ad when a user starts and joins a live stream (seconds since Jan-1-1970 UTC)
||
i.e. "1631098029" for live stream and ""(empty string) for a VoD.
||
|-
| subbrand || vcid/sub-brand/Channel  ID – value is automatically populated through provided AppID. In order to override the sub-brand configured to the App ID, value can be passed here (e.g. multiple sub-brands/Channels in the App) || provided by Nielsen || ✓
|}
|}


Line 239: Line 285:
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
JSONObject channelInfo = new JSONObject()
JSONObject channelInfo = new JSONObject()
     .put("channelname","My Channel Name 1")
     .put("channelName","My Channel Name 1")


JSONObject contentMetadata = new JSONObject()
JSONObject contentMetadata = new JSONObject()
Line 249: Line 295:
   .put("airdate", "20200713 10:22:00")
   .put("airdate", "20200713 10:22:00")
   .put("isfullepisode", "y")
   .put("isfullepisode", "y")
   .put("adloadtype", "2");
   .put("adloadtype", "2")
  .put("subbrand", "c05")
  .put("stationId", "TESTXXYYSSWWLL")
  .put("islivestn", "y")
  .put("pbstarttm", "1631098029");


JSONObject adMetadata = new JSONObject()
JSONObject adMetadata = new JSONObject()
   .put("assetid", "unique_postroll_ad_id")
   .put("assetid", "unique_postroll_ad_id")
   .put("type", "postroll");
   .put("type", "postroll"
  .put("subbrand", "c05")
  .put("stationId", "TESTXXYYSSWWLL")
  .put("islivestn", "y")
  .put("pbstarttm", "1631098029");


</syntaxhighlight>
</syntaxhighlight>


== Sequence of Calls ==
== Step 4: Start the Measurement ==
=== play ===
Use [[DCR_Video_APP_SDK#play|play]] to pass the channel descriptor information through channelName parameter when the user taps the '''Play''' button on the player.
<syntaxhighlight lang="java">    public void play(JSONObject channelInfo);</syntaxhighlight>


=== loadMetadata ===
=== Overview of SDK API Calls ===
<syntaxhighlight lang="java">public void loadMetadata(JSONObject contentMetadata);</syntaxhighlight>


=== playheadPosition ===
==== play ====
<syntaxhighlight lang="java">
The play method prepares the SDK for reporting once an asset has loaded and playback has begun. Use play to pass the channel descriptor information through channelName parameter when the user taps the ""Play"" button on the player. Call play only when initially starting the video.
public void setPlayheadPosition(long position)
</syntaxhighlight>


=== stop ===
<syntaxhighlight lang="java">mAppSdk.play(JSONObject channelInfo);</syntaxhighlight>
<syntaxhighlight lang="java">public void stop()</syntaxhighlight>


=== end ===
==== loadMetadata ====
When content stop is initiated and content cannot be resumed from the same position (it can only be restarted from the beginning of stream).
Needs to be called at the beginning of each asset, pass JSON object for relevant content or ad. Make sure to pass as 1st loadMetadata for content at the begining of playlist - see below API call sequence examples.
<syntaxhighlight lang="java">public void end()</syntaxhighlight>
<syntaxhighlight lang="java">mAppSdk.loadMetadata(JSONObject contentMetadata);</syntaxhighlight>


== Configure API Calls ==
==== playheadPosition ====
<pre style="background-color:#d0f6f8">
Note: "setPlayheadPosition" has to be called every second and the value passed should match the broadcast time for live channel.
</pre>
* VOD: current position in seconds. Pass whole number that increments only by 1 like 0,1,2,3... <br/>
* Live: Unix timestamp matching the broadcast time for Content and Ad (seconds since Jan-1-1970 UTC). Pass whole number that increments only by 1 like 1631098029,1631098030,1631098031,1631098032,... <br/>


=== Sample API Sequence ===
<syntaxhighlight lang="Java">mAppSdk.setPlayheadPosition(long videoPositon);</syntaxhighlight>
A Sample API sequence could follow this flow:
{| class="wikitable"
|-
! Type !! Sample code !! Description
|-
|On App Start||<code>[nielsenMeter loadMetadata: contentMetadata];</code> || // contentMetadata Object contains the JSON metadata for the impression
|-
| rowspan="2" | Start of stream || <code>[nielsenMeter play];</code> || // call at start of each new stream
|-
| <code>[nielsenMeter loadMetadata: contentMetadataObject];</code> || // contentMetadataObject contains the JSON metadata for the content being played
|-
| Content || <code>[nielsenMeter setplayheadPosition: position];</code> || // playheadPosition is position of the playhead while the content is being played
|-
| End of Stream || <code>[nielsenMeter end];</code> || // Content playback is completed.
|}


=== SDK Events ===
==== sendID3 ====
{| class="wikitable"
Needs to be called when ID3 Tags are included in the Video Stream.
|-
<syntaxhighlight lang="Java">mAppSdk..sendID3(String id3);</syntaxhighlight>
! Event !! Parameter !! Description
|-
| 'play' || || Call at start of each new stream
|-
| 'loadMetadata' || content/ad metadata object || Needs to be called at the beginning of each asset
|-
| 'setPlayheadPosition' || playhead position as integer<br/>
VOD: || current position in seconds <br/>
Live: current Unix timestamp (seconds since Jan-1-1970 UTC) <br/>
Note: 'setPlayheadPosition' has to be called every second
||
Pass playhead position every second during playback
|-
| 'stop' || playhead position || Call during any interruption to content or Ad playback and at the end of each Ad.
|-
| 'end' || playhead position in seconds || Call when the current video asset completes playback and pass the playhead position. <br/>
Example: At the end of the content stream, if the user switches to another piece of content, when the browser is refreshed or closed.
|}
<blockquote>Note: For livestream, send the Unix timestamp; for VOD send the time in seconds as integer. The final playhead position must be sent for the current asset being played before calling <code>'''stop'''</code>, <code>'''end'''</code> or<code> '''loadmetadata'''</code>,.</blockquote>
 
=== Life cycle of SDK instance ===
Life cycle of SDK instance includes four general states:
# '''Initial state''' – The SDK is not initialized and hence, not ready to process playing information. Once the SDK is moved out of this state, it needs instantiation of the new SDK instance in order to get the instance in the '''Initial state'''.
# '''Idle state''' – The SDK is initialized and is ready to process playing information. Once Initialized, the SDK instance is not processing any data, but is listening for the play event to occur.
# '''Processing state''' – The SDK instance is processing playing information. The <code>'''play'''</code> and <code>'''loadMetadata''' </code> calls move the SDK instance into this state. In this state, the SDK instance will be able to process the following calls.
## <code>'''setplayheadPosition'''</code> – Call this API every one second when playhead position timer is fired.  If a LIVE event, use the current Unix timestamp (seconds since Jan-1-1970 UTC).
## <code>'''stop'''</code> – Call this API when the playback is paused, switches between content and ad (within the same content playback) or encounters interruptions.
## <code>'''end'''</code> – Call when content completes. When called, the SDK instance exits from Processing state.
# '''Disabled state''' – The SDK instance is disabled and is not processing playing information. SDK instance moves into this state in one of the following scenarios.
## Initialization fails
## <code>'''appDisableApi'''</code> is set to <code>true</code>
 
<blockquote>'''Note:''' For API Version 5.1 and above, App SDK will fire data pings and continue measurement even after the user has opted out from Nielsen measurement on a device. The data ping will be marked as opted-out ping.
 
'''Note''': In case of any interruptions during playback due to alarm, calendar, call, flight mode, Wi-Fi toggle, channel change, etc., call [[stop]] to stop the measurement.
* As soon as the playback resumes, <code>'''loadMetadata''' </code> and  <code>'''playheadPosition'''</code> </blockquote>
 
== Handling Foreground and Background states ==
There are a few approaches to managing the Foreground and Background states of an app available to use for state measurement.
* Utilizing the Androidx LifeCycleObserver (The recommended approach starting sdk version 7.1.0.0+)
* Utilizing the [[DCR_Video_Android_SDK#The_SdkBgFgDetectionUtility_class|SdkBgFgDetectionUtility]] class
* Adding a tag to the Manifest XML
* Manual Management
=== The LifeCycleObserver ===
AndroidX replaces the original support library APIs with packages in the androidx namespace, and Android Studio 3.2 and higher provides an automated migration tool. (Select '''Refactor> Migrate to AndroidX''' from the menu bar.)
 
Starting with version 7.1.0, with AndroidX support, an additional utility is provided in the AppSDK - application background/foreground state detection by the AppSdk leveraging the Android Architecture component "LifeCycleObserver".
 
The AppSdk is now capable of detecting the application UI visibility state transitions between background and foreground, without forcing the applications to register for AppSdk's AppSdkApplication class, which is responsible for handling the detection of application background/foreground state transitions at present.
 
<blockquote>Please note, that if you already have an app designed that utilizes the depreciated SdkBgFgDetectionUtility Class, the AppSDK will ignore any calls to these methods if it can utilize the LifeCycleObserver.  LifeCycleObserver based auto detection will take precedence. </blockquote>
==== Adding the AndroidX dependency ====
In order to make use of the app background/foreground state transition auto detection feature of AndroidX AppSdk, the app gradle file needs the androidx dependency. The AppSdk API calls - <code>appInForeground()</code> and <code>appInBackground()</code>  will still be respected by AppSdk by executing the old AppSdk flow of handling "app in foreground" and "app in background" states as is.
 
==== Using the LifeCycle Extension ====
The following androidx dependency is required in the app gradle file:
<syntaxhighlight lang="java">
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
</syntaxhighlight>


<blockquote>
<blockquote>
If you would like to take advantage of this auto detection feature of AppSdk at the very initial stage (e.g. splash screen or at of app launch time), before the AppSdk is initialized, can do so by calling the following newly introduced AppSdk public api, passing the application context :
ID3 Tags are parsed from the Video Stream, refer to the DK Reference Implementation [http://nielsenonlinesupport.com/dk/index.htm sdkRefImplDK] on how to parse ID3 Tags included in a sample HLS Stream.
<syntaxhighlight lang="java">
For further technical details, please contact your Technical Account Manager (TAM).
public static void registerLifeCycleObserver(Context applicationContext)
</syntaxhighlight>
</blockquote>
</blockquote>


==== Log messages for the new auto detection ====
==== stop ====
Call when
* ads complete playing
* when a user pauses playback
* upon any user interruption scenario - see bellow chapter Interruption scenario


When the AppSdk app successfully registers for the LifeCycleObserver : <code>Registered LifeCycleObserver for App Background/Foreground auto-detection</code>
<syntaxhighlight lang="java">mAppSdk.stop();</syntaxhighlight>


* When the app enters the foreground state :<code>App is in foreground, auto detected by AppSDK</code>
==== end ====
* When the app enters the background state :<code>App is in background, auto detected by AppSDK</code>
Call when the content asset completes playback.  Stops measurement progress.
* If the client app doesn't have the "androidx" gradle dependency and AppSdk fails to register LifeCycleObserver :<code>AndroidX LifecycleObserver can not be observed. Please use androidx dependency to activate SDK auto-detection of app background/foreground state.</code>
<syntaxhighlight lang="java">mAppSdk.end();</syntaxhighlight>
* When the appInForeground() is explicitly called while LifeCycleObserver auto detection is active :<code>Ignoring the appInBackground() call, as the App Background/Foreground auto-detection is active. The current state is - foreground</code>
<br />
* When the appInBackground() is explicitly called while LifeCycleObserver auto detection is active :<code>Ignoring the appInBackground() call, as the App Background/Foreground auto-detection is active. The current state is - background</code>


=== The SdkBgFgDetectionUtility class ===
Foreground/Background state measurement is a requirement of Nielsen AppSDK implementation which is especially crucial for static measurement. It may be implemented in multiple ways for Android. This includes
* Enable the Nielsen SDK to measure background/foreground state by makingthe relevant update to the AndroidManifest.
* Integrate Nielsen’s SdkBgFgDetectionUtility class within your Custom Application Class.
* Custom implementation of the required methods within your application.


==== ForeGround/Background Measurement via AndroidManifest ====
=== Start the Measurement ===
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.
In order to start the measurement, follow the 3 first steps below for Content without Ads. When terminating the Content playback call <code>end</code> to terminate the Content Measurement for the given asset.
<syntaxhighlight lang="java">
<application android:name="com.nielsen.app.sdk.AppSdkApplication">
</syntaxhighlight>


==== Using the Android SdkBgFbDetectionUtility Class ====
{| class="wikitable"
For developers who are already using the application class, it is recommended that background/foreground state is implemented using the [https://engineeringportal.nielsen.com/docs/Android_Background_Foreground SdkBgFgDetectionUtility class].  The  [https://engineeringportal.nielsen.com/docs/Android_Background_Foreground SdkBgFgDetectionUtility class] is compatible with Android 4+ and has been made available to Nielsen clients. (You will need to copy/paste the code provided into a file).
|-
! Playlist !! Sample code !! Description
|-
| rowspan="2" | 1. Start of stream || <code>play(channelName)</code> || channelName contains JSON metadata of channel/video name being played
|-
| <code>loadMetadata(contentMetadataObject)</code> || contentMetadataObject contains the JSON metadata for the content being played
|-
| 2. Content is playing || <code>playheadPosition(position)</code> || playheadPosition is position of the playhead while the content is being played
|-
| || <code>sendID3(id3)</code> || id3 is an ID3 Tag parsed from the Video Stream, pass ID3 Tag immediately to SDK when found
|-
| 3. End of Stream || <code>end</code> || Content playback is completed.
|}


==== Manual Background/ForeGround State Management ====
== Step 5: Stop/Resume the Measurement for video Playback Interruptions ==
In cases where the developer is not able to use the AndroidManifest.xml solution nor the Nielsen provided  [https://engineeringportal.nielsen.com/docs/Android_Background_Foreground 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.
As part of integrating Nielsen App SDK with the player application, the Audio / Video app developer needs to handle the following possible interruption scenarios:
* Pause / Play
* Network Loss (Wi-Fi / Airplane / Cellular)
* Call Interrupt (SIM or Third party Skype / Hangout call)
* Alarm Interrupt
* Content Buffering
* Device Lock / Unlock (Video players only, not for Audio players)
* App going in the Background/Foreground (Video players only, not for Audio players)
* Channel / Station Change Scenario
* Unplugging of headphone
In case of encountering one of the above interruptions, the player application needs to
* Call <code>stop</code> immediately (except when content is buffering) and withhold sending playhead position.
* Once the playback resumes, start sending pings <code>playheadPosition</code> for the new viewing session.
Please see the [[Digital Measurement FAQ]] for more details


The SDK is informed about app state using the below methods.
== Step 6: Review SDK Integration Architecture Diagram ==
<syntaxhighlight lang="java">
AppLaunchMeasurementManager.appInForeground(getApplicationContext());
AppLaunchMeasurementManager.appInBackground(getApplicationContext());
</syntaxhighlight>
Within the lifecycle of individual activities, onResume() and onPause() are best suited to providing indication of the app state.
 
 
Correct measurement of the foreground/background state is crucial to Static App measurement within Nielsen Digital Content Ratings (DCR).
 
== SDK Integration Architecture Diagram ==


=== For Content Playback ===
=== For Content Playback ===


[[File:nlsn-sdk-achitecture-diagram_content.png||SDK Integration Architecture Diagram - Content]]
[[File:nlsn-sdk-achitecture-diagram-content-v1.png||SDK Integration Architecture Diagram - Content]]


=== For Ad Playback ===
=== For Ad Playback ===
[[File:nlsn-sdk-achitecture-diagram_ad.png||SDK Integration Architecture Diagram - Ad]]
[[File:nlsn-sdk-achitecture-diagram-ad-v1.png||SDK Integration Architecture Diagram - Ad]]


== Privacy and Opt-Out ==
== Step 7 : Disclose Nielsen Privacy Statement ==
There are two primary methods for implementing user Opt-out preferences:
The App SDK uses Mobile Ad IDs (Android ID or IDFA) which are fully hashed on the device before being sent to Nielsen (Nielsen never receives un-hashed values).
# '''[[#OS-level_Opt-out|OS-level Opt-out]]''' - managed by ''Opt out of Ads Personalization'' setting on device ('''preferred approach''').
Users retain the possibility to oppose the use of Mobile Ad IDs, or to reset them, by using the functionality provided by the mobile operating system (iOS or Android).
# '''[[#User Choice|User Choice]]''' - Direct call to SDK. Can be used without Google Play Services.


=== Special Note Regarding Apps in the '''Kids Category''' ===
In order to disclose Nielsen measurement privacy statement, please include the following items in your privacy policy:
If you are building an app that will be listed in the Kids Category:
* A notice that the player includes third party measurement software that allows users to contribute to market research.
#  Ensure that you are using the NoID version of the Nielsen SDK Framework.
* A link to the Nielsen Digital Measurement Privacy Policy located at https://nielsen.com/legal/privacy-principles/digital-measurement-privacy-statement/?lang=da .
#  Immediately following the initialization of the Nielsen SDK ensure you call the userOptOut API with Opt out selection:  
<syntaxhighlight lang=java>appSdk.userOptOut("nielsenappsdk://1");  // User opt-out</syntaxhighlight>


=== OS-level Opt-out ===
== Step 8 : Review the Reference Implementation for VoD and Live Streams ==
''OS-level Opt-out'' method available on Nielsen Android.


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"'' setting is ''enabled''. As a publisher, you cannot override this setting.
The Reference Implementation covers VoD and Live use cases.  
It also covers DAI (Dynamic Ad Insertion) with  preroll Ad.


==== Webview Example  ====
Download the Reference Implementation for Android [https://nielsenonlinesupport.com/dk/android/DKRefPLAYER.zip DKRefPLAYER].
The below code is an AndroidX example of displaying the Nielsen Privacy page to the user.
Unzip and open the project in Android Studio, then run it i.e. in the simulator or on Android device and then filter the Logcat output with ">>>" in order to see only relevant Nielsen SDK API Calls, as below:
<syntaxhighlight lang="java">
public class OptOutActivity extends AppCompatActivity implements IAppNotifier {
 
    WebView webView;
    AppSdk appSdk;
 
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_optout);
        webView = (WebView) findViewById(R.id.webView);
 
        webView.getSettings().setJavaScriptEnabled(true);
 
        webView.setWebViewClient(new WebViewClient() {
            @SuppressWarnings("deprecation")
            @Override
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                Toast.makeText(OptOutActivity.this, description, Toast.LENGTH_SHORT).show();
            }
            @TargetApi(android.os.Build.VERSION_CODES.M)
            @Override
            public void onReceivedError(WebView view, WebResourceRequest req, WebResourceError rerr) {
                // Redirect to deprecated method, so you can use it in all SDK versions
                onReceivedError(view, rerr.getErrorCode(), rerr.getDescription().toString(), req.getUrl().toString());
            }
        });
 
        NielsenInit nielsenInit = new NielsenInit();      // Initializing the NielsenSDK
        appSdk = nielsenInit.initAppSdk(getApplicationContext(), this);    //Create Instance
        String url = appSdk.userOptOutURLString();  // Request Optout URL from NielsenSDK
        webView.loadUrl(url);                        //Display to the user in a Webview
    }
</syntaxhighlight>
<br>


=== User Choice ===
The ''User Choice'' method is only necessary when the host application does not leverage Google Play Services.
Nielsen Android SDK 5.1.1.18 and above will check for ''OS-level opt-out'' first, if available. The user will be opted out if indicated at the OS-level '''OR''' the App-level.
<br>
==== The legacy opt-out method works as follows: ====
* Get the current Nielsen opt-out URL via [[userOptOutURLString()]]
* Display a WebView element whose loadUrl is set to the value obtained from [[userOptOutURLString()]]
* Detect if the WebView URL changes to a special URL that indicates Opt-in, or Opt-out and close the WebView
** Opt-out if the WebView URL = <code>nielsenappsdk://1</code>
** Opt-in if the WebView URL = <code>nielsenappsdk://0</code>
* Pass the detected URL to the [[userOptOut()]] function
** Example: <syntaxhighlight lang=java>appSdk.userOptOut("nielsenappsdk://1");  // User opt-out</syntaxhighlight>
==== Legacy Opt Out example code ====
<syntaxhighlight lang="java">
<syntaxhighlight lang="java">
private class MonitorWebView extends WebViewClient
2021-04-08 15:11:27.075  D/NielsenInit: ====>>> SDK CALL : class NielsenInit :: Create new AppSdk() Instance.
{
2021-04-08 15:11:27.568  D/NielsenInit: ====>>> SDK EVENT - onAppSdkEvent: Description = Nielsen App SDK is initiated. AppSdk.jar aa.8.0.0.0_gsxaon. App SDK was successfully initiatedCode l param : 1617887487Code i param 2000
  private static final String NIELSEN_URL_OPT_OUT = "nielsenappsdk://1";
2021-04-08 15:11:29.497  D/NielsenInit: ====>>> SDK EVENT - onAppSdkEvent: Description = Nielsen App SDK has started up. AppSdk.jar aa.8.0.0.0_gsxaon. Config file successfully loaded and parsed.Code l param : 1617887489Code i param 2001
  private static final String NIELSEN_URL_OPT_IN = "nielsenappsdk://0";
2021-04-08 15:11:42.736  D/MainActivity: ====>>> SDK CALL :  appSdk.play(sdkMethods.loadChannelInfo());
   
2021-04-08 15:11:42.738  D/MainActivity: ====>>> SDK CALL : onPrepared() : appSdk.loadMetadata(data)
   @Override
2021-04-08 15:11:43.065  D/MainActivity: ====>>> Media Player Event : onInfo ();what = 3
   public boolean shouldOverrideUrlLoading(WebView view, String url)
2021-04-08 15:11:43.065 D/MainActivity: ====>>> Media Player Event :  first video frame pushed for rendering.
   {
2021-04-08 15:11:43.747  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 0 / 224
    if (NIELSEN_URL_OPT_OUT.equals(url)
2021-04-08 15:11:44.752  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 1 / 224
      || NIELSEN_URL_OPT_IN.equals(url))
2021-04-08 15:11:45.759  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 2 / 224
    {
2021-04-08 15:11:46.764  D/MainActivity: ====>>> SDK CALL :  appSdk.setPlayheadPosition(mCurrentPosition): @ 3 / 224
      // Get AppSdk instance from the host
2021-04-08 15:11:47.767  D/MainActivity: ====>>> SDK CALL :  appSdk.setPlayheadPosition(mCurrentPosition): @ 4 / 224
      AppSdk appSdk = HostApp.getAppSdk();
2021-04-08 15:11:48.772  D/MainActivity: ====>>> SDK CALL :  appSdk.setPlayheadPosition(mCurrentPosition): @ 5 / 224
      // Send the URL to the AppSdk instance
2021-04-08 15:11:49.777  D/MainActivity: ====>>> SDK CALL :  appSdk.setPlayheadPosition(mCurrentPosition): @ 6 / 224
      appSdk.userOptOut(url);
2021-04-08 15:11:50.781  D/MainActivity: ====>>> SDK CALL :  appSdk.setPlayheadPosition(mCurrentPosition): @ 7 / 224
      return true;
2021-04-08 15:11:51.819  D/MainActivity: ====>>> SDK CALL :  appSdk.setPlayheadPosition(mCurrentPosition): @ 8 / 224
    }
2021-04-08 15:11:51.821  D/MainActivity: ====>>> Activity onPause()
    return false;
2021-04-08 15:11:52.668  D/MainActivity: ====>>>: Activity onDestroy()
  }
2021-04-08 15:11:52.668  D/MainActivity: ====>>> SDK CALL : terminateNielsenSDK () :appSdk.end();
}
</syntaxhighlight>
</syntaxhighlight>


=== Retrieve current Opt-Out preference ===
== Step 9 :  Test your player by yourself ==
Whether the user is opted out viaOS-level Opt-out or via App-level Opt-out, the current Opt-Out status as detected by the SDK is available via the [[getOptOutStatus()]] property in the Nielsen Android SDK API,
=== Guide ===
 
1. Connect your PC and test device (tablet or phone) via same router.<br />
=== Required Privacy Links ===
2. PC side: run Proxy sw (like Charles) and get local IP<br />
Users must either have access to the "About Nielsen Measurement" page, or have similar text available within the native app. Include "About Nielsen Measurement" and "Your Choices" link in the Privacy Policy / EULA or as a button near the link to the app's Privacy Policy.
3. Test device: modify Wifi setting to pass through Proxy IP from step 2.<br />
 
4. Test device: run your player, launch video<br />
In addition, the following text must be included in your app store description.
5. PC side: filter traffic by "nmr" and confirm presence of GN pings<br />
<blockquote>
'''"Please note: This app features Nielsen’s proprietary measurement software which contributes to market research, like Nielsen’s TV Ratings. Please see http://priv-policy.imrworldwide.com/priv/mobile/us/en/optout.html for more information"'''</blockquote>
 
<br>


== Going Live ==
=== Example of GN ping ===
Following Nielsen testing, you will need to:
<code><nowiki>https://secure-dmk.nmrodam.com/cgi-bin/gn?prd=dcr&ci=us-500207&ch=us-500207_c77_P&asn=defChnAsset&fp_id=&fp_cr_tm=&fp_acc_tm=&fp_emm_tm=&ve_id=&devmodel=&manuf=&sysname=&sysversion=&sessionId=zlmmxkq867zt4bpnumlyz6dpn9hyp1610980356&tl=Episode%201&prv=1&c6=vc%2Cc77&ca=us-500207_c77_VID5556674-123456&cg=TAMSample%20DK&c13=asid%2CP10DF14BA-937E-436D-99DF-ED39A0422387&c32=segA%2CNA&c33=segB%2CNA&c34=segC%2CNA&c15=apn%2C&plugv=&playerv=&sup=1&segment2=&segment1=&forward=0&ad=0&cr=4_00_99_V1_00000&c9=devid%2C&enc=true&c1=nuid%2C999&at=view&rt=video&c16=sdkv%2Cbj.6.0.0&c27=cln%2C0&crs=&lat=&lon=&c29=plid%2C16109803568088038&c30=bldv%2C6.0.0.563&st=dcr&c7=osgrp%2C&c8=devgrp%2C&c10=plt%2C&c40=adbid%2C&c14=osver%2CNA&c26=dmap%2C1&dd=&hrd=&wkd=&c35=adrsid%2C&c36=cref1%2C&c37=cref2%2C&c11=agg%2C1&c12=apv%2C&c51=adl%2C0&c52=noad%2C0&sd=170&devtypid=&pc=NA&c53=fef%2Cy&c54=oad%2C20200713%2010%3A22%3A00&c55=cref3%2C&c57=adldf%2C2&ai=VID5556674-123456&c3=st%2Cc&c64=starttm%2C1610980392&adid=VID5556674-123456&c58=isLive%2Cfalse&c59=sesid%2Cgezrb92q4i9b9jg7acxgn783gjw0a1610980365&c61=createtm%2C1610980392&c63=pipMode%2C&c68=bndlid%2C&nodeTM=&logTM=&c73=phtype%2C&c74=dvcnm%2C&c76=adbsnid%2C&c77=adsuprt%2C2&uoo=&evdata=&c71=ottflg%2C0&c72=otttyp%2Cnone&c44=progen%2C&davty=0&si=http%3A%2F%2Fnielsenonlinesupport.com%2Fdk%2Findex.htm&c66=mediaurl%2Cassets%252FRTVOD_C3%252Fprog_index.m3u8&c62=sendTime%2C1610980392&rnd=714644</nowiki></code>


# '''Disable Debug Logging''': Disable logging by deleting <code>{nol_sdkDebug: 'DEBUG'}</code> from initialization call.
== Step 10 : Provide your app for certification ==
# '''Notify Nielsen''': Once you are ready to go live, let us know so we can enable you for reporting. We will not be able to collect or report data prior to receiving notification from you.
Once ready please send your application to Nielsen local staff for certification.


== Step 9 :  Going Live ==
== Step 11 :  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 2.
After the integration has been certified (but not prior that), disable debug logging by deleting {nol_devDebug: "DEBUG"} from initialization call - see Step 2.

Latest revision as of 13:01, 4 August 2022

Engineering Portal / Digital / International DCR / DCR Denmark Video Android SDK

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 It supports a variety of Nielsen Measurement Products like Digital in TV Ratings (DTVR), Digital Content Ratings (DCR), and Digital Ad Ratings (DAR). 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

Before you start the integration, you will need:

Item Description Source
"App ID (appid)" Unique ID assigned to the player/site and configured by product. Contact Nielsen
"Nielsen SDK" Includes SDK frameworks and "sample implementation"; "See Android SDK Release Notes" Download

Step 1: Setting up your Android Development Environment


1) Ensure to unzip the Nielsen App SDK zip file and copy the "AppSdk.jar" into the app/libs folder on the App’s project. Add it as dependency.
2) Add the following permissions on the project’s "AndroidManifest.xml" file.

<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. Libraries:

  • com.google.android.gms:play-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.
  • The Android OS hosting the App SDK should use a media player supporting HLS streaming (Android 4.0 and later will support it natively).
  • If the player application uses a 3rd party media player implementing its own HLS/MPEG-DASH stack, then the minimum Android version will be limited to version 2.3, since the SDK depends on Google Play support to work properly.

Step 2: Create SDK Instance

The latest version of the Nielsen App SDK allows instantiating multiple instances of the SDK object when needed, which can then be used simultaneously. For the general use case where only one video is played at the same time in the App, a single instance of SDK object can then be used to play back and measure all watched streams one after another.

The following table contains the list of arguments that can be passed 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 "PXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
appname Name of the application value is automatically populated through App Name included in the App Resource File "Nielsen Sample App"
appversion Current version of the app used value is automatically populated through App Name included in the App Resource File "1.0.2"
nol_devDebug Enables Nielsen console logging. Only required for testing Nielsen-specified "DEBUG"


1) AppSDK() is no longer a singleton object and should be created as below.

try{
  // Prepare AppSdk configuration object (JSONObject)
  JSONObject appSdkConfig = new JSONObject()
          .put("appid", "PXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
          .put("nol_devDebug", "DEBUG"); // only for debug builds

         // Pass appSdkConfig to the AppSdk constructor
         mAppSdk = new AppSdk(appContext, appSdkConfig, this);
}
catch (JSONException e){
         Log.e(TAG, "Couldn’t prepare JSONObject for appSdkConfig", e);
}

2) implement IAppNotifier into your activity like

public class MainActivity extends AppCompatActivity implements IAppNotifier

3) implement callback

@Override
public void onAppSdkEvent(long timestamp, int code, String description){
  Log.d(TAG, "SDK callback onAppSdkEvent " + description);
}

So whole Activity will look like

package com.example.josefvancura.nlsdemotmp;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import com.nielsen.app.sdk.*;

import org.json.JSONException;
import org.json.JSONObject;

public class MainActivity extends AppCompatActivity implements IAppNotifier {

    private AppSdk mAppSdk = null;
    private String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Context context = getApplicationContext();

        try{
            // Prepare AppSdk configuration object (JSONObject)
            JSONObject appSdkConfig = new JSONObject()
                    .put("appid", "PXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
                    .put("nol_devDebug", "DEBUG"); // only for debug builds

            // Pass appSdkConfig to the AppSdk constructor
            mAppSdk = new AppSdk(context, appSdkConfig, this ); // Notifier - activity implements IAppNotifier, callback in onAppSdkEvent()

        }
        catch (JSONException e){
            Log.e(TAG, "Couldn’t prepare JSONObject for appSdkConfig", e);
        }

    }

    @Override
    public void onAppSdkEvent(long timestamp, int code, String description) {
        Log.d(TAG, "SDK callback onAppSdkEvent " + description);
    }

}

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.

Life cycle of SDK instance

Life cycle of SDK instance includes four general states:

  1. ""Initial state"" – The SDK is not initialized and hence, not ready to process playing information. Once the SDK is moved out of this state, it needs instantiation of the new SDK instance in order to get the instance in the ""Initial state"".
  2. ""Idle state"" – The SDK is initialized and is ready to process playing information. Once Initialized, the SDK instance is not processing any data, but is listening for the play event to occur.
  3. ""Processing state"" – The SDK instance is processing playing information. API calls "play" and "loadMetadata" move the SDK instance into this state. In this state, the SDK instance will be able to process the API calls (see below)
  4. ""Disabled state"" – The SDK instance is disabled and is not processing playing information. SDK instance moves into this state in one of the following scenarios.
    1. Initialization fails
    2. appDisableApi is called
@property (assign) BOOL appDisableApi;

Step 3: Create Metadata Objects

The parameters passed must be either a JSON formatted string or a NSDictionary object. The JSON passed in the SDK must be well-formed.

  • NSDictionary object
    • If an object of unexpected type is passed to the method, the error message will be logged.
    • If string has invalid JSON format, the error message will be logged.
  • JSON value must be string value.
    • This includes boolean and numeric values. For example, a value of true should be represented with "true", number value 123 should be "123".
    • All the Variable Names like appid, appname, dataSrc, title, type etc. are case-sensitive. Use the correct variable name as specified in the documentation.
  • JSON string can be prepared using either raw NSString or serialized NSDictionary.


Create channelName Metadata

channelName should remain constant throughout the completion of an episode or live stream.

Key Description Values Required
channelName Any string representing the channel/stream custom

Create Content Metadata

Content metadata should remain constant throughout the entirety of an episode/clip including when ads play.


program and title metadata values should be passed to SDK as UTF-8 strings.

Keys Description Values Required
type type of asset "content" for main content
assetid unique ID assigned to asset custom
(no Special Characters)
program name of program custom
title episode name custom
length length of content in seconds Length of content in seconds (86400 seconds for live stream (24/7). For Event-Live streams planned length. For VoD video length)
airdate the original date (local time with hh:mm:ss as 24h time stamp) content became available. This date does not change if the content is rebroadcasted on linear TV. YYYYMMDD HH:MI:SS (if not known set it to eg. "19700101 00:00:00")
isfullepisode full episode flag
  • "y" full episode (full content for a program)
  • "n" non full episode (only one part of the entire content for a program , i.e. preview, sub-episodes)

adloadtype type of Ad load
  • "1" Linear - matches TV ad load
  • "2" Dynamic - Dynamic Ad Insertion (DAI)

stationId Unique Gracenote Station ID generated by Nielsen that uniquely identifies a live channel

provided by Nielsen

islivestn Indicates if a stream is playing on a live channel
  • "y" playing on a live channel
  • "n" otherwise i.e. VoD

pbstarttm Playback Start Time (UTC): Unix timestamp in seconds matching the broadcast time for Content and Ad when a user starts and joins a live stream (seconds since Jan-1-1970 UTC)

i.e. "1631098029" for live stream and ""(empty string) for a VoD.

subbrand vcid/sub-brand/Channel ID – value is automatically populated through provided AppID. In order to override the sub-brand configured to the App ID, value can be passed here (e.g. multiple sub-brands/Channels in the App)

provided by Nielsen

Create Ad Metadata (optional for DR)

The Ad Metadata (if applicable) should be passed for each individual ad.

Note: All metadata values should be passed as UTF-8 strings.

Keys Description Values Required
type type of Ad "preroll", "midroll", "postroll"
"ad" - If specific type can not be identified.
assetid unique ID assigned to Ad custom
(no Special Characters)
stationId Unique Gracenote Station ID generated by Nielsen that uniquely identifies a live channel

provided by Nielsen

islivestn Indicates if a stream is playing on a live channel
  • "y" playing on a live channel
  • "n" otherwise i.e. VoD

pbstarttm Playback Start Time (UTC): Unix timestamp in seconds matching the broadcast time for Content and Ad when a user starts and joins a live stream (seconds since Jan-1-1970 UTC)

i.e. "1631098029" for live stream and ""(empty string) for a VoD.

subbrand vcid/sub-brand/Channel ID – value is automatically populated through provided AppID. In order to override the sub-brand configured to the App ID, value can be passed here (e.g. multiple sub-brands/Channels in the App) provided by Nielsen

MetaData Example

JSONObject channelInfo = new JSONObject()
    .put("channelName","My Channel Name 1")

JSONObject contentMetadata = new JSONObject()
   .put("assetid", "unique_content_id")
   .put("type", "content")
   .put("program", "program name")
   .put("title", "episode title")
   .put("length", "length in seconds")
   .put("airdate", "20200713 10:22:00")
   .put("isfullepisode", "y")
   .put("adloadtype", "2")
   .put("subbrand", "c05")
   .put("stationId", "TESTXXYYSSWWLL")
   .put("islivestn", "y")
   .put("pbstarttm", "1631098029");

JSONObject adMetadata = new JSONObject()
   .put("assetid", "unique_postroll_ad_id")
   .put("type", "postroll"
   .put("subbrand", "c05")
   .put("stationId", "TESTXXYYSSWWLL")
   .put("islivestn", "y")
   .put("pbstarttm", "1631098029");

Step 4: Start the Measurement

Overview of SDK API Calls

play

The play method prepares the SDK for reporting once an asset has loaded and playback has begun. Use play to pass the channel descriptor information through channelName parameter when the user taps the ""Play"" button on the player. Call play only when initially starting the video.

mAppSdk.play(JSONObject channelInfo);

loadMetadata

Needs to be called at the beginning of each asset, pass JSON object for relevant content or ad. Make sure to pass as 1st loadMetadata for content at the begining of playlist - see below API call sequence examples.

mAppSdk.loadMetadata(JSONObject contentMetadata);

playheadPosition

Note: "setPlayheadPosition" has to be called every second and the value passed should match the broadcast time for live channel.
  • VOD: current position in seconds. Pass whole number that increments only by 1 like 0,1,2,3...
  • Live: Unix timestamp matching the broadcast time for Content and Ad (seconds since Jan-1-1970 UTC). Pass whole number that increments only by 1 like 1631098029,1631098030,1631098031,1631098032,...
mAppSdk.setPlayheadPosition(long videoPositon);

sendID3

Needs to be called when ID3 Tags are included in the Video Stream.

mAppSdk..sendID3(String id3);

ID3 Tags are parsed from the Video Stream, refer to the DK Reference Implementation sdkRefImplDK on how to parse ID3 Tags included in a sample HLS Stream. For further technical details, please contact your Technical Account Manager (TAM).

stop

Call when

  • ads complete playing
  • when a user pauses playback
  • upon any user interruption scenario - see bellow chapter Interruption scenario
mAppSdk.stop();

end

Call when the content asset completes playback. Stops measurement progress.

mAppSdk.end();



Start the Measurement

In order to start the measurement, follow the 3 first steps below for Content without Ads. When terminating the Content playback call end to terminate the Content Measurement for the given asset.

Playlist Sample code Description
1. Start of stream play(channelName) channelName contains JSON metadata of channel/video name being played
loadMetadata(contentMetadataObject) contentMetadataObject contains the JSON metadata for the content being played
2. Content is playing playheadPosition(position) playheadPosition is position of the playhead while the content is being played
sendID3(id3) id3 is an ID3 Tag parsed from the Video Stream, pass ID3 Tag immediately to SDK when found
3. End of Stream end Content playback is completed.

Step 5: Stop/Resume the Measurement for video Playback Interruptions

As part of integrating Nielsen App SDK with the player application, the Audio / Video app developer needs to handle the following possible interruption scenarios:

  • Pause / Play
  • Network Loss (Wi-Fi / Airplane / Cellular)
  • Call Interrupt (SIM or Third party Skype / Hangout call)
  • Alarm Interrupt
  • Content Buffering
  • Device Lock / Unlock (Video players only, not for Audio players)
  • App going in the Background/Foreground (Video players only, not for Audio players)
  • Channel / Station Change Scenario
  • Unplugging of headphone

In case of encountering one of the above interruptions, the player application needs to

  • Call stop immediately (except when content is buffering) and withhold sending playhead position.
  • Once the playback resumes, start sending pings playheadPosition for the new viewing session.

Please see the Digital Measurement FAQ for more details

Step 6: Review SDK Integration Architecture Diagram

For Content Playback

SDK Integration Architecture Diagram - Content

For Ad Playback

SDK Integration Architecture Diagram - Ad

Step 7 : Disclose Nielsen Privacy Statement

The App SDK uses Mobile Ad IDs (Android ID or IDFA) which are fully hashed on the device before being sent to Nielsen (Nielsen never receives un-hashed values). Users retain the possibility to oppose the use of Mobile Ad IDs, or to reset them, by using the functionality provided by the mobile operating system (iOS or Android).

In order to disclose Nielsen measurement privacy statement, please include the following items in your privacy policy:

Step 8 : Review the Reference Implementation for VoD and Live Streams

The Reference Implementation covers VoD and Live use cases. It also covers DAI (Dynamic Ad Insertion) with preroll Ad.

Download the Reference Implementation for Android DKRefPLAYER. Unzip and open the project in Android Studio, then run it i.e. in the simulator or on Android device and then filter the Logcat output with ">>>" in order to see only relevant Nielsen SDK API Calls, as below:

2021-04-08 15:11:27.075  D/NielsenInit: ====>>> SDK CALL : class NielsenInit :: Create new AppSdk() Instance.
2021-04-08 15:11:27.568  D/NielsenInit: ====>>> SDK EVENT - onAppSdkEvent: Description = Nielsen App SDK is initiated. AppSdk.jar aa.8.0.0.0_gsxaon. App SDK was successfully initiatedCode l param : 1617887487Code i param 2000
2021-04-08 15:11:29.497  D/NielsenInit: ====>>> SDK EVENT - onAppSdkEvent: Description = Nielsen App SDK has started up. AppSdk.jar aa.8.0.0.0_gsxaon. Config file successfully loaded and parsed.Code l param : 1617887489Code i param 2001
2021-04-08 15:11:42.736  D/MainActivity: ====>>> SDK CALL :  appSdk.play(sdkMethods.loadChannelInfo());
2021-04-08 15:11:42.738  D/MainActivity: ====>>> SDK CALL : onPrepared() : appSdk.loadMetadata(data)
2021-04-08 15:11:43.065  D/MainActivity: ====>>> Media Player Event : onInfo ();what = 3
2021-04-08 15:11:43.065  D/MainActivity: ====>>> Media Player Event :  first video frame pushed for rendering.
2021-04-08 15:11:43.747  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 0 / 224
2021-04-08 15:11:44.752  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 1 / 224
2021-04-08 15:11:45.759  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 2 / 224
2021-04-08 15:11:46.764  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 3 / 224
2021-04-08 15:11:47.767  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 4 / 224
2021-04-08 15:11:48.772  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 5 / 224
2021-04-08 15:11:49.777  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 6 / 224
2021-04-08 15:11:50.781  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 7 / 224
2021-04-08 15:11:51.819  D/MainActivity: ====>>> SDK CALL :   appSdk.setPlayheadPosition(mCurrentPosition): @ 8 / 224
2021-04-08 15:11:51.821  D/MainActivity: ====>>> Activity onPause()
2021-04-08 15:11:52.668  D/MainActivity: ====>>>: Activity onDestroy()
2021-04-08 15:11:52.668  D/MainActivity: ====>>> SDK CALL : terminateNielsenSDK () :appSdk.end();

Step 9 : Test your player by yourself

Guide

1. Connect your PC and test device (tablet or phone) via same router.
2. PC side: run Proxy sw (like Charles) and get local IP
3. Test device: modify Wifi setting to pass through Proxy IP from step 2.
4. Test device: run your player, launch video
5. PC side: filter traffic by "nmr" and confirm presence of GN pings

Example of GN ping

https://secure-dmk.nmrodam.com/cgi-bin/gn?prd=dcr&ci=us-500207&ch=us-500207_c77_P&asn=defChnAsset&fp_id=&fp_cr_tm=&fp_acc_tm=&fp_emm_tm=&ve_id=&devmodel=&manuf=&sysname=&sysversion=&sessionId=zlmmxkq867zt4bpnumlyz6dpn9hyp1610980356&tl=Episode%201&prv=1&c6=vc%2Cc77&ca=us-500207_c77_VID5556674-123456&cg=TAMSample%20DK&c13=asid%2CP10DF14BA-937E-436D-99DF-ED39A0422387&c32=segA%2CNA&c33=segB%2CNA&c34=segC%2CNA&c15=apn%2C&plugv=&playerv=&sup=1&segment2=&segment1=&forward=0&ad=0&cr=4_00_99_V1_00000&c9=devid%2C&enc=true&c1=nuid%2C999&at=view&rt=video&c16=sdkv%2Cbj.6.0.0&c27=cln%2C0&crs=&lat=&lon=&c29=plid%2C16109803568088038&c30=bldv%2C6.0.0.563&st=dcr&c7=osgrp%2C&c8=devgrp%2C&c10=plt%2C&c40=adbid%2C&c14=osver%2CNA&c26=dmap%2C1&dd=&hrd=&wkd=&c35=adrsid%2C&c36=cref1%2C&c37=cref2%2C&c11=agg%2C1&c12=apv%2C&c51=adl%2C0&c52=noad%2C0&sd=170&devtypid=&pc=NA&c53=fef%2Cy&c54=oad%2C20200713%2010%3A22%3A00&c55=cref3%2C&c57=adldf%2C2&ai=VID5556674-123456&c3=st%2Cc&c64=starttm%2C1610980392&adid=VID5556674-123456&c58=isLive%2Cfalse&c59=sesid%2Cgezrb92q4i9b9jg7acxgn783gjw0a1610980365&c61=createtm%2C1610980392&c63=pipMode%2C&c68=bndlid%2C&nodeTM=&logTM=&c73=phtype%2C&c74=dvcnm%2C&c76=adbsnid%2C&c77=adsuprt%2C2&uoo=&evdata=&c71=ottflg%2C0&c72=otttyp%2Cnone&c44=progen%2C&davty=0&si=http%3A%2F%2Fnielsenonlinesupport.com%2Fdk%2Findex.htm&c66=mediaurl%2Cassets%252FRTVOD_C3%252Fprog_index.m3u8&c62=sendTime%2C1610980392&rnd=714644

Step 10 : Provide your app for certification

Once ready please send your application to Nielsen local staff for certification.

Step 11 : Going Live

After the integration has been certified (but not prior that), disable debug logging by deleting {nol_devDebug: "DEBUG"} from initialization call - see Step 2.