Android (x)

Prerequisites

Things to keep handy before starting your integration with Apxor

Please have the following handy before beginning the integration:

Note

Please add .apxor as a suffix to the bundle ID when adding on the Apxor Dashboard

eg. com.example.myapp.apxor

(Know more about bundle IDs here)

Understanding dependencies to add to your project

Plugin Name
Description
Example

apxor-android-sdk-core

Core Plugin is used to track events for to measure their results and consists of essential information that controls other plugins.

How many of my users have clicked on the cart icon after showing them a nudge Sample Event : 'ViewCart'

apxor-android-sdk-qe

plugin is used to setup behavioural triggers for the campaigns which helps to setup campaign rules.

Show a campaign to users who land on the home screen and add an item to the cart.

apxor-android-sdk-rtm

plugin is used for the show experiences created from the design library using the Apxor dashboard.

Show a tooltip on the cart icon with messaging "Tap here to view items"

com.apxor.android:surveys

plugin is used to show surveys created using the Apxor dashboard.

An NPS survey that would ask the user to rate the app experience on a scale of 1-10.

com.apxor.android:wysiwyg

plugin is used to facilitate creation of campaigns by mirroring your mobile screen to identify the right UI element to show the campaign.

Casting your mobile screen to the dashboard and selecting the hamburger icon

Integration and Initialization

Step 1: Add Apxor Repository

Add Maven URL in project level build.gradle file

Path: <project>/build.gradle:


allprojects {
    repositories {
        // ...
        maven {
           url "https://repo.apxor.com/artifactory/list/libs-release-android/"
        }
        // ...
    }
}

Step 2: Add dependencies

Add plugin dependencies to your application build.gradle file

Path: <project>/<app-module>/build.gradle:

Check the latest release notes here.

2.1 ApxorSDK dependencies (mandatory)

dependencies {
//...

    // Event tracking and a must-have dependency for other plugins
    implementation 'com.apxor.androidx:apxor-android-sdk-core:3.1.7@aar'


    // Add these for Realtime Actions and Surveys
    implementation 'com.apxor.androidx:apxor-android-sdk-qe:1.8.2@aar'
    implementation 'com.apxor.androidx:apxor-android-sdk-rtm:2.6.1@aar'
    implementation 'com.apxor.androidx:surveys:2.2.2@aar'


    // Helper plugin to create walkthroughs
    implementation 'com.apxor.androidx:wysiwyg:1.6.3@aar'
    
    // Add the below two dependencies to establish an SSE connection for WYSIWYG
    implementation 'com.squareup.okhttp3:okhttp:4.9.0'
    implementation 'com.launchdarkly:okhttp-eventsource:2.5.0'
    
//...
}

2.2 Add exoplayer in your app (optional)

Add exoplayer in your app

Exoplayer enables you to configure Picture In Picture videos from the Apxor dashboard; if you are already using the exoplayer in your app, this step is not needed; otherwise, add the following dependency in the application build.gradle file. To use video pip templates, this is necessary.

For com.apxor.androidx:apxor-android-sdk-rtm:2.3.6@aar version onwards

dependencies {
  //... 

  implementation 'androidx.media3:media3-exoplayer:1.1.1'
  implementation 'androidx.media3:media3-ui:1.1.1'


  //...
  }

For com.apxor.androidx:apxor-android-sdk-rtm:2.3.5@aar and below

dependencies {
  //... 

  implementation 'com.google.android.exoplayer:exoplayer:2.14.0'

  //...
  }

2.3 Enable uninstall tracking for your users (optional)

Enable uninstall tracking for your users

Apxor uses your Firebase server key to send silent push notifications to track uninstalls and measure the outcomes of your campaign. To enable this, please do the following:

For Firebase Version < 22.0.0

dependencies {
  // Add this to track uninstalls from the Apxor dashboard
  implementation('com.apxor.androidx:apxor-android-sdk-push:1.2.8@aar') {
    exclude group: 'com.google.firebase'
  }
}

Please handle the notifications like the following:

public class MyFirebaseMessagingService extends FirebaseMessagingService {
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        // Creating Notification Channel
        ApxorPushAPI.createNotificationChannel(this.getApplicationContext(), "Apxor", "Apxor", "Apxor");
        if (remoteMessage.getFrom().equals(YOUR_FCM_SENDER_ID)) {
            // Push Notification receiver with your Sender ID
        } else {
            // Check if Push Notification received from Apxor
            if (ApxorPushAPI.isApxorNotification(remoteMessage)) {
                ApxorPushAPI.handleNotification(remoteMessage, getApplicationContext());
            } else {
                // Silent or Data push notification, which you can send through Apxor dashboard
            }
        }
    }
}

For Firebase Version >= 22.0.0

dependencies {
  // Add this to track uninstalls from the Apxor dashboard
  implementation('com.apxor.androidx:apxor-android-sdk-push-v2:1.3.1@aar') {
    exclude group: 'com.google.firebase'
  }
}

Read here on how to get your Firebase sender ID and also FCM server key to share it with apxor to configure uninstall tracking.

2.4 Install Referrer Dependency (optional)

Install Referrer Dependency (optional)

If added, we can provide install attribution.

dependencies {

  // Add this to get the install attribution
  implementation "com.android.installreferrer:installreferrer:2.2"
  
}

Step 3: Add the following in proguard-rules.pro

Note

If you use proguard to obfuscate the classes, you have to add the following to ignore obfuscation for Apxor SDK classes

Configure the below rules in your proguard-rules.pro file

Path: <project>/<app-module>/proguard-rules.pro:

-keep class com.apxor.** { *; }
-dontwarn com.apxor.**

Step 4: Disable Dexing Artifact Transformation

Note

In Android Gradle Plugin 3.5.0, we use Gradle artefact transforms for desugaring and dexing, enabling greater parallelism and caching. This process depends on libraries having accurate Maven information since dependencies specified in POM files are used to set up the desugaring classpath. If we encounter issues with missing dependencies during desugaring, it's necessary to disable parallel transformation to facilitate the process by adding the following property:

Add the following to gradle.properties file

Path: <project>/<app-module>/gradle.properties:

android.enableDexingArtifactTransform = false

Step 5: Initialize ApxorSDK

To start tracking with the Apxor Android SDK, you must first initialize it.

Make Sure to enter the AppID for $APXOR_ID

You will need your Application Identifier for this. Here is how you can get the Apxor Application Identifier, as mentioned in the Integration Essentials section

Manual Initialization

Alternatively, you can initialize it manually with your Apxor App ID. To initialize the SDK,

ApxorSDK.initialize("$APXOR_ID" , this.getApplicationContext());

You are all set: Verify your SDK integration

We have to verify two things as follows :

SDK Initialization

On running your Android project, search for the following log in logcat :

ApxorSDK(v2**) successfully initialized for: APP_ID

Plugin Initialization

By default, only error logs are enabled. To see debug logs for plugin initialization and to confirm tracking event triggers and user properties, Please run the below command in the terminal

adb shell setprop log.tag.Apxor VERBOSE

Add-Ons

Now that you have completed the basic integration, you can proceed to set up event triggers, capture data for targeting, add slots for embed cards and stories, and personalize messaging. Include the following sections as needed.

Identifying Users

This section is mandatory if you intend to use Segments and Cohorts.

The Apxor SDK automatically captures device IDs, and this is used to identify users uniquely by Apxor. Apart from this, you can log a custom user identifier that you use to uniquely identify users in your app.

This identifier would be very instrumental, especially when exporting data of a certain campaign or survey to your analytics system to create a cohort and measure the results of the campaign.

Similarly, when you are importing data to Apxor from your system that your marketing/product/data science team has identified and wants to run campaigns specifically for them, the custom user identifier will serve as the bridge to communicate between your systems and Apxor effectively.

Here is how you can set your user identifier for Apxor to recognize your users :

ApxorSDK.setUserIdentifier(<SOME_USER_ID>);

Here is how you can fetch the Apxor device identifier, which is automatically captured and maintained by the SDK if you want to record and map it to your data.

String deviceId = ApxorSDK.getDeviceId(applicationContext);

Log events and user data for Targeting, Triggering and goal Tracking

The product/marketing or the growth team lists the use cases with an idea of when and to whom to launch. To do this, we need to capture data in the form of events. Let us consider the following use case as an example:

App Events

In the above scenario, we want to trigger the campaign for users who have spent 'x' seconds and haven't tapped on a product. To understand that if the user has tapped the product, we should log an event along with its attributes as follows to capture data:

Similarly, if you want to send promotions to users who have viewed at least five products of the category shoes in the last three days or you want to measure how many people added an item to a cart from the campaign as a goal, all this information is captured in the form of events.

These types of events are classified as app events - the data that is transferred to the servers at Apxor, where you can segment users based on historical behaviour or measure your goals as specified above.

Here is how we track app events :

Attributes additionalInfo = new Attributes();
additionalInfo.putAttribute("ProductName", "MixerGrinder");
additionalInfo.putAttribute("ProductPrice", 2999);
ApxorSDK.logAppEvent("ProductClicked", additionalInfo);

Client Events

In the below scenario, let's assume you want to launch a survey when the soft back button is pressed asking the user for product feedback. In this, we don't need to capture the data of how many people pressed the back button, which is useless and bloats your event storage as it is a high-frequency event, which increases your cost unnecessarily. This data point doesn't potentially answer any of your product questions, and hence, there is no ROI in storing data from this event.

So for such scenarios where we need the behavioural data to launch a campaign or to collect feedback, which doesn't provide ROI on storing for measuring goals, answering your product questions or segmenting your target audience, we log these events as Client Events, which involves zero transfer of data and is used only to set up your triggers on behavioural information from the user.

Attributes additionalInfo = new Attributes();
additionalInfo.putAttribute("Screen", "com.example.app.SettingsActivity");
ApxorSDK.logClientEvent("SoftBackPressed", additionalInfo);

User Attributes

We can personalize the messaging copy in the experiences we build for the user or target based on his persona using information that is centric to individual users.

Let us consider the following example where we know the user prefers English as a language and has a Gold membership. This information helps to tailor content in English to that specific user and gives us the flexibility to use different messaging for different membership tiers. This is how the information captured here is used for segmenting.

Similarly, capturing attributes like Name, can you help to personalize your message copy where it reads "Hi {username} can't find your product?" where the username is replaced by the attribute value of the property from the nudges dashboard along with providing meaningful defaults in their absence.

This is how you log user information to Apxor :

Attributes userInfo = new Attributes();
userInfo.putAttribute("Name", "Prabhu");
userInfo.putAttribute("AcquistionSource", "Facebook");
userInfo.putAttribute("CampaignAttribution", "shoes-adset");
userInfo.putAttribute("UserGender", "Male");
userInfo.putAttribute("MembershipType", "Gold");
userInfo.putAttribute("AppLanguage", "English");
ApxorSDK.setUserCustomInfo(userInfo);

Session Attributes

A Session can be simply defined as a user's journey from when he opens the app until he closes the app. There can be various pieces of information that be very impactful when accumulated in a session. For example, location in a session can be useful to know exactly where the user is utilizing the app most.

To add session attributes that are specific to a session,

ApxorSDK.setSessionCustomInfo("network", "4G");

Or if you have multiple key-value pairs that need to be logged, you can simply put them in a hashmap like,

Attributes sessionInfo = new Attributes();
sessionInfo.putAttribute("network", "4G");
sessionInfo.putAttribute("city", "GAJ");
ApxorSDK.setSessionCustomInfo(sessionInfo);

Track Screens

In the scenario discussed in this guide, how will we know if the user has spent thirty seconds on the home screen and did not click on the product? For this reason, it is important to track the screens to set them up as triggers and also to capture the time spent on the screens.

Please ensure the following if you have fragments:

  • If your screen has different fragments, then it is important to track all the fragments using the API once the screen is visible to your user

  • If you use Android's Navigation architecture component, you can use ApxorSDK.trackScreen API in NavController.OnDestinationChangedListener callback

  • If your activity has different tabs either at the top or bottom, you can call ApxorSDK.trackScreen API every time a tab gets selected, as shown below:

By using the following API to track the screens in the app, you can set campaigns on inactivity or time spent on those screens:

ApxorSDK.trackScreen("HomeScreen");

Note

Make sure you use the ApxorSDK.trackScreen API in your activity's onResume method

bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_share:
                ApxorSDK.trackScreen("Share");
                // Your logic
                break;
            case R.id.action_settings:
                ApxorSDK.trackScreen("Settings");
                // Your logic
                break;
            //.. other cases
        }
    }
});
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
    @Override
    public void onTabSelected(TabLayout.Tab tab) {
        ApxorSDK.trackScreen(tab.getText().toString());
        // Your logic
    }

    @Override
    public void onTabUnselected(TabLayout.Tab tab) {
        // Your logic
    }

    @Override
    public void onTabReselected(TabLayout.Tab tab) {
        // Your logic
    }
});

Nudges in WebView

Tooltips in WebView

Note

Make sure you have apxor-sdk-rtm plugin version >= 1.5.3

Many native applications feature WebViews to display descriptive content and much more. Apxor Android SDK provides a way to show tooltips inside that WebView to make the most of it.

Following are the steps in order to show tooltips in your WebView.

  • Add a tag to the WebView (which is to be later provided in the dashboard) as shown.

    webView.setTag("MyWebView");
  • Attach Apxor SDK JavaScript Interface as mentioned below.

    // You must enable Javascript for the webview
    WebSettings settings = webView.getSettings();
    settings.setJavaScriptEnabled(true);
    
    // Attach Apxor SDK Javascript Interface
    webview.addJavascriptInterface(new ApxorJSInterface(), "Apxor");

That's it. All you have to do is set some IDs for your HTML elements inside your web page and configure them in the dashboard along with WebView's tag.

Log Events inside WebView

It is suggested that you log events inside your WebView once the page is completely rendered using Apxor JavaScript Interface methods. Based on these events, you can configure Tooltips.

  • Methods exposed from Apxor JavaScript Interface

    window.Apxor.logAppEvent(event_name[, event_props]);
    window.Apxor.logClientEvent(event_name[, event_props]);
  • Examples for logging App Event

    Example:

    Log an event on page load event.

    ...
    <head>
      ...
      <script>
        function logApxorEvent(eventName, attributes) {
          if (window.Apxor && window.Apxor.logAppEvent) {
            window.Apxor.logAppEvent(eventName, attributes);
          }
        }
      </script>
    </head>
    <body onload="logApxorEvent('PageLoaded')">
      ...
    </body>

    Example (React based web pages):

    Log an event on componentDidMount.

    componentDidMount() {
        if (window.Apxor && window.Apxor.logAppEvent) {
            window.Apxor.logAppEvent('LoginPageLoaded', null);
        }
    }

Embed and Story Slots

Add Embed Card Slot

Note

For using Embed Cards, the versions of the following plugins should be greater than or equal to the ones mentioned below

Core: 3.1.0

qe: 1.7.5

rtm: 2.4.8

WYSIWYG: 1.5.6

For using Apxor's Embed cards template, this step is necessary.

In all areas of the app where you may want to show an in-line widget, insert the following code in the XML

<com.apxor.androidsdk.plugins.realtimeui.ApxorWidget
        android:tag="<Tag>"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

Rename <Tag> to any unique ID. Ensure that you keep the ID unique across different IDs and across different ApxorWidget instances.

Alternatively, you can also add the view dynamically during runtime. Make sure the widget is in a place where it can occupy the entire width and height as per its requirement.

ApxorWidget widget = new ApxorWidget(context,"<Tag>")
layout.addView(widget);

Add Story Slot

Note

For using Stories, the versions of the following plugins should be greater than or equal to the ones mentioned below

Core: 3.1.0

qe: 1.7.5

rtm: 2.4.8

WYSIWYG: 1.5.6

For using Apxor's Stories template, this step is necessary.

In all areas of the app where you may want to show an in-line widget, insert the following code in the XML

<com.apxor.androidsdk.plugins.realtimeui.stories.ApxorStoryWidget
        android:tag="<Tag>"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

Rename <Tag> to any unique ID. Ensure that you keep the ID unique across different IDs and across different ApxorWidget instances.

Alternatively, you can also add the view dynamically during runtime. Make sure the widget is in a place where it can occupy the entire width and height as per its requirement.

ApxorStoryWidget widget = new ApxorStoryWidget(context,"<Tag>")
layout.addView(widget);

Handle custom redirection using Key-Value pairs

If your app wants to redirect users based on simple key-value pairs instead of using Deeplink URLs or Activity, you can follow the below approach

import android.app.Application;
import com.apxor.androidsdk.core.ApxorSDK;
import com.apxor.androidsdk.core.RedirectionListener;

import org.json.JSONArray;

public class MyApplication extends Application {
  @Override
  public void onCreate() {

    // Register a redirection listener ONLY ONCE in your app
    // If you register in multiple places, ONLY the last value will be available.
    // Whenever you register a new one, it will override the existing listener
    Apxor.setRedirectionListener(new RedirectionListener() {
      @Override
      public void onActionComplete(JSONArray keyValuePairs) {
        int length = keyValuePairs.length();

        /**
         * [
         *      {
         *          "name": "YourKey",
         *          "value": "YourValue"
         *      },
         *      ....
         * ]
         */
        try {
          for (int i = 0; i < length; i++) {
            JSONObject pair = keyValuePairs.getJSONObject(i);
            String key = pair.getString("name");
            // Values are always String type. You need to convert based on your need
            String value = pair.getString("value");

            // Your logic continues from here
          }
        } catch (JSONException e) {

        }
      }
    });
  }
}

Miscellanious

Reporting Custom Errors

Custom errors describe situations like LOGIN_FAILED and NETWORK_CALL_FAILED and are to be treated differently compared to app events. So, these are treated as errors and are shown on the issues page to let you know their impact.

A custom error takes the exception itself and some context (what? OR which?) to make it easy for you to identify. To report a custom error,

Exception e = new Exception("LOGIN FAILED EXCEPTION");
HashMap<String, String> additionalInfo = new HashMap<>();
additionalInfo.put("email", "spock@vulcan.com");
additionalInfo.put("cause", "network failure");
ApxorSDK.reportCustomError("Null Value", additionalInfo, e);

To get Apxor Device Identifier

Apxor SDK maintains a unique ID for every user. To get the Apxor Device ID, use the below

String deviceId = ApxorSDK.getDeviceId(applicationContext);

To get Apxor Attributes

Now you can use getAttributes API to get the user and session attributes from ApxorSDK in a single API call.

Note:

This is an asynchronus call. So, you have to pass a callback. For that you can use ApxorDataCallback. There are two methods in this interface. One is onSuccess and onFailure. Both these methods will be called in a separate background thread.

ApxorSDK.getAttributes(
  new String[]{ "custom_user_id", "email" },
  new ApxorDataCallback() {
    @Override
    public void onSuccess(JSONObject props) {
      if (props == null) {
        return;
      }
      String userId = props.optString("custom_user_id");
      String email = props.optString("email");
    }

    @Override
    public void onFailure() {
      Log.e(TAG, "Failed to get attributes");
    }
  }
);

Yeah! You are ready to create your first Campaign or Survey.

Last updated