Add to the /app/build.gradle
or /app/settings.gradle
file, depending on your project, the repository where EMMA is located.
repositories {
maven { url 'https://repo.emma.io/emma' }
}
Add the dependency in the same build.gradle
file.
If you are using the new Android Gradle Plugin version 8 and have the minifyEnabled option set to true you need to update the SDK to version 4.14.1 or higher to avoid a possible crash with one of our dependencies. If the update is not possible, please modify the Proguard rules in the section below.
dependencies {
implementation 'io.emma:eMMaSDK:4.14.+'
}
dependencies {
implementation("io.emma:eMMaSDK:4.14.+")
}
If you use proguard see the documentation about proguard.
Finally, compile the project with gradle.
The latest version of the SDK is 4.15.5. See this page for details on updates.
Session Key
EMMA Key
on the EMMA documentation pageThe SDK contains by default the following mandatory permissions. These permissions DO NOT have to be added in the AndroidManifest.xml
of the application, as they are added by the SDK itself:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="com.google.android.gms.permission.AD_ID"/>
If you want to enable localization, you have to add the following permissions to the AndroidManifest.xml
of your application:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
If the app is going to be uploaded to AppGallery or run on HMS compatible devices, it is necessary to add the connection with Huawei and the library to get a unique identifier for the device.
In the build.gradle
of the project add the Huawei plugin.
buildscript {
repositories {
google()`
jcenter()
maven { url 'http://developer.huawei.com/repo/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.1'
classpath 'com.huawei.agconnect:agcp:1.6.2.300'
}
}
allprojects {
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
}
In the build.gradle
of the application, add the following dependency and apply the plugin to collect the configuration information of the app in Huawei.
dependencies {
// other dependencies
implementation 'com.huawei.hms:ads-identifier:3.4.62.300'
}
apply plugin: 'com.huawei.agconnect'
To obtain credentials you first need to create an account at developer.huawei.com and become a developer.
Once you have created an account you have to add an application in Huawei AppGallery Connect. The app is created in draft mode, to make the push integration is your client.
Once the app is created we can activate the Push Kit (it is optional, only if you want integrate push notifications) in the develop section. Inside Project Settings the information about the app will appear.
It is necessary to add the fingerprint of the certificate used for signing the app.
We download the agconnect-services.json
and add it in /app
. This way the HMS service will be able to authenticate the app.
Add the appId and appSecret in the app preferences section of the EMMA dashboard.
In your Application
class, add the following:
import android.app.Application
import io.emma.android.EMMA
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
val configuration = EMMA.Configuration.Builder(this)
.setSessionKey("example0ikl98")
.setDebugActive(BuildConfig.DEBUG)
.build()
EMMA.getInstance().startSession(configuration)
}
}
import android.app.Application;
import io.emma.android.EMMA;
public class ExampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
EMMA.Configuration configuration = new EMMA.Configuration.Builder(this)
.setSessionKey("example0ikl98")
.setDebugActive(BuildConfig.DEBUG)
.build();
EMMA.getInstance().startSession(configuration);
}
}
Screen sending is enabled by default in the EMMA SDK. To disable it use the following configuration.
import android.app.Application
import io.emma.android.EMMA
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
val configuration = EMMA.Configuration.Builder(this)
.setSessionKey("example0ikl98")
.trackScreenEvents(false)
.setDebugActive(BuildConfig.DEBUG)
.build()
EMMA.getInstance().startSession(configuration)
}
}
Instead of doing it from your Application
class, you can defer SDK initialisation by calling startSession
from an Activity
class.
If the application invokes
startSession
from anActivity
class, it is necessary to provide the context of that activity to the SDK withsetCurrentActivity()
.
Example of use:
class ExampleActivity : ComponentActivity() {
override fun onCreate() {
super.onCreate()
val configuration = EMMA.Configuration.Builder(this)
.setSessionKey("example0ikl98")
.setDebugActive(BuildConfig.DEBUG)
.build()
EMMA.getInstance().startSession(
configuration,
EMMASessionStartListener {
EMMA.getInstance().setCurrentActivity(this)
}
)
}
}
Configure sub-domain for POWLINK
To support Powlink on Android, see the support guide to configure Powlink on the EMMA dashboard.
Knowing the subdomain that corresponds to your POWLINKS, you must add a new activity to the AndroidManifest.xml
of your application as indicated below. This activity is the one that must also be used for the deeplink, as explained below.
<activity
android:name="io.emma.android.activities.EMMADeepLinkActivity"
android:noHistory="true"
android:exported="true"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="{YOUR_DEEPLINK_SCHEME}"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:host="subdomain.powlink.io"
android:scheme="https"/>
<data
android:host="shortsubdomain.pwlnk.io"
android:scheme="https"/>
</intent-filter>
</activity>
As detailed below in the Rich push URL uso DeepLinking section, it is necessary to add a metadata within the <application>
tag that specifies the activity in charge of handling the POWLINK url.
<meta-data
android:name="io.emma.DEEPLINK_OPEN_ACTIVITY"
android:value="com.your.package.CustomDeeplinkActivity"/>
As we said, the Rich push URL uso DeepLinking section shows how you can handle the received POWLINKS or Deeplinks in order to process them and display the corresponding part of your application.
If you are using a tracker with a non-EMMA domain (.powlink.io or .pwlnk.io), it is necessary to add the domain when starting the library.
val configuration = EMMA.Configuration.Builder(this)
.setSessionKey("example0ikl98")
.setDebugActive(BuildConfig.DEBUG)
.setShortPowlinkDomains("emma.link.mycompany.com")
.setPowlinkDomains("emma.link.mycompany.com")
.build()
EMMA.getInstance().startSession(configuration)
To differentiate a notification from our push system with respect to other systems, the payload sent by EMMA contains a flag called "eMMa".
EMMA offers a complete Push Notification delivery and reporting system that is easy to integrate using Firebase Cloud Messaging (FCM) on Android.
First get your own Sender ID and Server Key for FCM as specified in this article and set these parameters for the app in your EMMA account.
To integrate FCM into your app follow the steps below:
AndroidManifest.xml
:<service
android:name="io.emma.android.push.EMMAFcmMessagingService"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"/>
</intent-filter>
</service>
build.gradle
located in the root of the project:// build.gradle file
buildscript {
// ...
dependencies {
// ...
classpath 'com.google.gms:google-services:4.3.10' // google-services plugin`
}
}
allprojects {
// ...
repositories {
// ...
google() // Google's Maven repository`
}
}
// <project>/build.gradle.kts file
plugins {
// Add the dependency for the Google services Gradle plugin
id("com.google.gms.google-services") version "4.3.10" apply false
}
// <project>/<app-module>/build.gradle.kts file
plugins {
// Add the Google services Gradle plugin
id("com.google.gms.google-services")
}
build.gradle
of the app we add the Firebase dependency:apply plugin: 'com.android.application'
android {
// ...
}
dependencies {
//
// ...
implementation 'io.emma:eMMaSDK:4.12.+'
implementation 'com.google.firebase:firebase-messaging:23.1.0'
}
// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'
dependencies {
// ...
implementation("com.google.firebase:firebase-messaging:24.0.1")
}
Add the google-services.json
in the /app
directory inside the project. You can get here more information to obtain the file. Without this file push notifications will not work, it is mandatory to add it.
In the case of using a server key "Legacy " it is still valid and supported by Google, although it is recommended to use the FCM Server Key.
Start the push system under the Application
login:
override fun onCreate() {
super.onCreate();
EMMA.Configuration configuration = new EMMA.Configuration.Builder(this)
.setSessionKey("example0ikl98")
.setDebugActive(BuildConfig.DEBUG)
.build();
EMMA.getInstance().startSession(configuration);
val pushOpt = EMMAPushOptions.Builder(PushActivity::class.java, R.drawable.notification_icon)
.setNotificationColor(ContextCompat.getColor(this, R.color.yellow))
.setNotificationChannelName("Mi custom channel")
.build()
EMMA.getInstance().startPushSystem(configuration);
}
MyActivity.class: Activity you want to be opened when the Push is received.
R.drawable.icon: (Optional) The resource id of the image you want to display with the Push notification. If no icon is specified, the Push will use the app icon.
customDialog: (Optional) Set this to ensure that you want to use your Custom Dialog.
Since Android 13 to receive notifications it is necessary to request permission from the user. To do this, EMMA has added a method to the SDK available in version 4.12 or higher. For correct operation this method has to be called in an Activity. If the app already has a personalized alert or already integrates the permission request by default, there is no need to use this method:
class HomeActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
EMMA.getInstance().requestNotificationsPermission();
}
}
Add onNewIntent()
method calling EMMA.onNewNotification()
, which will check if the user has received a notification when the app is open.
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent);
EMMA.getInstance().onNewNotification(this, intent, true);
}
The boolean that has as parameter the
onNewNotification
method checks that the push contains Rich Push Url in case it is set to true.
To check the richPushUrl when the app is opened from the notification, add in the push activity the following:
class HomeActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
EMMA.getInstance().checkForRichPushUrl();
}
}
The
checkForRichPushUrl
may also be used anywhere in the app if preferred.
If your app is based on a WebView, you need to add the checkForRichPushUrl
in the following method:
webView.webViewClient = object : WebViewClient() {
...
override fun onPageFinished(view: WebView, url: String) {
EMMA.getInstance().checkForRichPushUrl();
}
}
Optionally, if you want to control what you receive from a Push, your Activity must implement the EMMANotificationInterface
interface and the onPushOpen
method that will be called when the user opens the notification.
class ExampleActivity : Activity(), EMMANotificationInterface {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
EMMA.getInstance().getNotificationInfo();
}
override fun onPushOpen(pushCampaign: EMMAPushCampaign) {
//Do whatever you want with push campaign
}
}
To disable notifications from a device, use EMMA.getInstance().unregisterPushService()
and make sure not to call EMMA.getInstance().startPushSystem(...)
again.
You can select the color you want in push notifications by adding the setNotificationColor
to the push options:
val pushOpt = EMMAPushOptions.Builder(PushActivity::class.java, R.drawable.notification_icon)
.setNotificationColor(ContextCompat.getColor(this, R.color.yellow))
.build()
EMMA.getInstance().startPushSystem(configuration);
SDK version 2.5.5 or higher is required.
To use custom sounds in the notifications you send with EMMA, you have to add the sound files you want to the raw folder of your app resources (res folder). Remember to use the same file names for the sounds in iOS and Android.
You can redirect push notifications openings to a section in your app. To do this you should use a structure like this:
scheme://host/page1/page2/page3...
Version 4.2 brings changes concerning the management of the deeplink. To integrate it follow the steps below:
Add the following activity in the AndroidManifest.xml
:
<activity
android:name="io.emma.android.activities.EMMADeepLinkActivity" android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="YOUR_SCHEME"`
android:host="YOUR_HOST"/>
</intent-filter>
</activity>
Add the activity to be launched, in the form of <meta-data>
inside the <application>
tag in AndroidManifest.xml
. This activity will be triggered when the SDK executes a deeplink:
<meta-data
android:name="io.emma.DEEPLINK_OPEN_ACTIVITY"
android:value="com.your.package.CustomDeeplinkActivity"/>
Remember that in case it is an activity that can be found open in the app when a deeplink is executed, it has to be declared in the AndroidManifest.xml
as singleTask
:
<activity`
android:name=".activities.MainActivity"
android:launchMode="singleTask"/>
In this case, if the activity is on the stack when the deeplink is executed, the intent with the deeplink information will reach the activity's onNewIntent
method. In case of not having the launchMode
as singleTask
it causes the activity to be instantiated again, which produces duplications.
For the deeplink that is entered in the Rich Push URL field (on the EMMA website) to be executed correctly, it is important to add the following method in the activity that is opened by the push or some other that is consequent to it, but that is executed when the app is opened from the notification:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
EMMA.getInstance().checkForRichPush();
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent);
EMMA.getInstance().onNewNotification(intent, true);
}
The checkForRichPush
and onNewNotification
methods check the Rich Push field sent in the notification payload and perform the appropriate actions. In the case of a deeplink they will execute it by opening the activity defined in the AndroidManifest.xml
metadata.
Define the behavior of the activity that has to open the deeplink. In this case CustomDeepLinkActivity
:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view);
if (intent != null && intent.data != null ) {
processDeepLink(intent.data);
}
finish();
}
private fun processDeepLink(uri: Uri) {
if (uri.host.equals("home")) {
goHome();
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent);
if (intent != null && intent.data != null ) {
processDeepLink(intent.data);
}
}
EMMA gives the possibility to integrate push notifications outside its own service.
class CustomService: FirebaseMessagingService() {
private fun isPushFromEMMA(remoteMessage: RemoteMessage): Boolean {
return remoteMessage.data["eMMa"] == "1"
}
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (isPushFromEMMA(remoteMessage)) {
EMMAPushNotificationsManager.handleNotification(applicationContext, remoteMessage.data)
}
}
override fun onNewToken(token: String) {
EMMAPushNotificationsManager.refreshToken(applicationContext, token, EMMAPushType.FCM)
}
}
On version 4.10.x, the functionality of adding buttons with actions in notifications has been developed. To add this functionality to Android, you simply have to have the SDK updated to this version.
For the configuration in the interface consult here.
On May 19, 2019, Google decided to terminate with Huawei those businesses that require hardware and software transfer, except those covered by open source licenses. This means that devices manufactured after this date will not have Google services such as the Play Store, although they will have the equivalent, the Huawei App Gallery.
From EMMA, we have modified our SDK to adapt it to the Huawei App Store. With the aim of measuring attribution as well as allowing Push notifications to be sent via their service.
The minimum version of the SDK available to make the Huawei integration is version 4.7.0. If you have an earlier version of the SDK, you will need to update it for everything to work properly.
To perform integration you have to follow the following steps:
The first thing we need to do is to add the dependencies to the build.gradle
of the project.
buildscript {
repositories {
google()`
jcenter()
maven { url 'http://developer.huawei.com/repo/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.4.1'
classpath 'com.huawei.agconnect:agcp:1.6.2.300'
}
}
allprojects {
repositories {
google()
jcenter()
maven {url 'https://developer.huawei.com/repo/'}
}
}
Next we add the HMS dependencies to the application. The ads-identifier dependency allows to get the advertisement identifier used in Huawei. The push dependency allows the use of the HMS service for push notifications.
dependencies {
// other dependencies
implementation 'com.huawei.hms:ads-identifier:3.4.62.300'
implementation 'com.huawei.hms:push:6.10.0.300'
}
apply plugin: 'com.huawei.agconnect'
The HMS service integration is compatible with the FCM integration in the EMMA SDK. The minimum Android version supported for HMS integration is Android 19.
The following service must be added to AndroidManifest.xml
:
<service
android:name="io.emma.android.push.EMMAHmsMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.huawei.push.action.MESSAGING_EVENT" />
</intent-filter>
</service>
To obtain the credentials go to the following section here.
Finally, we compile the app and we can now send push notifications to Huawei devices.
With EMMA you can perform a complete integration of the SDK that allows you to know the location of your users, how they register in your App, how many transactions they perform and even their own characteristics. That is, all the information about your users that you will get in the Behavior section.
The EMMA platform differentiates between two types of events. Those that the platform includes by default and the Custom events that you want to integrate according to the structure of your application.
You can find more information about default events here.
The EMMA.getInstance().registerUser()
method allows you to send information about registrations in the application.
fun register() {
EMMA.getInstance().registerUser("554234", "test@emma.io")
}
function register() {
EMMA.getInstance().registerUser("554234", "test@emma.io")
}
The EMMA.getInstance().loginUser()
method allows you to send information about login events.
If we have a successive login event with the same data, we can use the EMMA.loginDefault()
method. This method would be useful in the case of an "Auto-Login", for example.
fun login() {
EMMA.getInstance().loginUser("554234", "test@emma.io")
}
public void login() {
EMMA.getInstance().loginUser("554234", "test@emma.io");
}
EMMA allows you to track any transaction or purchase made in your app. This is an example to track a transaction:
fun trackTransaction() {
EMMA.getInstance().startOrder("<ORDER_ID>","<CUSTOMER_ID>",10.0, "")
EMMA.getInstance().addProduct("<PRODUCT_ID>", "<PRODUCT_NAME>", 1.0, 10.0)
EMMA.getInstance().trackOrder()
}
public void trackTransaction {
EMMA.getInstance().startOrder("<ORDER_ID>","<CUSTOMER_ID>",10.0, "");
EMMA.getInstance().addProduct("<PRODUCT_ID>", "<PRODUCT_NAME>", 1.0, 10.0);
EMMA.getInstance().trackOrder();
}
The method to start the transaction is EMMA.getInstance().startOrder()
.
EMMA.getInstance().startOrder("<ORDER_ID>","<CUSTOMER_ID>",10.0, "")
EMMA.getInstance().startOrder("<ORDER_ID>","<CUSTOMER_ID>",10.0, "");
Once the transaction has started, the products must be added to it. To do this we will use the EMMA.getInstance().addProduct()
method.
EMMA.getInstance().addProduct("<PRODUCT_ID>", "<PRODUCT_NAME>", 1.0, 10.0)
EMMA.getInstance().addProduct("<PRODUCT_ID>", "<PRODUCT_NAME>", 1.0, 10.0);
Once we have all the products added, we execute the measurement of the transaction with the EMMA.getInstance().trackOrder()
method.
EMMA.getInstance().trackOrder()
EMMA.getInstance().trackOrder();
In the event that it is necessary to cancel the tracking of a transaction, we will use the EMMA.getInstance().cancelOrder()
method.
fun cancelTransaction() {
EMMA.getInstance().cancelOrder("<ORDER_ID>")
}
public void cancelTransaction {
EMMA.getInstance().cancelOrder("<ORDER_ID>");
}
You can find more information about customized events here.
Example to send an application's own event:
val eventRequest = EMMAEventRequest("f983d4bef8fc44dad43a1bb30dde9e3c")
//Optional: custom attributes
eventRequest.attributes = attributes
//Optional: request status listener
eventRequest.requestListener = requestListener
//Optional: cumtom id for request delegate
eventRequest.customId = customId
EMMA.getInstance().trackEvent(eventRequest)
EMMAEventRequest eventRequest = new EMMAEventRequest("f983d4bef8fc44dad43a1bb30dde9e3c");
//Optional: custom attributes
eventRequest.setAttributes(attributes);
//Optional: request status delegate
eventRequest.setRequestDelegate(requestDelegate);
//Optional: cumtom id for request delegate
eventRequest.setCustomId(customId);
EMMA.getInstance().trackEvent(eventRequest);
In EMMA we can enrich the user profile with information stored by Key / Value which we call user tags.
To send this information we should implement the trackExtraUserInfo
:
val tags = ArrayMap<String, String>()
tags["MY_CUSTOM_TAG"] = "MY_CUSTOM_VALUE"
EMMA.getInstance().trackExtraUserInfo(tags)
Map<String, String> tags = new HashMap<>();
tags.put("MY_CUSTOM_TAG", "MY_CUSTOM_VALUE");
EMMA.getInstance().trackExtraUserInfo(tags);
If your application has location permissions, EMMA can store that information in the user's profile.
If you do not want to record the user's information, you must implement the disableTrackingLocation()
method before executing the startSession
call.
We can retrieve the internal EMMA identifier with the getUserID()
:
class MainActivity : BaseActivity(), EMMAUserInfoInterface {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
EMMA.getInstance().getUserID()
}
override fun OnGetUserInfo(userInfo: JSONObject?) {
// Not implemented
}
override fun OnGetUserID(id: Int) {
Log.d("MainActivity", id.toString())
}
}
public class MainActivity extends BaseActivity implements EMMAUserInfoInterface {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EMMA.getInstance().getUserID();
}
@Override
public void OnGetUserInfo(JSONObject userInfo) {
// Not implemented
}
@Override
public void OnGetUserID(int id) {
Log.d("MainActivity", Integer.toString(id));
}
}
The identifier format is UUID V4 type. To obtain the device identifier use the following method:
EMMA.getInstance().getDeviceId()
EMMA.getInstance().getDeviceId();
In the following example, we implement the EMMADeviceIdListener
interface and use the getDeviceId()
method. The device identifier is received in the onObtained()
function, which must be implemented by the interface:
class MainActivity : BaseActivity(), EMMADeviceIdListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
EMMA.getInstance().getDeviceId(deviceIdListener: this)
}
override fun onObtained(deviceId: String?) {
Log.d("MainActivity", "DeviceId: $deviceId")
}
}
public class MainActivity extends BaseActivity implements EMMADeviceIdListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EMMA.getInstance().getDeviceId(this);
}
@Override
public void onObtained(String deviceId) {
Log.d("MainActivity", "DeviceId: " + deviceId);
}
}
To send the customer ID independently of the login/registration use the following method:
EMMA.getInstance().setCustomerId("<Customer ID>")
EMMA.getInstance().setCustomerId("<Customer ID>");
Manually sets the user's preferred language.
This method allows overwriting the default language of the device to set a custom language to be used in all SDK requests. This is useful in applications that allow the user to select a different language than the one configured on the device.
The language code in ISO 639-1 format must be used: es
(Spanish), en
(English), fr
(French), de
(German), it
(Italian), zh-Hans
(Simplified Chinese), zh-Hant
(Traditional Chinese), etc.
// In this case, it establishes English as the language
EMMA.getInstance().setUserLanguage("en")
// In this case, it establishes English as the language
EMMA.getInstance().setUserLanguage("en");
If this method is not called, EMMA will default to the user's preferred language configured in the device system.
If we need to retrieve the user's profile from the application we will use the getUserInfo()
:
class MainActivity : BaseActivity(), EMMAUserInfoInterface {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
EMMA.getInstance().getUserInfo()
}
override fun OnGetUserInfo(userInfo: JSONObject?) {
userInfo?.let {
// Do something with userInfo
}
}
override fun OnGetUserID(id: Int) {
// Not implemented
}
}
public class MainActivity extends BaseActivity implements EMMAUserInfoInterface {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EMMA.getInstance().getUserInfo();
}
@Override
public void OnGetUserInfo(JSONObject userInfo) {
if (userInfo != null) {
// Do something with userInfo
}
}
@Override
public void OnGetUserID(int id) {
// Not implemented
}
}
After the attribution process, EMMA makes the user's attribution information available to the SDK.
To get the attribution information we will use the getInstallAttributionInfo
method:
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
EMMA.getInstance().getInstallAttributionInfo { attribution ->
if (attribution != null) {
// Do something with attribution
}
}
}
}
public class MainActivity extends BaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EMMA.getInstance().getInstallAttributionInfo(new EMMAInstallAttributionInterface() {
@Override
public void onAttributionReceived(EMMAInstallAttribution attribution) {
if (attribution != null) {
// Do something with attribution
}
}
});
}
}
See the description of attribution fields for available information.
If you use proguard or a compatible alternative, below is an example of the contents of the proguard-rules.pro
rules file. You may have to modify other rules depending on the application.
# EMMA SDK
-keep class io.emma.android.** { *; }
# Rules for play services ads identifier
-keep class com.google.android.gms.common.ConnectionResult {
int SUCCESS;
}
-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();
}
# Rule for google play referrer
-keep public class com.android.installreferrer.** { *; }
# Keep generic signatures; needed for correct type resolution
-keepattributes Signature
# Keep Gson annotations
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
-if class com.google.gson.reflect.TypeToken
-keep,allowobfuscation class com.google.gson.reflect.TypeToken
-keep,allowobfuscation class * extends com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowoptimization @com.google.gson.annotations.JsonAdapter class *
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.Expose <fields>;
@com.google.gson.annotations.JsonAdapter <fields>;
@com.google.gson.annotations.Since <fields>;
@com.google.gson.annotations.Until <fields>;
}
-keepclassmembers class * extends com.google.gson.TypeAdapter {
<init>();
}
-keepclassmembers class * implements com.google.gson.TypeAdapterFactory {
<init>();
}
-keepclassmembers class * implements com.google.gson.JsonSerializer {
<init>();
}
-keepclassmembers class * implements com.google.gson.JsonDeserializer {
<init>();
}
-if class *
-keepclasseswithmembers,allowobfuscation class <1> {
@com.google.gson.annotations.SerializedName <fields>;
}
-if class * {
@com.google.gson.annotations.SerializedName <fields>;
}
-keepclassmembers,allowobfuscation,allowoptimization class <1> {
<init>();
}
# Rules for retrofit2
-keepattributes Signature, InnerClasses, EnclosingMethod
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
-keepattributes AnnotationDefault
-keepclassmembers,allowshrinking,allowobfuscation interface * {
@retrofit2.http.* <methods>;
}
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn javax.annotation.**
-dontwarn kotlin.Unit
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface * extends <1>
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
-if interface * { @retrofit2.http.* public *** *(...); }
-keep,allowoptimization,allowshrinking,allowobfuscation class <3>
-keep,allowobfuscation,allowshrinking class retrofit2.Response
# Rules for okhttp3
-keepattributes Signature
-keepattributes Annotation
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
-dontwarn okio.**
# Rules for glide (used to display images by sdk)
-keep class com.bumptech.glide.** { *; }
-dontwarn com.bumptech.glide.**
# Rules for push
-keep class com.google.firebase.** { *; }
# Rules for Huawei hms push and odid identifier
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.huawei.hianalytics.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
# Rules for Huawei ads-identifier and ads referrer
-keep class com.huawei.hms.ads.** { *; }
-keep interface com.huawei.hms.ads.** { *; }
In some cases, it is necessary to change the EMMA API url (e.g. proxies), for this you have to add the following just before the startSession(...)
method:
val configuration = EMMA.Configuration.Builder(this)
.setSessionKey("example0ikl98")
.setWebServiceUrl("https://www.your_proxy_url.com/")
.setDebugActive(BuildConfig.DEBUG)
.build()
EMMA.Configuration configuration = new EMMA.Configuration.Builder(this)
.setSessionKey("example0ikl98")
.setDebugActive(BuildConfig.DEBUG)
.setWebserviceUrl("https://www.your_proxy_url.com/")
.build();
Messaging includes 7 different communication formats that you can integrate to impact your users:
EMMA NativeAd allows you to get the information of a NativeAd corresponding to a template that has been defined and configured in the EMMA platform.
class NativeAdsActivity: BaseActivity(),
EMMAInAppMessageInterface,
EMMABatchNativeAdInterface,
EMMANativeAdInterface {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun getNativeAd(templateId: String) {
val nativeAdRequest = EMMANativeAdRequest()
nativeAdRequest.templateId = templateId
EMMA.getInstance().getInAppMessage(nativeAdRequest, this)
}
fun getNativeAdBatch(templateId: String) {
val nativeAdRequest = EMMANativeAdRequest()
nativeAdRequest.templateId = templateId
nativeAdRequest.isBatch = true
EMMA.getInstance().getInAppMessage(nativeAdRequest, this)
}
override fun onBatchReceived(nativeAds: MutableList<EMMANativeAd>) {
nativeAds.forEach { nativeAd ->
nativeAd.tag?.let { tag ->
print("Received batch nativead with tag: $tag")
}
}
}
override fun onReceived(nativeAd: EMMANativeAd) {
val content = nativeAd.nativeAdContent
val title = content["Title"]
if (title != null) {
print("Received NativeAd with Title: $title")
EMMA.getInstance().sendInAppImpression(CommunicationTypes.NATIVE_AD, nativeAd)
}
}
fun openNativeAd(nativeAd: EMMANativeAd) {
EMMA.getInstance().openNativeAd(nativeAd)
}
fun sendNativeAdClick(nativeAd: EMMANativeAd) {
EMMA.getInstance().sendInAppClick(CommunicationTypes.NATIVE_AD, nativeAd)
}
override fun onShown(campaign: EMMACampaign) {
print("Invocadoo método onShown")
}
override fun onHide(campaign: EMMACampaign) {
print("Invocadoo método onHide")
}
override fun onClose(campaign: EMMACampaign) {
print("Invocadoo método onClose")
}
}
We will obtain all the information of the NativeAd available to the user regarding the templateId, according to the conditions that have been configured in the EMMA platform.
onReceived is called in case there is a NativeAd corresponding to the template with identifier "templateId".
onBatchReceived is called when the NativeAd call is made with the batch parameter set to true and there are one or more NativeAds corresponding to the template with identifier "templateId".
EMMANativeAd
contains all the fields configured in EMMA for this NativeAd template, the following method will be used to obtain them:
val content = nativeAd.nativeAdContent
val title = content["Title"]?.fieldValue
val image = content["Main picture"]?.fieldValue
val cta = content["CTA"]?.fieldValue
printSide(title, image, cta)
In the case of configuring a container template, the NativeAd values can be obtained as follows:
val content = nativeAd.nativeAdContent
val container = content["container"]
container?.fieldContainer?.forEach { containerSide ->
val title = containerSide["Title"]?.fieldValue
val image = containerSide["Main picture"]?.fieldValue
val cta = containerSide["CTA"]?.fieldValue
printSide(title, image, cta)
}
Once all the required fields have been obtained, the view can be created to paint this NativeAd on the screen depending on the design that you want to apply to it. Once the NativeAd has been painted on the screen, it is necessary to call this method to obtain the impressions in the reporting:
EMMA.getInstance().sendInAppImpression(CommunicationTypes.NATIVE_AD, nativeAd)
To obtain a unique NativeAd you have to use the method getInAppMessage
passing it an instance of EMMANativeAdRequest
with the specific templateId that you want to use. In the case of having several NativeAds under the same templateId with this method you get the last one created.
fun getNativeAd(templateId: String) {
val nativeAdRequest = EMMANativeAdRequest()
nativeAdRequest.templateId = templateId
EMMA.getInstance().getInAppMessage(nativeAdRequest, this)
}
Method used to obtain all the active NativeAds under the same templateId.
fun getNativeAdBatch(templateId: String) {
val nativeAdRequest = EMMANativeAdRequest()
nativeAdRequest.templateId = templateId
nativeAdRequest.isBatch = true
EMMA.getInstance().getInAppMessage(nativeAdRequest, this)
}
The SDK provides a method to perform the opening of a NativeAd, this method is set as a CTA when you click or perform some action on the NativeAd. Internally this method launches an inapp WebView or opens the browser with the CTA configured in the Dashboard and then traces the click.
fun openNativeAd(nativeAd: EMMANativeAd) {
EMMA.getInstance().openNativeAd(nativeAd)
}
Alternatively, if this method is not used, the click can be sent by calling the method:
EMMA.getInstance().sendInAppClick(nativeAd)
The StartView is a communication format that allows you to display HTML content, hosted at a URL, in a full-screen WebView.
fun getStartView() {
val startViewRequest = EMMAInAppRequest(EMMACampaign.Type.STARTVIEW)
EMMA.getInstance().getInAppMessage(startViewRequest)
}
The AdBall is a small circular view that displays an image. This view can be dragged all over the screen and removed from it at any time, it contains a CTA which is a URL with HTML content that launches a WebView when clicked.
fun getAdBall() {
val adBallRequest = EMMAInAppRequest(EMMACampaign.Type.ADBALL)
EMMA.getInstance().getInAppMessage(adBallRequest)
}
The banner is a communication format that allows to adapt an image or GIF in banner format within an application screen. This banner can be displayed on the screen where it is displayed or at the bottom of the screen. The banner contains a configurable CTA in the EMMA Dashboard and it can be a deeplink or an https url. In the case of the second option, when clicking it opens a WebView with the content of the url.
It is recommended to make the call after all the elements of the screen are loaded.
fun getBanner() {
val bannerRequest = EMMAInAppRequest(EMMACampaign.Type.BANNER)
EMMA.getInstance().getInAppMessage(bannerRequest);
}
The strip allows you to display a banner at the top of the device's screen with a text that scrolls as a carousel. Variables such as the duration of the rotation or the display time are configurable from the Dashboard.
fun getStrip() {
val stripRequest = EMMAInAppRequest(EMMACampaign.Type.STRIP)
EMMA.getInstance().getInAppMessage(stripRequest);
}
EMMA Coupons allows you to obtain, verify and redeem coupons that have been defined and configured on the EMMA platform.
class CouponsActivity: BaseActivity(), EMMACouponsInterface {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
EMMA.getInstance().addCouponsCallback(this)
getCoupons()
}
private fun getCoupons() {
EMMA.getInstance().getInAppMessage(EMMAInAppRequest(EMMACampaign.Type.COUPON))
}
private fun getSingleCoupon() {
val couponsRequest = EMMAInAppRequest(EMMACampaign.Type.COUPON)
couponsRequest.inAppMessageId = "<COUPON_ID>"
EMMA.getInstance().getInAppMessage(couponsRequest)
}
private fun redeemCoupon() {
val redeemCouponRequest = EMMAInAppRequest(EMMACampaign.Type.REDEEM_COUPON)
redeemCouponRequest.inAppMessageId = "<COUPON_ID>"
EMMA.getInstance().getInAppMessage(redeemCouponRequest)
}
private fun cancelCoupon() {
val cancelCouponRequest = EMMAInAppRequest(EMMACampaign.Type.CANCEL_COUPON)
cancelCouponRequest.inAppMessageId = "<COUPON_ID>"
EMMA.getInstance().getInAppMessage(cancelCouponRequest)
}
private fun couponValidRedeems() {
val couponValidRedeems = EMMAInAppRequest(EMMACampaign.Type.COUPON_VALID_REDEEMS)
couponValidRedeems.inAppMessageId = "<COUPON_ID>"
EMMA.getInstance().getInAppMessage(couponValidRedeems)
}
override fun onCouponsReceived(coupons: List<EMMACoupon>) {
coupons.let {
// Show coupons
coupons.forEach { coupon ->
EMMA.getInstance().sendInAppImpression(CommunicationTypes.COUPON, coupon)
}
}
}
override fun onCouponsFailure() {
print("An error has occurred obtaining coupons")
}
override fun onCouponRedemption(success: Boolean) {
print("Coupon redemption success: $success")
}
override fun onCouponCancelled(success: Boolean) {
print("Coupon cancelled success: $success")
}
override fun onCouponValidRedeemsReceived(numRedeems: Int) {
print("Coupon redeems: $numRedeems")
}
}
With this call, we will obtain all the information of the coupons available to the user, according to the conditions that have been configured in the EMMA platform.
private fun getCoupons() {
EMMA.getInstance().getInAppMessage(EMMAInAppRequest(EMMACampaign.Type.COUPON))
}
A list of existing coupons will be returned, listing first the automatic coupons sorted from most recent to oldest, and then the classic coupons will be listed, also sorted from most recent to oldest.
EMMACouponsInterface.onCouponsReceived
is called in case the user has coupons available to redeem or EMMACouponsInterface.onCouponsFailure
if the user has no coupons available. EMMACoupon
contains all the information related to the coupon: id (EMMA's internal identifier), code, maximum number of redemptions, number of times redeemed, title, description, image...
If you want to send to EMMA the information about when the coupon has been clicked or when it is displayed on the screen, you have to add the following methods when both actions are performed in the app:
EMMA.getInstance().sendInAppImpression(CommunicationTypes.COUPON, couponCampaign);
EMMA.getInstance().sendInAppClick(CommunicationTypes.COUPON, couponCampaign);
private fun cancelCoupon() {
val cancelCouponRequest = EMMAInAppRequest(EMMACampaign.Type.CANCEL_COUPON)
cancelCouponRequest.inAppMessageId = "<COUPON_ID>"
EMMA.getInstance().getInAppMessage(cancelCouponRequest)
}
With this call you can cancel the redemption of a previously made coupon.
The parameter couponId must be the EMMA internal identifier of a coupon identifier that can be obtained from a call to getCoupons
made previously. The EMMACouponsInterface.onCouponCancelled
is called, where success indicates whether the coupon has been cancelled.
As of version 4.9.0 the possibility to add in-app plugins to the SDK has been added. The in-app plugins work through NativeAd technology.
You can create your own communication format and turn it into an in-app plugin, for this it is necessary that the main class of the new format extends the abstract class EMMAInAppPlugin
, this class forces to override two methods:
class CustomInAppPlugin: EMMAInAppPlugin() {
override fun getId(): String = "emma-plugin-custom"
override fun show(context: Activity?, nativeAd: EMMANativeAd) {
// Process data
}
override fun dismiss() {
}
The show()
method is the main method as it is the one that launches the SDK when it receives the Native Ad corresponding to the plugin. The SDK passes to the plugin as context the current activity that is visible in the app and the NativeAd with the content according to the template marked as plugin in the Dashboard. With these parameters you can create any communication format adapted to the content of the NativeAd.
The dismiss()
method is to hide the plugin automatically. At the moment the SDK does not have this functionality built in, it leaves the control of hiding to the plugin itself.
The getId()
method returns the identifier of the plugin that corresponds to the templateId generated in the template.
The EMMAInAppPlugin
class contains several static methods such as sendInAppImpression
and sendInAppClick
. As in NativeAd, you can send these actions. You can also invoke the inappMessageListener
with the invokeShownListeners
, invokeCloseListeners
and invokeHideListeners
methods.
You can check the plugin example here.
To integrate a plugin it is necessary to add it in the sdk after the login, for this it is necessary to use the addInAppPlugin
method.
class App: Application {
override fun onCreate() {
super.onCreate()
....
EMMA.getInstance().startSession(configuration)
EMMA.getInstance().addInAppPlugins(CustomPlugin())
}
}
Once the sdk has the plugin you simply have to call it in the part of the app where it is required as if it were a NativeAd.
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val request = EMMANativeAdRequest()
request.templateId = "emma-plugin-custom"
EMMA.getInstance().getInAppMessage(request)
}
}
With this functionality we can limit the urls that the EMMA SDK will open, so that in In-App communications, only content that begins with one of the urls that we have indicated in the whitelist will be shown in a Webview.
If we do not indicate any url in the whitelist, any url is allowed. This functionality would affect Push (Rich URL), Banners, StartViews and AdBalls. Communications (Banners, StartViews and AdBalls) that can load external content to the app through a Webview and for Banners and AdBalls could load external images that would also be controlled by the whitelist.
For a Push with Rich URL, if it does not comply, the corresponding Webview would not be opened, but the push does reach the application. Note that if instead of a url a deeplink is used, the deeplink scheme must be added to the whitelist to be able to open it.
fun enableWhitelist() {
EMMA.getInstance().whitelist = mutableListOf("https://mydomain.com", "https://mydomain2.com")
}
If my whitelist is "http://mydomain.com".
Upload to EMMA dashboard a Banner with Target URL https://mydomain.com.
The Banner will not be displayed, we should add https://mydomain.com to the whitelist.
Configure a StartView with StartView URL http://www.mydomain.com in the EMMA dashboard.
The StartView will not be displayed, we should add http://www.mydomain.com to the whitelist.
Configure in the EMMA dashboard a Banner with Target URL http://mydomain.com/my/url and SmartPhone Banner URL http://subdomain.mydomain.com/my/image.
The Banner will not be displayed, the url of the image does not comply with the whitelist, we should add to the whitelist http://subdomain.mydomain.com.
We upload a Banner with Target URL http://mydomain.com/my/url/ to the EMMA dashboard.
The Banner will be displayed because the url entered in the Target URL field starts with the same protocol and domain as the whitelist url.
We configure in the EMMA dashboard a StartView with StartView URL http://mydomain.com/mypage.html¶m=value.
The StartView will be displayed because the url entered in the StartView URL field starts with the same protocol and domain as the whitelist url.
Before proceeding, it is necessary to add the parameters to the communication in the EMMA dashboard so that the SDK picks them up and makes them available to the application. To learn more.
We can get the campaign parameters once we have received the NativeAd, either in the onReceived
method or in onBatchReceived
. To learn more about its implementation.
Example of use with onReceived
:
class NativeAdsActivity: BaseActivity(), EMMANativeAdInterface {
// ...
override fun onReceived(nativeAd: EMMANativeAd?) {
print(nativeAd.params)
}
}
For the StartView, AdBall, Strip and Banner formats, the campaign parameters are obtained through the methods provided by the EMMAInAppMessageInterface
: onShown
, onHide
and onClose
.
Each of these methods receives as parameter an instance of EMMACampaign
, through which it is possible to access the campaign parameters through the campaign.params
property.
Usage example:
// EMMAInAppMessageInterface implementation
class MainActivity : BaseActivity(), EMMAInAppMessageInterface {
// ...
override fun onShown(campaign: EMMACampaign) {
if (campaign.params != null && campaign.params.isNotEmpty()) {
print(campaign.params)
}
}
override fun onHide(campaign: EMMACampaign) {
// Access to campaign.params if necessary
}
override fun onClose(campaign: EMMACampaign) {
// Access to campaign.params if necessary
}
}
EMMA allows you to disable tracking for those users who express this wish.
This method is the best way to adapt to the new RGPD (General Data Protection Regulation).
The user's communication to express this do-not-track option must be handled by the app by making a call to the following method:
public fun disableUserTracking(deleteUserData: Bool) {
/*
disableUserTracking() shuts down all communication with EMMA servers
If deleteUserData is true; EMMA will remove this device data from
their servers. This is a unrecoverable action.
*/
EMMA.getInstance().disableUserTracking(deleteUserData)
}
public void disableUserTracking(boolean deleteUserData) {
/*
disableUserTracking() shuts down all communication with EMMA servers
If deleteUserData is true; EMMA will remove this device data from
their servers. This is a unrecoverable action.
*/
EMMA.getInstance().disableUserTracking(deleteUserData)
}
In case you want to re-enable the user's communications, you can use this other method:
public func enableUserTracking() {
EMMA.getInstance().enableUserTracking()
}
- public void enableUserTracking {
EMMA.getInstance().enableUserTracking()
}
For all apps that are within the "Designed for Families" program, they need to meet a series of requirements regarding the information to be shared, for this EMMA has enabled a property at the start session to ensure compliance with this policy.
import android.app.Application
import io.emma.android.EMMA
class ExampleApplication : Application() {
override fun onCreate() {
super.onCreate()
val configuration = EMMA.Configuration.Builder(this)
.setSessionKey("example0ikl98")
.setDebugActive(BuildConfig.DEBUG)
.setFamiliesPolicyTreatment(true)
.build()
EMMA.getInstance().startSession(configuration)
}
}
import android.app.Application;
import io.emma.android.EMMA;
public class ExampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
EMMA.Configuration configuration = new EMMA.Configuration.Builder(this)
.setSessionKey("example0ikl98")
.setFamiliesPolicyTreatment(true)
.setDebugActive(BuildConfig.DEBUG)
.build();
EMMA.getInstance().startSession(configuration);
}
}
In addition, it is also important to disable the permission to collect the Google Advertising ID in the AndroidManifest.
<uses-permission android:name="com.google.android.gms.permission.AD_ID"
tools:node="remove"/>