dependencies {
//... other dependencies
implementation 'com.my.tracker:mytracker-sdk:3.3.+'
}
Integrate SDK into your Android project. As an example, use a ready-made demo app in Java and Kotlin.
Oldest Android version supported — 5.0 (API Level 21).
For Google Play apps, the project must have Google Play Services (com.google.android.gms:play-services-ads-identifier module) and Google Play Install Referrer (com.android.installreferrer:installreferrer module) enabled.
Add build.gradle file to your project dependencies section:
dependencies {
//... other dependencies
implementation 'com.my.tracker:mytracker-sdk:3.3.+'
}
Download the latest version of MyTracker.aar.
Add MyTracker.aar to your project as a dependency.
Add next dependencies to your application build.gradle file:
dependencies {
...
implementation 'com.android.installreferrer:installreferrer:2.2'
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.0'
implementation 'com.google.android.gms:play-services-appset:16.0.1'
}
(Optionally or when using jar) If using Proguard make sure to add an exception:
-keep class com.my.tracker.** { *; }
-dontwarn com.my.tracker.**
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient {
com.google.android.gms.ads.identifier.AdvertisingIdClient$Info getAdvertisingIdInfo(android.content.Context);
}
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info {
java.lang.String getId();
boolean isLimitAdTrackingEnabled();
}
-keep class com.android.installreferrer.** { *; }
-keep class com.android.vending.billing.** { *; }
-keep class com.android.billingclient.api.** { *; }
(Optionally or when using jar) Add required permissions to your app’s AndroidManifest.xml at "manifest" section:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
>
...
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
...
</manifest>
For correct MyTracker SDK operation, set it up and initialize inside of your app the onCreate
method of the Application
class. To initialize SDK, you have to enter your SDK_KEY
.
And before that, you may set different settings for the
tracker (configurations, parameters, deep links, etc.).
SDK_KEY
is generated automatically after you added your application to MyTracker.
To get the key, go to the Application list page, select the required application, and copy the key from the Overview tab.
public class YourApplicationClass extends Application
{
@Override
public void onCreate()
{
super.onCreate();
// Setting up the configuration if needed
MyTrackerParams trackerParams = MyTracker.getTrackerParams();
MyTrackerConfig trackerConfig = MyTracker.getTrackerConfig();
// …
// Setting up params
// …
// Initialize the tracker
MyTracker.initTracker(SDK_KEY, this);
}
}
class YourApplicationClass : Application()
{
override fun onCreate()
{
super.onCreate()
// Setting up the configuration if needed
val trackerParams = MyTracker.getTrackerParams()
val trackerConfig = MyTracker.getTrackerConfig()
// ...
// Setting up params
// ...
// Initialize the tracker
MyTracker.initTracker(SDK_KEY, this)
}
}
If you cannot initialize MyTracker inside the Application
class, you could do that in the first
starting activity of your application. However, in this case you must call the trackLaunchManually
method of MyTracker after initialization.
public class YourActivity extends Activity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Setting up the configuration if needed
MyTrackerParams trackerParams = MyTracker.getTrackerParams();
MyTrackerConfig trackerConfig = MyTracker.getTrackerConfig();
// ...
// Setting up params
// ...
// Initialize the tracker
MyTracker.initTracker(SDK_KEY, getApplication());
//Enable activity tracking
MyTracker.trackLaunchManually(this);
}
}
class YourActivity : Activity()
{
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
// Setting up the configuration if needed
val trackerParams = MyTracker.getTrackerParams()
val trackerConfig = MyTracker.getTrackerConfig()
// ...
// Setting up params
// ...
// Initialize the tracker
MyTracker.initTracker(SDK_KEY, getApplication())
MyTracker.trackLaunchManually(this)
}
}
Starting version 3.0.10 of MyTracker SDK, MyTracker automatically collects only resettable device IDs:
MyTracker uses this information for advertising by attributing installs to traffic sources and uniqueizing the device within the project. When publishing your app on Google Play, complete the Google Play Data Safety section according to our guidelines.
If your app is not published on Google Play or the project requires additional IDs, you can send them to MyTracker using the appropriate SDK method:
MyTrackerParams trackerParams = MyTracker.getTrackerParams();
trackerParams.setCustomParam("custom_param_key", "custom_param_value");
val trackerParams = MyTracker.getTrackerParams()
trackerParams.setCustomParam("custom_param_key", "custom_param_value");
This method supports the following custom_param_key
values:
According to the Google Play Privacy Policy, android_id cannot be used with GAID. However, it can be sent if the app is published in alternative app stores.
According to the Google Play Privacy Policy, imei cannot be used with GAID. However, it can be sent if the app is published in alternative app stores.
The sending of the listed identifiers is optional.
An example of initializing MyTracker with additional identifiers:
public class YourApplicationClass extends Application
{
// Method of receiving Android ID
static @Nullable String getAndroidId(Context context)
{
try
{
ContentResolver cr = context.getContentResolver();
if (cr != null)
{
return Settings.Secure.getString(cr, Settings.Secure.ANDROID_ID);
}
}
catch (Throwable e) {}
return null;
}
// Method of receiving MAC
static @Nullable String getMac()
{
try
{
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all)
{
if (!nif.getName().equalsIgnoreCase("wlan0"))
{
continue;
}
try
{
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null)
{
return null;
}
StringBuilder result = new StringBuilder();
for (byte b : macBytes)
{
result.append(String.format("%02X:", b));
}
int length = result.length();
if (length > 0)
{
result.deleteCharAt(length - 1);
}
return result.toString();
}
catch (Throwable e) {}
}
}
catch (Throwable e) {}
return null;
}
@Override
public void onCreate()
{
super.onCreate();
// Set up tracker configuration
MyTrackerParams trackerParams = MyTracker.getTrackerParams();
// Set up additional IDs sending
trackerParams.setCustomParam("android_id", getAndroidId(getApplicationContext()));
trackerParams.setCustomParam("mac", getMac());
MyTrackerConfig trackerConfig = MyTracker.getTrackerConfig();
// …
// Set up tracker parameters
// …
// Initialize tracker
MyTracker.initTracker(SDK_KEY, this);
}
}
class YourApplicationClass : Application()
{
override fun onCreate()
{
super.onCreate()
// Set up tracker configuration
val trackerParams = MyTracker.getTrackerParams()
// Set up additional IDs sending
trackerParams.setCustomParam("android_id", getAndroidId(applicationContext))
trackerParams.setCustomParam("mac", getMac())
val trackerConfig = MyTracker.getTrackerConfig()
// …
// Set up tracker parameters
// …
// Initialize tracker
MyTracker.initTracker(SDK_KEY, this)
}
// Method of receiving Android ID
fun getAndroidId(context: Context): String?
{
try
{
val cr = context.contentResolver
if (cr != null)
{
return Settings.Secure.getString(cr, Settings.Secure.ANDROID_ID)
}
} catch (e: Throwable)
{
}
return null
}
// Method of receiving MAC
fun getMac(): String?
{
try
{
val all: List<NetworkInterface> = Collections.list(NetworkInterface.getNetworkInterfaces())
for (nif in all)
{
if (!nif.name.equals("wlan0", ignoreCase = true))
{
continue
}
try
{
val macBytes = nif.hardwareAddress ?: return null
val result = StringBuilder()
for (b in macBytes)
{
result.append(String.format("%02X:", b))
}
val length = result.length
if (length > 0)
{
result.deleteCharAt(length - 1)
}
return result.toString()
}
catch (e: Throwable)
{
}
}
}
catch (e: Throwable)
{
}
return null
}
}
Configuration can be set up in the MyTrackerConfig
class instance available through the MyTracker.getTrackerConfig()
method.
All parameters can be set up in the MyTrackerParams
class instance available through
the MyTracker.getTrackerParams()
method. Currently available:
TrackingLaunchEnabled: app launch tracking. True by default.
@NonNull MyTrackerConfig setTrackingLaunchEnabled(boolean trackingLaunchEnabled)
fun setTrackingLaunchEnabled(trackingLaunchEnabled: Boolean): MyTrackerConfig
LaunchTimeout: an interval (in seconds) during which a new launch is not tracked and a session is not interrupted while app is in background. 30 seconds by default. Possible value range: 30-7200 seconds.
@NonNull MyTrackerConfig setLaunchTimeout(int seconds)
fun setLaunchTimeout(seconds: Int): MyTrackerConfig
BufferingPeriod: the time during which events are accumulated locally on the device before being sent to the MyTracker server. The default value is 900 seconds, allowed values are: 1-86400 seconds (1 day).
@NonNull MyTrackerConfig setBufferingPeriod(int seconds)
fun setBufferingPeriod(seconds: Int): MyTrackerConfig
ForcingPeriod: an interval (in seconds) starting from application install/update during which any new event will be send to the server immediately, without local buffering. Default value is set to 0 (immediate sending is disabled), allowed values are 0-432000 seconds (5 days).
@NonNull MyTrackerConfig setForcingPeriod(int seconds)
fun setForcingPeriod(seconds: Int): MyTrackerConfig
AutotrackingPurchaseEnabled: if payment events should be tracked automatically. True by default.
@NonNull MyTrackerConfig setAutotrackingPurchaseEnabled(boolean autotrackingPurchaseEnabled)
fun setAutotrackingPurchaseEnabled(autotrackingPurchaseEnabled: Boolean): MyTrackerConfig
LocationTrackingMode: geolocation tracking. NONE by default.
Available values:
If your application requests location access, you can enable this parameter to improve the accuracy of statistics related to the user geography. In some cases, location tracking also improves the attribution and predictive models of Fraud Scanner, Personalize, and LTV Predictions.
LocationTrackingMode is available since SDK version 3.3.0 instead of TrackingLocationEnabled
@NonNull MyTrackerConfig setLocationTrackingMode(@LocationTrackingMode int locationTrackingMode)
fun setLocationTrackingMode(@LocationTrackingMode locationTrackingMode: Int): MyTrackerConfig
TrackingPreinstallEnabled: pre-installs tracking with the System Properties method. True by default. MyTracker will catch pre-installs if you share an application and a special key-value with the device manufacturer. For details, see the documentation for method System Properties.
@NonNull MyTrackerConfig setTrackingPreinstallEnabled(boolean trackingPreinstallEnabled)
fun setTrackingPreinstallEnabled(trackingPreinstallEnabled: Boolean): MyTrackerConfig
ApkPreinstallParams: retail pre-installs tracking. MyTracker will catch pre-installs if you pass the build of your app with APK parameters to the retailer. For details, see the documentation for method ApkPreinstallParams.
@NonNull MyTrackerConfig setApkPreinstallParams(String apkPreinstallParams)
fun setApkPreinstallParams(apkPreinstallParams: String): MyTrackerConfig
The app build with APK parameters is for the retailer only. You should not upload APK to the app store, as all installs will be attributed to the one partner.
Region, where the data collection server is located.
Since March 1, 2023, the region
parameter is disabled.
Regardless of the selected value, the data will be sent to servers located in the Russian Federation. To select a different region, please contact our support team
Choose a region based, for example, on current legislation. Available values:
Region.RU — server on the territory of Russian Federation
Region.EU — server on the territory of Europe
@NonNull MyTrackerConfig setRegion(int region)
fun setRegion(region: Int): MyTrackerConfig
Enabling and disabling debug mode can be done via MyTracker
class static methods.
False by default.
@AnyThread
void setDebugMode(boolean debugMode)
@AnyThread
fun setDebugMode(debugMode: Boolean)
Set the customUserId
parameter to track user stats, not only device.
It's a unique user identifier in the project, that you specify at the time of registration.
The identifier should remain unchanged even when user switch devices.
The parameter allows you to estimate the size and activity of user base, regardless of the number of user devices.
Also, you can keep your customer data consistent if a user changes a device. For details, see the User tracking section.
It's essential to set the parameter before tracking events to pass user identifier with every event.
public void setUserInfo()
{
MyTrackerParams trackerParams = MyTracker.getTrackerParams();
// Set user id
trackerParams.setCustomUserId("user_id");
}
fun setUserInfo()
{
val trackerParams = MyTracker.getTrackerParams()
// Set user id
trackerParams.setCustomUserId("user_id")
}
If customUserId
was set to the application with an existing registered users, MyTracker cannot calculate exact Lifetime metrics without registration time.
For pre-registered users, Lifetime statistic will be count on the date of the first tracked event with customUserId
.
To turn off user tracking, pass an empty value in the customUserId
parameter.
Events can be sent via MyTracker
class static methods.
Before you call methods, set the customUserId parameter
to pass user identifier with every tracked event.
The following methods for event tracking are available:
Registration event. You should call the method right after user registered in the app.
Pass the user identifier in the parameter userId
.
userId is a required parameter since SDK version 2.0.8
@AnyThread
void trackRegistrationEvent(@NonNull String userId)
@AnyThread
fun trackRegistrationEvent(userId: String)
Login event. You should call the method right after user successfully authorized in the app. Pass the user identifier in the parameter userId.
userId is a required parameter since SDK version 2.0.8
@AnyThread
void trackLoginEvent(@NonNull String userId)
@AnyThread
fun trackLoginEvent(userId: String)
Invite event. Any optional parameters can be passed with event as "key-value" by the optional eventParams
parameter. Max key or value length — 255 chars.
@AnyThread
void trackInviteEvent()
@AnyThread
void trackInviteEvent(@Nullable Map<String, String> eventParams)
@AnyThread
fun trackInviteEvent()
@AnyThread
fun trackInviteEvent(eventParams: Map<String, String>?)
Level up event. The level
parameter is optional. Any optional parameters can be passed
with event as "key-value" by the optional eventParams
parameter. Max key or value length — 255
chars.
@AnyThread
void trackLevelEvent()
@AnyThread
void trackLevelEvent(@Nullable Map<String, String> eventParams)
@AnyThread
void trackLevelEvent(int level, @Nullable Map<String, String> eventParams)
@AnyThread
fun trackEvent(name: String)
@AnyThread
fun trackEvent(name: String, eventParams: Map<String, String>?)
Any user defined event with a custom name. Any optional parameters can be passed with event
as "key-value" by the optional eventParams
parameter. Max name, key or value length — 255 chars.
@AnyThread
void trackEvent(@Nullable String name)
@AnyThread
void trackEvent(@Nullable String name, @Nullable Map<String, String> eventParams)
@AnyThread
fun trackEvent(name: String)
@AnyThread
fun trackEvent(name: String, eventParams: Map<String, String>?)
Example:
Map<String, String> eventParams = new HashMap<>();
eventParams.put("someParamKey1", "someParamValue1");
eventParams.put("someParamKey2", "someParamValue2");
MyTracker.trackEvent("eventName", eventParams);
val eventParams = HashMap<String, String>()
eventParams["someParamKey1"] = "someParamValue1";
eventParams["someParamKey2"] = "someParamValue2";
MyTracker.trackEvent("eventName", eventParams);
Force sending events from local buffer to server and reset buffering timers.
The SDK, which reduces channel load and lowers impact on app performance, collects all data
on the device before sending it off to the server and regularly does so in a compressed
format. By default, data is sent every 15 minutes. The interval can be changed to anywhere
from 1 second to 1 day through the bufferingPeriod
property.
If the user has quit the app, the events will be sent during next launch.
It's extremely important to analyse certain events as soon as possible, especially in the
first sessions since installing the app. The flush()
method will help.
@AnyThread
void flush()
@AnyThread
fun flush()
MyTracker collects data about in-app payments and subscriptions.
If you use the Google Play Billing Library, for automatic
payment events tracking you must call the corresponding MyTracker method from onPurchasesUpdated method of
BillingClient
listener.
private BillingClient client;
...
client = BillingClient.newBuilder(context)
.setListener(new PurchasesUpdatedListener()
{
@Override
public void onPurchasesUpdated(int responseCode, @Nullable List<Purchase> purchases)
{
MyTracker.onPurchasesUpdated(responseCode, (List) purchases);
// Your code for payments handling
// ...
}
})
.build();
...
private var client: BillingClient? = null
// ...
client = BillingClient.newBuilder(this)
.setListener { billingResult, purchases ->
MyTracker.onPurchasesUpdated(billingResult.responseCode, purchases as List<Any>?)
// ...
// Your code for payments handling
// ...
}
.build()
// ...
If you use you own In-App Billing API implementation, for automatic payment events tracking you must call the corresponding MyTracker method from onActivityResult method of activity that started the payment process.
public static final int PURCHASE_REQUEST_CODE = 1001;
...
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PURCHASE_REQUEST_CODE)
{
MyTracker.onActivityResult(resultCode, data);
// Your code for payment handling
// ...
}
}
...
// ...
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
{
super.onActivityResult(requestCode, resultCode, data)
if (PURCHASE_REQUEST_CODE == requestCode)
{
MyTracker.onActivityResult(resultCode, data)
// ...
// Your code for payment handling
// ...
}
}
// ...
companion object
{
const val PURCHASE_REQUEST_CODE = 1001
}
If you would like to track payment events manually, you should disable auto tracking
by setting autotrackingPurchaseEnabled = false
and use MyTracker methods:
@AnyThread
void trackPurchaseEvent(@NonNull JSONObject skuDetails,
@NonNull JSONObject purchaseData,
@NonNull String dataSignature)
@AnyThread
void trackPurchaseEvent(@NonNull JSONObject skuDetails,
@NonNull JSONObject purchaseData,
@NonNull String dataSignature,
@Nullable Map<String, String> eventParams)
@AnyThread
fun trackPurchaseEvent(skuDetails: JSONObject,
purchaseData: JSONObject,
dataSignature: String)
@AnyThread
fun trackPurchaseEvent(skuDetails: JSONObject,
purchaseData: JSONObject,
dataSignature: String,
eventParams: Map<String, String>?)
If auto tracking is enabled, this methods calls will be ignored. Required parameters:
skuDetails
— return value of the getSkuDetails()
method call according to documentation
purchaseData
— JSON object in INAPP_PURCHASE_DATA field of the getBuyIntent()
method
return value, according to documentation
dataSignature
— string from INAPP_DATA_SIGNATURE field of the getBuyIntent()
method
return value
Since data returned by the getSkuDetails()
and getBuyIntent()
methods comes as Bundle with String
type fields in it, transformation to JSONObject before send is required.
Any optional parameters can be passed with event as "key-value" by the optional eventParams
parameter.
Max key or value length — 255 chars.
public static final int PURCHASE_REQUEST_CODE = 1001;
...
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PURCHASE_REQUEST_CODE && resultCode == RESULT_OK)
{
try
{
final String purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA");
final String dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE");
final JSONObject purchaseDataJson = new JSONObject(purchaseData);
// Receive skuDetails response
final JSONObject skuDetailsJson = obtainSkuDetailJson(purchaseData);
MyTracker.trackPurchaseEvent(skuDetailsJson, purchaseDataJson, dataSignature);
// Your code for payment handling
// ...
}
catch (Exception ignored)
{
}
}
}
...
// ...
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
{
super.onActivityResult(requestCode, resultCode, data)
// Checking if the request code is PURCHASE_REQUEST_CODE
if (PURCHASE_REQUEST_CODE == requestCode && RESULT_OK == resultCode && data != null)
{
try
{
val dataSignature = data.getStringExtra("INAPP_DATA_SIGNATURE") ?: return
val purchaseData = data.getStringExtra("INAPP_PURCHASE_DATA") ?: return
// Receive skuDetails response
val skuDetailsJson = obtainSkuDetailJson(purchaseData)
val purchaseDataJson = JSONObject(purchaseData)
MyTracker.trackPurchaseEvent(skuDetailsJson, purchaseDataJson, dataSignature)
}
catch (_: Exception)
{
}
}
}
// ...
companion object
{
const val PURCHASE_REQUEST_CODE = 1001
}
// ...
To send data from your server to MyTracker (for example, untracked data, offline events, etc.), you might need a special device identifier — instanceId
.
The instanceId
is a device identifier (UUID v4), that generated at the first app launch
and unchanged until a user delete the app (or app data) from device.
The instanceId
can get via MyTracker
class static method (you should not use it in the main thread).
@WorkerThread
@NonNull String getInstanceId(@NonNull Context context)
@WorkerThread
fun getInstanceId(context: Context): String
It's essential to collect instanceId
as soon as possible
and send the identifier to your server
if you use this ID for work with S2S API.
Instead of instanceId
you can use any other device ID:
gaid
, androidId
, appSetId
,
and/or the user identifier customUserID
(in this case, S2S data will generate stats on users).
Learn more
Deep links allow sending additional parameters to the app. So a user can go to a specific app screen. There are two types of deep links: regular when parameters to be sent on a launch of the app and deferred when parameters to be sent on a launch of the app after the install. For more details, see the Deep Links section.
To allow MyTracker to handle regular deep links, in each application activity which support
deep links in the onCreate
and onNewIntent
methods you must call the handleDeeplink
method of MyTracker.
public class SomeActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate();
// ...
String deeplink = MyTracker.handleDeeplink(getIntent());
if(deeplink != null)
{
// Handle deep link
}
}
@Override
protected void onNewIntent(Intent intent)
{
super.onNewIntent(intent);
// ...
String deeplink = MyTracker.handleDeeplink(intent);
if(deeplink != null)
{
// Handle deep link
}
}
}
class SomeActivity : Activity()
{
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
// ...
val deeplink = MyTracker.handleDeeplink(intent)
if (deeplink != null)
{
// ...
// Handle deep link
// ...
}
}
override fun onNewIntent(intent: Intent?)
{
super.onNewIntent(intent)
// ...
val deeplink = MyTracker.handleDeeplink(intent)
if (deeplink != null)
{
// ...
// Handle deep link
// ...
}
}
}
To support deferred deep links in your app you need to add a listener which implement the
MyTracker.AttributionListener
interface. The listener would be called out just once, at first launch
of the app if a deferred deep link is found.
As a parameter to the onReceiveAttribution
method, the MyTrackerAttribution
attribution object
containing the deep link property would be passed. Check that the property value belongs to your app
otherwise a third-party app might open when using Intent transition.
public class YourApplicationClass extends Application
{
@Override
public void onCreate()
{
super.onCreate();
// Setting up the configuration if needed
MyTrackerParams trackerParams = MyTracker.getTrackerParams();
MyTrackerConfig trackerConfig = MyTracker.getTrackerConfig();
// ...
// Setting up params
// ...
// Set listener to receive the deferred deep link
MyTracker.setAttributionListener(new MyTracker.AttributionListener()
{
@Override
public void onReceiveAttribution(MyTrackerAttribution attribution)
{
String deeplink = attribution.deeplink;
// Processing the deferred deep link
// ...
}
});
// Initialize the tracker
MyTracker.initTracker(SDK_KEY, this);
}
}
class YourApplicationClass : Application
{
override fun onCreate()
{
super.onCreate()
// Setting up the configuration if needed
val trackerParams = MyTracker.getTrackerParams();
val trackerConfig = MyTracker.getTrackerConfig();
// ...
// Setting up params
// ...
// Set listener to receive the deferred deep link
MyTracker.setAttributionListener { myTrackerAttribution ->
val deeplink = myTrackerAttribution.deeplink
// ...
// Handle the deferred deep link
// ...
}
// Initialize the tracker
MyTracker.initTracker(SDK_KEY, this)
}
}
You may set up a Handler for listener method execution when setting the listener with the
setAttributionListener(AttributionListener attributionListener, Handler handler)
method. If no
Handler is set up when installing the listener then the onReceiveAttribution
method would be called
in the main application flow.