Prerequisites

Minimal version of Android

The current version of the FollowAnalytics SDK works with Android API level 19 (KitKat) and above.

Before you start to integrate the SDK into your app, you need to register your app and generate its API key on the FollowAnalytics platform by following the instructions here.

Getting Started

In this section, you will find the basic steps to integrate the SDK into your app:

Once the integration is finished, you can validate your setup, and start tagging events and saving attributes in your application.

Install using Gradle

The best way to install the SDK is with Gradle:

  1. Fetch the library from the private nexus repository

  2. Add the FollowAnalytics SDK repository to your project-level build.gradle file:

    allprojects {
        repositories {
            ...
            maven {
                url 'https://nexus.follow-apps.com/nexus/content/repositories/releases/'
            }
        }
    }
    
  3. Add the FollowAnalytics SDK dependency to your module-level build.gradle file:

    dependencies {
      ...
      implementation "com.followapps.android:sdk:7.3.1"
      ...
    }
    

Additional step if your applicationId contains capital letters

You must follow the step below if your applicationId contains at least one capital letter. If this is not the case, please skip it.

  1. Add the following lines to your module-level build.gradle file:

    android {
      ...
        applicationVariants.all { variant ->
            variant.outputs.each { output ->
                def processManifest = output.getProcessManifestProvider().get()
                processManifest.doLast { task ->
                    def outputDirectory = task.multiApkManifestOutputDirectory.asFile.get()
                    File manifestOutFile = file("$outputDirectory/AndroidManifest.xml")
                    if (manifestOutFile.exists() && manifestOutFile.canRead() && manifestOutFile.canWrite()) {
                        def updatedManifest = manifestOutFile.getText().replaceAll("(?=com.followanalytics.scheme.placeholder)(.*)(?=\")",
                                variant.applicationId.toLowerCase())
                        manifestOutFile.write(updatedManifest, 'UTF-8')
                    }
                }
            }
        }
      ...
    }
    

Once installation is over you will notice that dependencies and permissions are automatically added. For more information on dependencies and permissions, refer to the two subsections .

Dependencies

Using Gradle, dependencies will automatically be added while merging the manifest. The FollowAnalytics SDK depends on the following libraries:

implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.google.android.gms:play-services-location:16.0.0'
implementation 'com.google.firebase:firebase-messaging:17.4.0'
implementation 'me.leolin:ShortcutBadger:1.1.19@aar'

ShortcutBadger dependency

The ShortcutBadger dependency allows devices to display the number of unread messages on the app icon. These are called badges and, unlike on iOS, they are automatically managed with this dependency.

Migration to AndroidX

Please note that the FollowAnalytics sdk uses AndroidX libraries. If your project does not use AndroidX, please make sure to migrate your project to use it. See this guide for more informations about the migration.

Permissions

Automatically added permissions

Using Gradle, the SDK automatically adds the following normal permissions to your app manifest.

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Initialize with your API key

Prepare your API key

Be sure to have your API key for this step of the configuration. If you are not sure where to find it, check instructions here.

To initialize the FollowAnalytics SDK, call FollowAnalytics.init(Context context, Configuration configuration) inside the onCreate() method of the Application class.

Open your Application subclass, or create a new one, and add the following lines to override the onCreate method:

public class MyAwesomeApplication extends Application {
  @Override
  public void onCreate(){
    [...]
    FollowAnalytics.init(this.getApplicationContext(), new FollowAnalytics.Configuration() {
      {
            this.apiKey = "MY_API_KEY";
            this.apiMode = BuildConfig.DEBUG ? ApiMode.DEV : ApiMode.PROD;
      }
    });
    [...]
  }
}
class MyAwesomeApplication : Application() {

    override fun onCreate() {
        [...]
        FollowAnalytics.init(this.applicationContext, object : FollowAnalytics.Configuration() {

            init {
                apiKey = "MY_API_KEY"
                apiMode = if (BuildConfig.DEBUG) ApiMode.DEV else ApiMode.PROD
            }
        })
        [...]
    }
}

If you just created an Application subclass, remember to declare it in your AndroidManifest.xml file:

<application
    android:name=".MyAwesomeApplication"
/>

Setting up push notifications

The FollowAnalytics SDK supports push notifications based on Firebase Cloud Messaging or Huawei Push Kit. To set up push notifications, you will need to:

Important

Push notifications don't work in debug. If you wish to test push notifications during development, which is highly recommended, you have to build your app in release.

Integrate only one push service at a time

You must add only one push SDK to your app (either Firebase or Huawei Mobile Services Core) at a time and configure it accordingly on the FollowAnalytics platform. If you want to support both services, you have to configure two different apps on the FollowAnalytics plaform.

Notification channel for Android 8.0 (Oreo)

Since Google has refactored its "Notification system" for Android 8.0 ("Oreo"), (details here) FollowAnalytics SDK uses default_notification_fa as the id for the NotificationChannel object.

Add the Firebase SDK

Migrating from GCM to FCM

If your app is using Google Cloud Messaging (GCM) for push campaigns, you just need to follow the Migrate to Firebase Cloud Messaging (FCM) guide and skip this section.

Register to Firebase

If you haven't done so yet, you will need to register your project to Firebase. To do so, follow the instructions from the official Firebase documentation.

Add your FCM key
  1. From the Firebase console home page, open your project.

  2. Click the Gear icon, and select Project settings:

  3. Select the Cloud Messaging tab, and copy the Server key:

  4. From the FollowAnalytics platform, go to Administration -> Apps -> YourApp, select the Push Settings tab, select the Google push service, and paste the Server key you copied previously into the Firebase Cloud Messaging (FCM) key field:

  5. Save the changes

Install Firebase Cloud Messaging
  1. Add the Firebase Cloud Messaging library dependency in your app-level build.gradle file:

    implementation "com.google.firebase:firebase-messaging:17.4.0"
    
  2. Create a class which extends FirebaseMessagingService and override onMessageReceived and onNewToken as following:

    import com.followanalytics.FollowAnalytics;
    import com.google.firebase.messaging.FirebaseMessagingService;
    import com.google.firebase.messaging.RemoteMessage;
    
    public class MyFirebaseMessagingService extends FirebaseMessagingService {
    
        /**
         * This implemenation allows the FA SDK to receive Push campaigns
         */
        @Override
        public void onMessageReceived(RemoteMessage remoteMessage) {
            FollowAnalytics.processFirebaseMessage(this, remoteMessage);
        }
    
    
        /**
         * This implemenation allows the FA SDK to send the device token to the FA platform
         */
        @Override
        public void onNewToken(String token){
            FollowAnalytics.setFirebasePushToken(token);
        }
    }
    
    import com.followanalytics.FollowAnalytics
    import com.google.firebase.messaging.FirebaseMessagingService
    import com.google.firebase.messaging.RemoteMessage
    
    class MyFirebaseMessagingService : FirebaseMessagingService() {
    
        /**
         * This implemenation allows the FA SDK to receive Push campaigns
         */
        override fun onMessageReceived(remoteMessage: RemoteMessage) {
            FollowAnalytics.processFirebaseMessage(this, remoteMessage)
        }
    
    
        /**
         * This implemenation allows the FA SDK to send the device token to the FA platform
         */
        override fun onNewToken(token: String) {
            FollowAnalytics.setFirebasePushToken(token)
        }
    }
    
  3. Add the service previously created to the AndroidManifest inside your <application> tag. Make sure to set android:name to the relative path to your service:

    <service
        android:name=".MyFirebaseMessagingService"
        android:exported="false">
        <intent-filter>
            <action android:name="com.google.firebase.MESSAGING_EVENT"/>
        </intent-filter>
    </service>
    

Check your Google Services configuration

Make sure Google Services used in your project is at least 4.0.1

Add the Huawei Mobile Services Core SDK

To have your app configured for Huawei Moble Services you have to:

Register to Huawei AppGallery Connect

If you haven't done so yet, you will need to create an AppGallery Connect Project and add your app to this project. To do so, follow the instructions from the official Huawei Push Kit documentation. Make sure to configure the Signing Certificate Fingerprint and to enable Push Kit, as described in Huawei's documentation.

Add your Client Id and Client Secret
  1. Sign in to AppGallery Connect and click My projects.

  2. Find your app project, and click the desired app name.

  3. In the App information section, make sure you added the agconnect-services.json file to your project after you enabled Push Kit when you followed the Huawei Push Kit documentation:

  4. Still in the App information section, make sure the SHA-256 certificate fingerprint is well defined:

  5. Still in the App information section, take note of the App ID and App Secret:

  6. From the FollowAnalytics platform, go to Administration -> Apps -> YourApp, select the Push Settings tab, select the Huawei push service, and copy the App ID and the App Secret fom the previous step into their related fields:

  7. Save the changes

Install Push Kit
  1. Add the Push Kit library dependency in your app-level build.gradle file:

    implementation 'com.huawei.hms:push:5.0.4.302'
    
  2. Create a class which extends HmsMessageService and override onMessageReceived and onNewToken as following:

    import com.followanalytics.FollowAnalytics;
    import com.huawei.hms.push.HmsMessageService;
    import com.huawei.hms.push.RemoteMessage;
    
    public class MyHuaweiMessagingService extends HmsMessageService {
    
        /**
         * This implemenation allows the FA SDK to receive Push campaigns
         */
        @Override
        public void onMessageReceived(RemoteMessage remoteMessage) {
            FollowAnalytics.processHmsMessage(this, remoteMessage);
        }
    
        /**
         * This implemenation allows the FA SDK to send the device token to the FA platform
         */
        @Override
        public void onNewToken(String token) {
            FollowAnalytics.setHmsPushToken(token);
        }
    }
    
    import com.followanalytics.FollowAnalytics
    import com.huawei.hms.push.HmsMessageService
    import com.huawei.hms.push.RemoteMessage
    
    class MyHuaweiMessagingService : HmsMessageService() {
    
        /**
         * This implemenation allows the FA SDK to receive Push campaigns
         */
        override fun onMessageReceived(remoteMessage: RemoteMessage) {
            FollowAnalytics.processHmsMessage(this, remoteMessage)
        }
    
        /**
         * This implemenation allows the FA SDK to send the device token to the FA platform
         */
        override fun onNewToken(token: String) {
            FollowAnalytics.setHmsPushToken(token)
        }
    }
    
  3. Add the service previously created to the AndroidManifest inside your <application> tag. Make sure to set android:name to the relative path to your service:

    <service
        android:name=".MyHuaweiMessagingService"
        android:exported="false">
        <intent-filter>
            <action android:name="com.huawei.push.action.MESSAGING_EVENT"/>
        </intent-filter>
    </service>
    

Setup the FollowAnalytics Notification Channel name (Android 8.0 and above)

When you initialize the SDK, you can setup the name of the notification channel (whose id is default_notification_fa as mentionned above) created by the SDK. This name will displayed to the user in your application's settings. To do so, you need to override the value of the notificationChannelName attribute of the configuration object that you pass to the FollowAnalytics.init(Context context, Configuration configuration) method as below :

public class MyAwesomeApplication extends Application {
  @Override
  public void onCreate(){
    [...]
    FollowAnalytics.init(this.getApplicationContext(), new FollowAnalytics.Configuration() {
      {
            this.apiKey = "MY_API_KEY";
            this.apiMode = BuildConfig.DEBUG ? ApiMode.DEV : ApiMode.PROD;
            this.notificationChannelName = "MY_CHANNEL_NAME";
      }
    });
    [...]
  }
}
class MyAwesomeApplication : Application() {

    override fun onCreate() {
        [...]
        FollowAnalytics.init(this.applicationContext, object : FollowAnalytics.Configuration() {

            init {
                apiKey = "MY_API_KEY"
                apiMode = if (BuildConfig.DEBUG) ApiMode.DEV else ApiMode.PROD
                notificationChannelName = "MY_CHANNEL_NAME"
            }
        })
        [...]
    }
}

Validate your setup

Validator

The SDK has a Validator that will ensure that everything is properly configured. To know more about what the validator can check and how, refer to its dedicated page.

Checking your SDK integration is valid can be done in two ways :

Analytics

To enrich User Analytics on the platform, FollowAnalytics SDK allows you to:

Logs and attributes can also be used to target specific audiences when creating campaigns on the platform. You can find more information about the difference between logs and attributes here.

Our CSM team can help you creating relevant logs in your app. You can also find our recommendations here.

Logging Events and Errors

You can log Events and Errors in your app by calling the following methods:

static boolean logEvent(String name);
static boolean logError(String name);

static boolean logEvent(String name, String details);
static boolean logError(String name, String details);

static boolean logEvent(String name, Map<String, String> details);
static boolean logError(String name, Map<String, String> details);

Those methods return true if the log is valid, otherwise false is returned and an error description is printed to the console. If details exceeds 60KB it will be considered as invalid. A valid log will be sent to the platform only if opt-in analytics is true.

Use name as a unique identifier for your log. Use details to pass specific context to your log.

The following code shows an example of how you can log the viewing and the purchasing of a product:

FollowAnalytics.logEvent("Product view", "RF1672") // Product viewing

HashMap<String, String> details = new HashMap<String, String>();
details.put("reference", "RF1672");
details.put("payment_mode", "credit_card");
details.put("color", "red");
FollowAnalytics.logEvent("Product purchased", details); // Product purchasing
FollowAnalytics.logEvent("Product view", "RF1672") // Product viewing

val details : HashMap<String, String> = HashMap<String, String>();
details.put("reference", "RF1672");
details.put("payment_mode", "credit_card");
details.put("color", "red");
FollowAnalytics.logEvent("Product purchased", details); // Product purchasing

Logging Locations

There are two ways for logging locations:

static boolean logLocation(double latitude, double longitude);
static boolean logLocation(Location location);

Those methods return true if the log is valid, otherwise false is returned and an error description is printed to the console:

A valid log will be sent to the platform only if opt-in analytics is true.

One way to use the logLocation method, is to implement a LocationCallback in your application, request location updates and log the last location update. FollowAnalytics SDK will take care of ignoring a location log if this location is too close from the last one:

LocationRequest locationRequest = LocationRequest.create()
        .setFastestInterval(5*1000)
        .setInterval(10*1000)
        .setMaxWaitTime(30*1000)
        .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient(your_context);
LocationCallback locationCallback = new LocationCallback() {
  @Override
  public void onLocationResult(LocationResult locationResult) {
    Location location = locationResult.getLastLocation();
    if (location != null) {
        FollowAnalytics.logLocation(location);
    }
  }
};
client.requestLocationUpdates(locationRequest, locationCallback, null);
val locationRequest = LocationRequest.create()
    .setFastestInterval(5 * 1000)
    .setInterval(10 * 1000)
    .setMaxWaitTime(30 * 1000)
    .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)

val client = LocationServices.getFusedLocationProviderClient(your_context)
val locationCallback = object : LocationCallback() {
    override fun onLocationResult(locationResult: LocationResult) {
        val location = locationResult.lastLocation
        if (location != null) {
            FollowAnalytics.logLocation(location)
        }
    }
}
client.requestLocationUpdates(locationRequest, locationCallback, null)

Last known location logs

If you don't request location updates, and you don't send location logs you can still have location logged thanks to the Configuration parameter lastKnownLocationEnabled.

If you set Configuration.lastKnownLocationEnabled to true, the SDK will send a location log whenever a new session is created and a valid location is available.

The SDK is started by default with Configuration.lastKnownLocationEnabled set to false.

For this feature to work on Huawei Mobile Services devices, you must add a dependency to HMS Location Kit into your gradle app module : implementation 'com.huawei.hms:location:5.1.0.300'

User Attributes

A user can be defined by setting its identifier. This identifier is a string that you provide to the SDK to uniquely identify a user of your app. It can be an e-mail address, an identifier from your own backend (for instance a primary key), a phone number, or anything else that could allow you to uniquely identify a user.

Attributes (like first name, date of birth...) can be associated to a user if it's defined. Attributes are associated to the device when no user is defined.

When a user is defined, a user profile is created server-side and can be shared across apps.

You can set predefined as well as custom attributes.

Define a user

To define a user, set its user ID:

FollowAnalytics.setUserId("UniqueIdentifier");

To remove a user (for example in case of a sign out), just pass null to setUserId:

FollowAnalytics.setUserId(null);

Predefined attributes

To set predefined attributes, you can call the following methods on the UserAttributes class provided by the SDK:

static boolean setFirstName(String firstName);
static boolean setLastName(String lastName);
static boolean setEmail(String email);
static boolean setDateOfBirth(Date birthDate);
static boolean setGender(Gender gender);
static boolean setCountry(String country);
static boolean setCity(String city);
static boolean setRegion(String region);
static boolean setProfilePictureUrl(String profilePictureUrl);

Those methods return:

For example, to set the user Joe's city to "Paris", you would proceed as follows:

FollowAnalytics.UserAttributes.setFirstName("Joe");
FollowAnalytics.UserAttributes.setCity("Paris");

Custom attributes

In addition to predefined attributes, you can set your own custom attributes.

Always double check your custom attribute types

When a value for an unknown attribute is received by the server, the attribute is declared with the type of that first value.

If you change the type of an attribute in the SDK, values might be refused server-side. Ensure the attribute types match. This could be done by comparing with the ones you have in the Profile Data tab in the Administration page on FollowAnalytics platform.

Set a custom attribute

To set custom attributes, you can call the following methods on the UserAttributes class provided by the SDK:

static boolean setNumber(String key, Integer value);
static boolean setNumber(String key, Long value);
static boolean setNumber(String key, Double value);
static boolean setNumber(String key, Float value);
static boolean setNumber(String key, BigDecimal value);
static boolean setString(String key, String value);
static boolean setBoolean(String key, Boolean value);
static boolean setDate(String key, Date value);
static boolean setDateTime(String key, Date value);

Those methods return:

For instance, to set the user's job:

FollowAnalytics.UserAttributes.setString("job", "Taxi driver");
Delete a custom attribute value

You can delete the value of an attribute using its key. For instance, to delete the user's job:

FollowAnalytics.UserAttributes.clear("job");
Set of attributes

You can add or remove an item to or from a set of attributes.

To add an item to a set:

FollowAnalytics.UserAttributes.addToSet("fruits", "apple"); // Adds "apple" to set "fruits"
FollowAnalytics.UserAttributes.addToSet("fruits", "strawberry"); // Adds "strawberry" to set "fruits"
FollowAnalytics.UserAttributes.addToSet("fruits", "lemon"); // Adds "lemon" to set "fruits"

// This is the equivalent, by using the variable number of arguments mechanism
FollowAnalytics.UserAttributes.addToSet("fruits", "apple", "strawberry", "lemon");

To remove an item from a set:

FollowAnalytics.UserAttributes.removeFromSet("fruits", "lemon"); // Removes "lemon" from set "fruits".
FollowAnalytics.UserAttributes.removeFromSet("fruits", "strawberry"); // Removes "strawberry" from set "fruits".

// This is the equivalent, by using the variable number of arguments mechanism
FollowAnalytics.UserAttributes.removeFromSet("fruits", "lemon", "strawberry");

To clear a set:

FollowAnalytics.UserAttributes.clearSet("fruits"); // Removes all items from set "fruits".

Opt-in Analytics

FollowAnalytics SDK opt-in analytics state define whether to send logs and attributes to the platform. Opt-in analytics is true by default, meaning logs and attributes will be sent to FollowAnalytics platform.

You can set it to opt-in analytics to false at SDK initialization by setting the optInAnalyticsDefault parameter to false in the FollowAnalytics.Configuration object passed to the initialization method. This is only used to set the default opt-in value at initialization. Moreover you can change the opt-in value at runtime.

To change opt-in analytics state at runtime, use the setOptInAnalytics method:

static void setOptInAnalytics(boolean state);

This value is persisted in memory and the opt-in analytics state is changed for all subsequent launch of your application, meaning optInAnalyticsDefault is ignored.

To retrieve the current opt-in analytics state, use the getOptInAnalytics method:

static boolean getOptInAnalytics();

GDPR

To request the data collected with FollowAnalytics SDK, call the following method on the GDPR class provided by the SDK:

static void requestToAccessMyData()

To delete the data that has been collected with FollowAnalytics SDK, call the following method on the GDPR class provided by the SDK:

 static void requestToDeleteMyData()

Campaigns

In this section, we cover all the necessary for your app to handle InApp and Push campaigns sent from the FollowAnalytics platform. We will also see how you can take advantage of the SDK's capabilities, with concret examples.

Before you start, make sure the SDK is properly integrated into your app by carefully follow our Getting Started guide.

Opt-in Notifications

The FollowAnalytics SDK opt-in notifications status defines whether your app will receive notifications from your Push campaigns. Opt-in notifications is true by default, meaning notifications from your Push campaigns will be received by your app. This status is independent from the Android system notification authorization, which is also needed by your app to display notifications.

Thanks to this opt-in notifications status, you can implement a UI in your app to allow to choose whether to receive notifications, without the need to go to the Android notification authorization settings. Note that the opt-in notifications status will have no effect if the Android system notification authorization is not allowed, and in this case, your app will not receive notifications from your Push campaigns, whatever the opt-in notifications status.

To update your app UI to reflect the current opt-in notifications status, use the getOptInNotifications method:

static boolean getOptInNotifications();

To update the current opt-in notifications status upon UI change, use the setOptInNotifications method:

static void setOptInNotifications(boolean state);

Handling transistion from opt-out to opt-in notifications

Just calling setOptInNotifications(true) after the user interacts with your app UI to opts-in for notifications could be insufficient if the Android system notification authorization is not allowed. For this reason, we recommend to implement the following flow after the user opts-in for notifications in your app:

  • Check the return value of FollowAnalytics.isRegisteredForPushNotifications() which is true only if Android system notification authorization AND the SDK opt-in notifications status are true.
  • If false, display a message and button to invite your user to enable the Android system notification authorization.
  • Call FollowAnalytics.openNotificationSettingsIfNeeded() when the user taps the button, to direct him to the Android notification settings screen.

Another possibility is to bypass the first two steps and only implement the last one. In this case the Android notification settings will be opened only if the system authorization was not allowed.

It's also possible to set the opt-in notifications status to false at the SDK initialization by setting the optInNotificationsDefault parameter to false in the Configuration object passed to the initialization method. This is only used to set the default opt-in value at the first launch of the app. Note that calling setOptInNotifications() will persist the opt-in notifications status for all subsequent launch of your application, meaning optInNotificationsDefault will be ignored.

Customize push notifications

To customize the appearence and behavior of Push campaigns notifications, you have to override the onNotificationBuilding() method in the FollowAnalytics.Configuration class, which is called right before the notification is actually displayed on the device. In your implementation, you can use the FollowAnalytics.NotificationBuilder provided to customize the notification layout, or to add action buttons to the notification, as well as the FollowAnalytics.Message to retrieve the Push campaign information:

new FollowAnalytics.Configuration() {
    // ...
    @Override
    public void onNotificationBuilding(@NonNull FollowAnalytics.NotificationBuilder notificationBuilder, @NonNull FollowAnalytics.Message message) {
        // Put your notification customization here
    }
    // ...
}
object : FollowAnalytics.Configuration() {
    // ...
    override fun onNotificationBuilding(FollowAnalytics.NotificationBuilder notificationBuilder,  FollowAnalytics.Message message) {
        // Put your notification customization here
    }
    // ...
}

The FollowAnalytics.NotificationBuilder class is a subclass of the Android NotificationCompat.Builder class. You can use it as you would do with a regular NotificationCompat.Builder instances, except for the following methods, which have been deactivated and have no effect in FollowAnalytics.NotificationBuilder:

Notification icon customization

The SDK will use your app's launcher icon as the default notification icon. You can change this behavior by adding a ic_fa_notification.png file in your drawable folders. If you wish to have a custom background color on your notification icon, you can define this value in value to your color.xml file as following: <color name="ic_fa_notification_color">#ff0000</color>. Make sure to follow Google's guidelines for notification icons on Android.

Interactive notifications

An Interactive Notification is a notification containing custom action buttons that the user can access by revealing the notification:

When tapped, an action button will:

To create a campaign with Interactive Notifications:

static final String DELETE = "Delete";
static final String VIEW = "View";
static final String REMIND = "Remind me tomorrow";
val DELETE = "Delete"
val VIEW = "View"
val REMIND = "Remind me tomorrow"

To add action buttons to your notification, you have to use either addAction(Context context, int icon, String actionLabel, String actionIdentifier) or addBackgroundAction(Context context, int icon, String actionLabel, String actionIdentifier) from theFollowAnalytics.NotificationBuilder class:

An example of adding buttons to the notification is available below. Please note how the "NewContent" field defined above is used.

    @Override
    public void onNotificationBuilding(@NonNull FollowAnalytics.NotificationBuilder notificationBuilder, @NonNull FollowAnalytics.Message message) {
        Context context = MyApplication.this;
        if (message.getCategory().equals("NewContent")) {
            notificationBuilder.addAction(context, android.R.drawable.ic_menu_send, context.getResources().getString(R.string.view), VIEW);
            notificationBuilder.addBackgroundAction(context, android.R.drawable.ic_menu_delete, context.getResources().getString(R.string.delete), DELETE);
            notificationBuilder.addBackgroundAction(context, android.R.drawable.ic_menu_agenda, context.getResources().getString(R.string.remind_me), REMIND);
        }
    }
    override fun onNotificationBuilding(context: Context, notificationBuilder: FollowAnalytics.NotificationBuilder, message: FollowAnalytics.Message) {
        val context = this@MyApplication
        if (message.category.equals("NewContent")) {
            notificationBuilder.addAction(context, android.R.drawable.ic_menu_send, context.resources.getString(R.string.view), VIEW)
            notificationBuilder.addBackgroundAction(context, android.R.drawable.ic_menu_delete, context.resources.getString(R.string.delete), DELETE)
            notificationBuilder.addBackgroundAction(context, android.R.drawable.ic_menu_agenda, context.resources.getString(R.string.remind_me), REMIND)
        }
    }

Choose carefully the action type

Make sure to properly define if an action should be executed in foreground or background, and use the right method to create it. Note that your your app will always be opened when the user taps on a foreground action button. Also, if your implementation of onNotificationAction() attempts to perform a foreground action (i.e. launching an Activity) from a background action button, this attempt will fail.

Behavior on notification action

By default, the SDK has the following behavior when the user opens a notification (i.e. tap on the notification's body) or taps on a notification's foreground action:

By default, the SDK does nothing if the following actions are performed on the notication:

You can define a custom behavior when the user interacts with a notification by implementing the onNotificationAction callback in FollowAnalytics.Configuration. This allows your app to follow a specific logic for each possible action on the notification.

You will find below an usage example for the onNotificationAction callback, taking into consideration we implemented customized notifications by adding a VIEW, DELETE and REMIND action buttons (see the example provided in the Interactive notifications section) :

new FollowAnalytics.Configuration() {

    /**
     * This method is executed when a action is performed on a notification.
     */
    @Override
    public void onNotificationAction(@NonNull FollowAnalytics.ActionInfo actionInfo) {
        switch (actionInfo.getIdentifier()) {
            case FollowAnalytics.ActionInfo.PUSH_OPEN:
                // The user tapped on the notification body
                if (!actionInfo.getParameters().isEmpty()) {
                    // Your behaviour to handle custom parameters (key/values)
                } else {
                    // Let the SDK opens the URL (default behavior)
                    super.onNotificationAction(actionInfo);
                }
                break;
            case FollowAnalytics.ActionInfo.PUSH_DISMISS:
                // Your behavior when the notification is explicitly dismissed by the user
                // Note this is a background action
                break;
            case VIEW:
                // Your behavior for a tap on the "View" foreground action button
                break;
            case DELETE:
                // Your behavior for a tap on the "Delete" background action button
                break;
            case REMIND:
                // Your behavior for a tap on the "Remind me tomorrow" background action button
                break;
        }
    }

}
object : FollowAnalytics.Configuration() {

    /**
     * This method is executed when a action is performed on a notification.
     */
    override fun onNotificationAction(actionInfo: ActionInfo) {
        when (actionInfo.identifier) {
            ActionInfo.PUSH_OPEN -> {
                // The user tapped on the notification body
                if (!actionInfo.parameters.isEmpty()) {
                    // Your behaviour to handle custom parameters (key/values)
                } else {
                    // Let the SDK opens the URL (default behavior)
                    super.onNotificationAction(actionInfo)
                }
            }
            ActionInfo.PUSH_DISMISS -> {
                // Your behavior when the notification is explicitly dismissed by the user
                // Note this is a background action
            }
            VIEW -> {
                // Your behavior for a tap on the "View" foreground action button
            }
            DELETE -> {
                // Your behavior for a tap on the "Delete" background action button
            }
            REMIND -> {
                // Your behavior for a tap on the "Remind me tomorrow" background action button
            }
        }
    }

}

The actionInfo argument provides all the action button details to your app. Here is a description of the FollowAnalytics.ActionInfo class :

Accessor Return type Description
getIdentifier() String This is the action's unique identifier
getMessageId() String Unique identifier of the associated campaign Message.
getCampaignName() String Name of the associated campaign as defined on the platform.
getOpeningUrl() String Url provided on the platform for this Push campaign.
getParameters() Map Custom parameters (key/value pairs) provided in the campaign editor.
Constant Type Description
PUSH_OPEN String Value of the actionInfo's unique identifier when the user click's on your notification's body (and not on an action button).
PUSH_DISMISS String Value of the actionInfo's unique identifier when the notification is explicitly dismissed by the user.

Call super.onNotificationAction(actionInfo) to execute the SDK default behaviour

If your Push campaign contains an url (or app-link), this method will let the android system handle it. Please note that the intent flags used for launching your app are FLAG_ACTIVITY_CLEAR_TASK and FLAG_ACTIVITY_NEW_TASK: this will clear any work in progress by the user in your application and open the main Activity (i.e. your home screen).

Behavior on Push message reception

You can handle the reception of a Push campaign message by overriding the onPushMessageReceived callback in FollowAnalytics.Configuration. This allows you to access any Push campaign details at the message reception time and perform specific logic accordingly:

new FollowAnalytics.Configuration() {
        ...
        @Override
        public void onPushMessageReceived(@NonNull FollowAnalytics.Message message) {
            // YOUR BEHAVIOR
        }
        ...
}
object : FollowAnalytics.Configuration() {
    ...
    override fun onPushMessageReceived(message: FollowAnalytics.Message) {
        // YOUR BEHAVIOR
    }
    ...
}

You can use this callback to perform specific logic when a silent push message is received, for instance to download content in the background.

Another usage could be to cancel a notification as soon as it arrives, according to a specific the notification from your code, you can use the Message.cancelNotification(Context). Here is an example of how to use it :

new FollowAnalytics.Configuration() {
    // ...
    @Override
    public void onPushMessageReceived(M@NonNull FollowAnalytics.Message message) {
        if (/* specific logic */) {
            message.cancelNotification(MyApplication.this);
        }
    }
    // ...
}
object : FollowAnalytics.Configuration() {
    // ...
    override fun onPushMessageReceived(message: FollowAnalytics.Message) {
        if (/* specific logic */) {
            message.cancelNotification(this@MyApplication)
        }
    }
    // ...
}

Behavior on native InApp action

The SDK has the following behavior when the user closes an native InApp (i.e. Native Alert or Evaluation Booster) is closed by tapping on one of its native buttons:

You can define a URL on a Native Alert button in the platform's campaign editor. For Evaluation Booster campaign, a URL is automatically set on the Rate button of the Positive answer dialog.

Additionally to the SDK behavior, you can define a custom behavior when the user closes a native InApp by implementing the onInAppNativeAction callback in FollowAnalytics.Configuration. This allows your app to follow a specific logic for each closing button:

new FollowAnalytics.Configuration() {

    /**
     * This method is executed when a Native Alert or Evaluation Booster dialog is closed by tapping a button.
     */
    @Override
    public void onInAppNativeAction(@NonNull FollowAnalytics.ActionInfo actionInfo) {
        // Your behavior
    }

}
object : FollowAnalytics.Configuration() {

    /**
     * This method is executed when a Native Alert or Evaluation Booster dialog is closed by tapping a button.
     */
    override fun onInAppNativeAction(actionInfo: ActionInfo) {
        // Your behavior
    }

}

The actionInfo argument provides all the InApp button details to your app. Here is a description of the FollowAnalytics.ActionInfo class :

Accessor Return type Description
getIdentifier() String This is the unique identifier for this button.
getMessageId() String Unique identifier of the associated campaign Message.
getCampaignName() String Name of the associated campaign as defined on the platform.
getOpeningUrl() String Url provided on the platform for this button.
getParameters() Map Custom parameters (key/value pairs) provided on the platform for this buton.
Constant Type Description
INAPP_CLOSE String Value of the actionInfo's unique identifier when an In-App Native Alert or Evaluation Booster "close" button is tapped by the user.
INAPP_RATE String Value of the actionInfo's unique identifier when the Evaluation Booster "rate" button is tapped by the user.
INAPP_POSITIVE String Value of the actionInfo's unique identifier when the Evaluation Booster dialog is closed after the "positive" answer is choosed by the user.
INAPP_NEGATIVE String Value of the actionInfo's unique identifier when the Evaluation Booster dialog is closed after the "negative" answer is choosed by the user.

Geofencing

The geofencing feature allows you to trigger campaigns when a user enters or leaves a given geographical area.

To use the geofencing feature, add the following permissions to your app manifest:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> (Since Android 10)
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

Dangerous permissions

The following permissions are labelled as dangerous since Android 6.0 and Android 10 respectively (find more information about dangerous permissions here):

  • android.permission.ACCESS_FINE_LOCATION
  • android.permission.ACCESS_BACKGROUND_LOCATION

Permissions and user experience

Note that each permissions results in a dialog being shown to your user. Instead of having them appear at installation, you can have them appear at the most relevant location in your app during runtime. To include permissions and keep a good user experience, we recommend you to follow the Android guidelines described here.

Pausing and resuming in-app campaigns

To pause and resume campaigns, add the following methods in you code at the location you wish the pause and resume to take effect:

FollowAnalytics.InApp.pauseCampaignDisplay();  // will pause campaign display
FollowAnalytics.InApp.resumeCampaignDisplay(); // will resume campaign display

Create safe spaces for you in-app messages

Rather than pause everywhere you have an important screen or process, you can pause right at the initialization of the SDK and resume in the areas you think it is safe for in-app messages to be displayed.

Archiving messages

FollowAnalytics SDK allows you to store campaigns's messages received by your app. This makes them available for custom usage, like building an inbox feature. Messages received by a device can be archived locally and accessed from the developer's code, formatted as a Message object. In order for you to enable the message archiving, you need to set the following Configuration properties at SDK initialization:

protected boolean archivePushMessages;
protected boolean archiveInAppMessages;

You will find below all the methods you need to handle all the archived Message.

FollowAnalytics.InApp.getAll() // Get all archived in-app messages
FollowAnalytics.Push.getAll()  // Get all archived push messages

FollowAnalytics.InApp.get("MESSAGE_ID") // Get an archived in-app message
FollowAnalytics.Push.get("MESSAGE_ID") // Get an archived push message

FollowAnalytics.InApp.delete(message.getId()) // Remove an in-app message from the archive
FollowAnalytics.Push.delete(message.getId())  // Remove a push message from the archive

FollowAnalytics.InApp.markAsRead(message.getId()) // Mark an archived iin-app message as read
FollowAnalytics.Push.markAsRead(message.getId()) // Mark an archived push message as read

FollowAnalytics.InApp.markAsUnread(message.getId()) // Mark an archived in-app message as unread
FollowAnalytics.Push.markAsUnread(message.getId()) // Mark an archived push message as unread

FollowAnalytics.InApp.display(message.getId()) // Re-display the InApp related to this message in the exact same way as it was displayed by the SDK at message reception.

Redisplaying an InApp

It's possible to re-display an InApp message in the same way it was displayed by the SDK at the time the message was received. To do so, call the displayInApp() method. It's important to note that no statistics will be sent to the platform when an InApp is re-displayed.

And here is a description of the Message methods:

Methods Return type Description
isRead() boolean Indicates if the message has been read or not. False at message reception.
isNotificationDismissed() boolean If the message comes from a Push campaign, this method returns true if the notification has been dismissed, false otherwise.
isPush() boolean Returns true if message comes from a Push campaign, false if it comes from a In-App campaign.
isInApp() boolean Returns true if message comes from a In-App campaign, false` if it comes from a Push campaign.
isSilent() boolean Returns true if message comes from a Push campaign with the "Silent" option enabled (silent push), false otherwise.
getNotificationId() int If message comes from a Push campaign, this is the Android notification identifier.
getReceivedDate() date If message comes from a Push campaign, this is the date at which the message is received on the device. If message comes from an In-App campaign, this is the start date of the campaign.
getIdentifier() string Unique message Identifier. If the message comes from a Push campaign, this also serves as the Android notification tag.
getTitle() string Text provided in the platform's "Title" field of the Push campaign editor.
getBody() string Text provided in the platform's "Content" field of the Push campaign editor.
getContentUrl() string If the message comes from an InApp Template, this is the local path to the template html index. If the message comes from an InApp Custom Web Page, this the url of the web page. If the message comes from a Push campaign, this is the url of the Rich Media.
getOpeningUrl() string Url provided in the platform's "App Link" field of the Push campaign editor.
getParameters() Map Custom parameters (key/value pairs) provided in the Push campaign editor.
displayInApp() void Re-display the InApp related to this message in the exact same way as it was displayed by the SDK at message reception.

Web Views

If your app contains web views, you can use FollowAnalytics SDK from within your JavaScript code. This is possible using our FAWebView to display your web pages, instead of the regular Android WebView.

FollowAnalytics JS interface not available on Android API level < 17

Using FollowAnalytics SDK from a web view is only possible on Android versions above 4.2 "Jelly Bean" (API level >= 17) in order to prevent a security flaw in Google's Android SDK. More details in Android documentation.

Using FAWebView

  1. Declare the FAWebView element into your <layout>.xml. For instance:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:padding="20dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <com.followapps.android.FAWebView
            android:layout_width="match_parent"
            android:id="@+id/fa_webview"
            android:layout_height="match_parent"/>
    
    </LinearLayout>
    
  2. In the corresponding activity which uses the <layout>.xml layout, retrieve the FAWebView instance:

    FAWebView faWebView = (FAWebView) view.findViewById(R.id.fa_webview);
    
    val faWebView = findViewById<FAWebView>(R.id.fa_webview)
    
  3. Load your HTML page in the FAWebView by using the loadUrl method:

    faWebView.loadUrl("file:///android_asset/webView.html");
    
    faWebView.loadUrl("file:///android_asset/webView.html");
    

FollowAnalytics SDK JS interface

Here are the SDK methods you can call from the JavaScript code:

Make sure to test if FollowAnalytics object exists with if (typeof FollowAnalytics !== 'undefined'). This way, you can reuse the exact same code for your mobile web site without impact.

Watch face

Initialize FollowAnalytics SDK

Do not forget to initialize FollowAnalytics SDK by doing those steps:

Follow Analytics SDK can be used with a watch face.

The watch face architecture has a different approach relatively to Android architecture with their lifecycle that needs to be settle down.

Two different types of background and foreground states exist in CanvasWatchFaceService.Engine.class:

The method onVisibilityChanged() will be executed whenever the watch face becomes visible or hidden in the smartwatch.

However, the method onAmbientModeChanged() will be executed whenever the device enters or exits ambient mode (this mode will switch to a black (hidden seconds) and white display (displayed seconds)).

Now in order to use watch face with FollowAnalytics SDK, two methods are available :

FollowAnalytics.WatchFace.enterForeground();
FollowAnalytics.WatchFace.enterBackground();

The function FollowAnalytics.WatchFace.enterForeground() triggers the information of entering in foreground.

The function FollowAnalytics.WatchFace.enterBackground() triggers the information of entering in background.

Those methods must be added in two different places :

@Override
 public void onAmbientModeChanged(boolean inAmbientMode) {
     super.onAmbientModeChanged(inAmbientMode);

     ...

     if (inAmbientMode) {
         FollowAnalytics.WatchFace.enterBackground();
     } else {
         FollowAnalytics.WatchFace.enterForeground();
     }
     ...
 }

@Override
public void onVisibilityChanged(boolean visible) {
    super.onVisibilityChanged(visible);

    if (visible) {
        ...
        FollowAnalytics.WatchFace.enterForeground();
    } else {
        FollowAnalytics.WatchFace.enterBackground();
        ...
    }
    ...
}
override fun onAmbientModeChanged(inAmbientMode: Boolean) {
    super.onAmbientModeChanged(inAmbientMode)

    ...

    if (inAmbientMode) {
        FollowAnalytics.WatchFace.enterForeground();
    } else {
        FollowAnalytics.WatchFace.enterBackground();
    }
    ...
}

override fun onVisibilityChanged(visible: Boolean) {
    super.onVisibilityChanged(visible)

    if (visible) {
        ...
        FollowAnalytics.WatchFace.enterForeground();
    } else {
        FollowAnalytics.WatchFace.enterBackground();
        ...
    }
    ...
}

Configuration

In order to customize the behavior of FollowAnalytics SDK, it is possible to change the default configuration by creating your own configuration in the project.

It can be achieved by creating an anonymous FollowAnalytics.Configuration, see all parameters here.

public class MyAwesomeApplication extends Application {
  public static boolean isPolicyCalled = false;

  @Override
  public void onCreate(){
    [...]
    FollowAnalytics.init(this.getApplicationContext(), new FollowAnalytics.Configuration() {
            {
              this.apiKey = "API_KEY";
              this.archiveInAppMessages = true;
            }
        });
    [...]
  }
}
class MyAwesomeApplication : Application() {
    var isPolicyCalled = false
    override fun onCreate() {
        [...]
        FollowAnalytics.init(this.applicationContext, object : FollowAnalytics.Configuration() {

            init {
                apiKey = "API_KEY"
                archiveInAppMessages = true
            }
        })
    [...]
    }
}

Or it can be achieved by creating a class that extends FollowAnalytics.Configuration, see all parameters here.

import com.followanalytics.FollowAnalytics;

public class MyConfiguration extends FollowAnalytics.Configuration {
    public static boolean isPolicyCalled = false;

    public MyConfiguration(){
      this.apiKey = "API_KEY";
      this.archiveInAppMessages = true;
    }
}
import com.followanalytics.FollowAnalytics

class MyConfiguration : FollowAnalytics.Configuration() {
    init {
        this.apiKey = "API_KEY"
        this.archiveInAppMessages = true
    }
}

And then, you can initialize FollowAnalytics SDK with instantiating and passing the configuration object. For Instance:

public class MyAwesomeApplication extends Application {
  @Override
  public void onCreate(){
    [...]
    FollowAnalytics.init(this.getApplicationContext(), new MyConfiguration());
    [...]
  }
}
class MyAwesomeApplication : Application() {
    override fun onCreate() {
        [...]
        FollowAnalytics.init(this.applicationContext, MyConfiguration())
        [...]
    }
}

Parameters

A description table with the properties that can be modified with FollowAnalytics.Configuration.

Parameter Type Default Value Description
apiKey String "" Your app api key to use our SDK
isVerbose boolean false To see internal logs made by the SDK through Logcat
apiMode enum ApiMode.PROD To avoid sending irrelevant logs to the production server
environment String "" If you want to use a custom environment
environmentDomain String "follow-apps.com" If you want to use a custom domain
environmentProtocol String "https" By default we use https, you can change it to http
optInAnalyticsDefault boolean true To choose your default opt-in / opt-out analytics behavior
optInNotificationsDefault boolean true To choose your default opt-in / opt-out notifications behavior
archivePushMessages boolean false To choose if you want to archive push messages or not
archiveInAppMessages boolean false To choose if you want to archive inApp messages or not
maxBackgroundTimeWithinSession int 120 To determine the lifetime of a session when in background (between 15 and 3600)
onConsoleLog callback -- Called when new logs are made internally in FollowAnalytics SDK
onNotificationBuilding callback -- Called right before the notification is actually displayed to the user
onPushMessageReceived callback -- Called when a notification is received by the app
onNotificationAction callback -- Called when a notification is tapped. The default behaviour of this callback is to open your app's home page, or a webpage if you provided an url.
onInAppNativeAction callback -- Called when a native InApp (i.e. Native Alert or Evaluation Booster) is closed by tapping one of its buttons.
lastKnownLocationEnabled boolean false true to enable the logging of the last known location when the session starts

Advanced Use Cases

Proguard

If you need to obfuscate and/or shrink your app before Google Play publication, be sure you protect the FollowAnalytics SDK, otherwise logs won't be sent.

Here is the ProGuard configuration:

-keep class com.followanalytics.** { *; }
-keep class com.followapps.android.** { *; }
-keep interface com.followapps.android.** { *; }

Updating from older versions

Updating from 7.2 to 7.3

From 7.3.0, opening a notification (i.e. tapping on its body) will always open your app to its home screen. This means any work in progress, for instance a form which is not saved/submitted yet, will be lost if ever the user opens a notification. This behavior also applies to notification action buttons, unless you explicitly define a background action button thanks to the new FollowAnalytics.NotificationBuilder.addBackgroundAction() method.

As a result of having the app always open on notification opening and foreground action buttons tap, your implementation of the onNotificationAction() callback is now called after the home screen is opened and calling super.onNotificationAction() will no longer open your app.

Make sure to review the notification interaction logic implemented in your app to handle this behavior and perform modifications in your code if needed. Please refer to the following documentation sections for more details:

Updating from 6.8 to 7.0

Updating from 6.4 to 6.5

From 6.5.0, by default, the last known location is no longer logged when a session starts. Set the Configuration parameter lastKnownLocationEnabled to true if you want to enable this feature.

Updating from 6.3 to 6.4

Prior to version 6.4.0, for apps that had the fine location permission, the SDK was requesting periodic location updates internally and was sending locations logs to the FollowAnalytics platform. Since version 6.4.0, it's the responsibility of the developer to send location logs to the platform by using the new logLocation() methods, as explained in the Logging Locations section.