# Android (x)

## Prerequisites

Things to keep handy before starting your integration with Apxor

Please have the following handy before beginning the integration:

* **Application identifier** generated on the Apxor dashboard for your app\
  \
  ([Read more on how to fetch the application identifier from the Apxor dashboard](https://guides.apxor.com/getting-started-with-apxor/adding-a-new-app))
* **App Bundle Id:** Every Android app has a unique application ID that looks like `com.example.myapp`. This ID uniquely identifies the app on the device and also on the Google Play Store.

{% hint style="info" %}
**Note**

Please add <mark style="background-color:blue;">**.apxor**</mark> as a suffix to the bundle ID when adding on the Apxor Dashboard

eg. <mark style="background-color:blue;">**`com.example.myapp.apxor`**</mark>
{% endhint %}

[(Know more about bundle IDs here)](https://developer.android.com/studio/build/configure-app-module)

* **The list of events** to set up triggers and track goals, user properties that allow to personalize messages and to target better.\
  \
  ([Read more on how you can setup here](#setting-up-campaign-triggerscapturing-data-for-targetting-and-goal-tracking))
* **Firebase Cloud Messaging (FCM) Server Key:** To track uninstalls, Apxor sends silent push notifications to measure your campaign outcomes.\
  \
  ([Read more on how to fetch Firebase credentials that are used to set uninstall tracking during integration here](https://guides.apxor.com/getting-started-with-apxor/firebase-credentials-optional))

**Understanding dependencies to add to your project**

<table><thead><tr><th width="258.3333333333333">Plugin Name</th><th width="252">Description</th><th>Example</th></tr></thead><tbody><tr><td>apxor-android-sdk-core</td><td>Core Plugin is used to track events for to measure their results and consists of essential information that controls other plugins.</td><td>How many of my users have clicked on the cart icon after showing them a nudge Sample Event : 'ViewCart'</td></tr><tr><td>apxor-android-sdk-qe</td><td>plugin is used to setup behavioural triggers for the campaigns which helps to setup campaign rules.</td><td>Show a campaign to users who land on the home screen and add an item to the cart.</td></tr><tr><td>apxor-android-sdk-rtm</td><td>plugin is used for the show experiences created from the design library using the Apxor dashboard.</td><td>Show a tooltip on the cart icon with messaging "Tap here to view items"</td></tr><tr><td>com.apxor.android:surveys</td><td>plugin is used to show surveys created using the Apxor dashboard.</td><td>An NPS survey that would ask the user to rate the app experience on a scale of 1-10.</td></tr><tr><td>com.apxor.android:wysiwyg</td><td>plugin is used to facilitate creation of campaigns by mirroring your mobile screen to identify the right UI element to show the campaign.</td><td>Casting your mobile screen to the dashboard and selecting the hamburger icon</td></tr></tbody></table>

## Integration and Initialization

### Step 1: Add Apxor Repository

Add Maven URL in project level `build.gradle` file

**`Path: <project>/build.gradle`**:

<pre class="language-java"><code class="lang-java">
allprojects {
<strong>    repositories {
</strong>        // ...
        maven {
           url "https://repo.apxor.com/artifactory/list/libs-release-android/"
        }
        // ...
    }
}
</code></pre>

<figure><img src="https://300211688-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQuYbJ9bg7CFtrBaVp9pB%2Fuploads%2FxvmIQxNCJEdQIj5URdXH%2FAddingApxorRepo.gif?alt=media&#x26;token=887e9211-b0ad-4372-9b50-fc0d24947172" alt=""><figcaption></figcaption></figure>

### 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.](https://guides.apxor.com/release-notes#android)

#### 2.1 ApxorSDK dependencies (mandatory)

```gradle
dependencies {
//...

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


    // Add these for Realtime Actions and Surveys
    implementation 'com.apxor.androidx:apxor-android-sdk-qe:1.8.8@aar'
    implementation 'com.apxor.androidx:apxor-android-sdk-rtm:2.7.1@aar'
    implementation 'com.apxor.androidx:surveys:2.3.0@aar'


    // Helper plugin to create walkthroughs
    implementation 'com.apxor.androidx:wysiwyg:1.6.5@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)

<details>

<summary>Add exoplayer in your app</summary>

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.

<img src="https://300211688-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQuYbJ9bg7CFtrBaVp9pB%2Fuploads%2FFVZvw6QsR1eWvpNnhqyP%2FInappVideo.png?alt=media&#x26;token=43091ace-c788-43de-a3d1-a49f2b23523a" alt="" data-size="original">

<mark style="background-color:blue;">**For com.apxor.androidx:apxor-android-sdk-rtm:2.3.6\@aar version onwards**</mark>

```gradle
dependencies {
  //... 

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


  //...
  }
```

<mark style="background-color:blue;">**For com.apxor.androidx:apxor-android-sdk-rtm:2.3.5\@aar and below**</mark>

```gradle
dependencies {
  //... 

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

  //...
  }
```

</details>

#### 2.3 Enable uninstall tracking for your users (optional)

<details>

<summary>Enable uninstall tracking for your users</summary>

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:

<mark style="background-color:blue;">**For Firebase Version < 22.0.0**</mark>

```gradle
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:

```gradle
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
            }
        }
    }
}
```

<mark style="background-color:blue;">**For Firebase Version >= 22.0.0**</mark>

```gradle
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](https://guides.apxor.com/getting-started-with-apxor/firebase-credentials-optional) on how to get your Firebase sender ID and also FCM server key to share it with apxor to configure uninstall tracking.

</details>

#### 2.4 Install Referrer Dependency (optional)

<details>

<summary>Install Referrer Dependency (optional)</summary>

If added, we can provide install attribution.

```gradle
dependencies {

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

</details>

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

{% hint style="info" %}
**Note**

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

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

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

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

### Step 4: Disable Dexing Artifact Transformation

{% hint style="info" %}
**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:
{% endhint %}

Add the following to `gradle.properties` file

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

```java
android.enableDexingArtifactTransform = false
```

### Step 5: Initialize ApxorSDK

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

{% hint style="warning" %}
**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](https://guides.apxor.com/getting-started-with-apxor/adding-a-new-app)
{% endhint %}

<details>

<summary>Auto Initialization (Recommended)</summary>

To Auto initialize SDK, add the following `meta-data` tag inside your `application` tag in your `AndroidManifest.xml` file. This will check for Apxor SDK, and if present, will initialize automatically.

**`Path: <project>/<app-module>/src/AndroidManifest.xml`**:

```java
<application>
//...
    <meta-data android:name="APXOR_APP_ID" android:value="$APXOR_ID"/>
//...
</application>
```

</details>

<details>

<summary>Manual Initialization</summary>

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

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

</details>

### 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 :

```java
ApxorSDK(v2**) successfully initialized for: APP_ID
```

<figure><img src="https://300211688-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQuYbJ9bg7CFtrBaVp9pB%2Fuploads%2Fu5WpUuW4YMYhX56O9eN8%2FSDKInitialisation.gif?alt=media&#x26;token=8e66f89d-fb3c-44f4-90f2-773fee052921" alt=""><figcaption></figcaption></figure>

#### **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

```java
adb shell setprop log.tag.Apxor VERBOSE
```

<figure><img src="https://300211688-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQuYbJ9bg7CFtrBaVp9pB%2Fuploads%2F3KZ8EKIIV6NpmkWEwWVb%2FPluginInitialisation.gif?alt=media&#x26;token=5d98613c-6569-4e35-811f-35d6b557fb1e" alt=""><figcaption></figcaption></figure>

## 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 <a href="#identifying-users" id="identifying-users"></a>

{% hint style="info" %}
This section is **mandatory** if you intend to use **Segments and Cohorts.**
{% endhint %}

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 :

```java
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.

```java
String deviceId = ApxorSDK.getDeviceId(applicationContext);
```

### Log events and user data for Targeting, Triggering and goal Tracking[​](http://localhost:3000/docs/android-sdk/Tracking#setting-up-campaign-triggerscapturing-data-for-targetting-and-goal-tracking) <a href="#setting-up-campaign-triggerscapturing-data-for-targetting-and-goal-tracking" id="setting-up-campaign-triggerscapturing-data-for-targetting-and-goal-tracking"></a>

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:

<figure><img src="https://300211688-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQuYbJ9bg7CFtrBaVp9pB%2Fuploads%2FO0IyF5MCgJqDjDZFaMhk%2FScenario.png?alt=media&#x26;token=d58b91d7-a239-4f33-90bf-fa2fc3511724" alt=""><figcaption></figcaption></figure>

#### **App Events**[**​**](http://localhost:3000/docs/android-sdk/Tracking#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:

<figure><img src="https://300211688-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQuYbJ9bg7CFtrBaVp9pB%2Fuploads%2FsPezIDe2pRzYYolpCwpA%2FEvents.png?alt=media&#x26;token=0fb7a53a-6777-45e7-a675-ca07c15ac434" alt=""><figcaption></figcaption></figure>

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 <mark style="background-color:yellow;">app events</mark> - 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 :

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

#### Client Events[**​**](http://localhost:3000/docs/android-sdk/Tracking#client-events) <a href="#client-events" id="client-events"></a>

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.<br>

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 <mark style="background-color:yellow;">Client Events</mark>, which involves zero transfer of data and is used only to set up your triggers on behavioural information from the user.

<figure><img src="https://300211688-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQuYbJ9bg7CFtrBaVp9pB%2Fuploads%2FsPezIDe2pRzYYolpCwpA%2FEvents.png?alt=media&#x26;token=0fb7a53a-6777-45e7-a675-ca07c15ac434" alt=""><figcaption></figcaption></figure>

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

#### User Attributes <a href="#user-attributes" id="user-attributes"></a>

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.&#x20;

Let us consider the following example where we know the user prefers <mark style="background-color:yellow;">English</mark> as a language and has a <mark style="background-color:yellow;">Gold</mark> 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.<br>

Similarly, capturing attributes like <mark style="background-color:yellow;">Name</mark>, 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.

<figure><img src="https://300211688-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQuYbJ9bg7CFtrBaVp9pB%2Fuploads%2F087vKxZUNxLzTIjzrLd6%2FUserProperties.png?alt=media&#x26;token=abec42df-d497-4529-a9a5-c8961151f279" alt=""><figcaption></figcaption></figure>

This is how you log user information to Apxor :

```java
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 href="#session-attributes" id="session-attributes"></a>

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,

```java
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,

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

### Track Screens <a href="#track-screens" id="track-screens"></a>

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.

<figure><img src="https://300211688-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FQuYbJ9bg7CFtrBaVp9pB%2Fuploads%2FWkfPWS9mVQCxNKIcQu0y%2FTrackScreen.png?alt=media&#x26;token=90629569-a9ca-4c88-b88c-756d674b60a9" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
**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](https://developer.android.com/guide/navigation), 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:
  {% endhint %}

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

```java
ApxorSDK.trackScreen("HomeScreen");
```

{% hint style="info" %}
**Note**

Make sure you use the `ApxorSDK.trackScreen` API in your activity's `onResume` method
{% endhint %}

```java
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
        }
    }
});
```

```java
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 <a href="#tooltips-in-webview" id="tooltips-in-webview"></a>

#### Tooltips in WebView <a href="#tooltips-in-webview" id="tooltips-in-webview"></a>

{% hint style="info" %}
**Note**

Make sure you have `apxor-sdk-rtm` plugin version >= 1.5.3
{% endhint %}

Many native applications feature `WebView`s 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.

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

  ```java
  // You must enable Javascript for the webview
  WebSettings settings = webView.getSettings();
  settings.setJavaScriptEnabled(true);

  // Attach Apxor SDK Javascript Interface
  webview.addJavascriptInterface(new ApxorJSInterface(), "Apxor");
  ```

{% hint style="success" %}
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.
{% endhint %}

#### Log Events inside WebView <a href="#log-events-inside-webview" id="log-events-inside-webview"></a>

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

  ```js
  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.

  ```html
  ...
  <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.

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

### Embed and Story Slots

#### Add Embed Card Slot

{% hint style="warning" %}
**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**
{% endhint %}

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

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

Rename <mark style="color:blue;">\<Tag></mark> 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.

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

#### Add Story Slot

{% hint style="warning" %}
**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**
{% endhint %}

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

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

Rename <mark style="color:blue;">\<Tag></mark> 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.

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

### Handle custom redirection using Key-Value pairs <a href="#handle-custom-redirection-using-key-value-pairs" id="handle-custom-redirection-using-key-value-pairs"></a>

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

```java
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 <a href="#reporting-custom-errors" id="reporting-custom-errors"></a>

#### Reporting Custom Errors <a href="#reporting-custom-errors" id="reporting-custom-errors"></a>

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,

```java
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 <a href="#to-get-apxor-device-identifier" id="to-get-apxor-device-identifier"></a>

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

```java
String deviceId = ApxorSDK.getDeviceId(applicationContext);
```

#### To get Apxor Attributes <a href="#to-get-apxor-attributes" id="to-get-apxor-attributes"></a>

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**.

```java
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](https://guides.apxor.com/product-guides/create-a-campaign/mobile) or [Survey](https://guides.apxor.com/product-guides/create-a-survey/legacy).


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://guides.apxor.com/getting-started-with-apxor/sdk/android-x.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
