In-App Messaging

In-App Messaging (IAM) module allows app developers to easily configure and display messages or campaigns within their app.

In-App Messaging Sample

This page covers:

Requirements


Configuration


1. Include Maven Central repo in root build.gradle

allprojects {
    repositories {
        mavenCentral()
    }
}

2. Add the SDK in dependencies

dependencies {
    implementation 'io.github.rakutentech.inappmessaging:inappmessaging:${latest_version}'
}

Note:

3. Add subscription ID and config URL in AndroidManifest.xml

<meta-data
    android:name="com.rakuten.tech.mobile.inappmessaging.subscriptionkey"
    android:value="change-to-your-subsrcription-key"/>

<meta-data
    android:name="com.rakuten.tech.mobile.inappmessaging.configurl"
    android:value="change-to-config-url"/>

Note: These can also be set at runtime, as described here.

4. Enable debug logs (Optional)

If you want to enable SDK debug logging (tags begins with “IAM_”), add this metadata in AndroidManifest.xml:

<meta-data
    android:name="com.rakuten.tech.mobile.inappmessaging.debugging"
    android:value="true"/>

5. Call configure()

This method initializes the SDK and should be called in your Application’s onCreate.

An optional lambda function callback can be set to receive exceptions that caused failed configuration or non-fatal failures in the SDK.

class MainApplication : Application() {

    override fun onCreate() {
        // This can be used for analytics and logging of encountered configuration issues.
        InAppMessaging.errorCallback = { Log.e(TAG, it.localizedMessage, it.cause) }

        // If using Java, call with `InAppMessaging.Companion.configure(context: Context)`.
        InAppMessaging.configure(
            context = this,
            subscriptionKey = "your_subscription_key", // Optional
            configUrl = "endpoint for fetching configuration", // Optional
            enableTooltipFeature = true // Optional (disabled by default)
        )
    }
}                                  

Notes:

6. Enable and disable the SDK remotely (Optional)

We recommend, as good engineering practice, that you integrate with a remote config service so that you can fetch a feature flag, e.g. Enable_IAM_SDK, and use its value to dynamically enable/disable the SDK without making an app release. There are many remote config services on the market, both free and paid.

Final Code Preview (Sample)


By the end of this guide, your SDK integration code will look something like this:

(click to expand)

MainApplication.kt

class MainApplication: Application() {

    val yourUserProvider = YourUserInfoProvider()

    override fun onCreate() {
        InAppMessaging.configure(this)
        InAppMessaging.instance().registerPreference(yourUserProvider)
    }
}

YourUserInfoProvider.kt

class YourUserInfoProvider: UserInfoProvider() {

    // Update during login or logout
    var userId = ""
    var accessToken = ""
    var idTracking = ""

    override fun provideUserId() = userId

    override fun provideAccessToken() = accessToken

    override fun provideIdTrackingIdentifier() = idTracking
}

MainActivity.kt

class MainActivity: AppCompatActivity(), View.OnClickListener {

    override fun onStart() {
        InAppMessaging.instance().logEvent(AppStartEvent())
    }

    override fun onResume() {
        InAppMessaging.instance().registerMessageDisplayActivity(this)
    }

    override fun onPause() {
        InAppMessaging.instance().unregisterMessageDisplayActivity()
    }

    override fun onClick(v: View) {
      // Log the events based on your use-cases
      when (v.id) {
        R.id.purchase_button_tapped -> InAppMessaging.instance().logEvent(PurchaseSuccessfulEvent())

        R.id.home_tab_tapped -> InAppMessaging.instance().logEvent(CustomEvent("tab_visit").addAttribute("tab_name", "home"))

        R.id.cart_tab_tapped -> InAppMessaging.instance().logEvent(CustomEvent("tab_visit").addAttribute("tab_name", "cart"))
      }
    }

    fun onUserLogin() {
        yourUserProvider.userId = "<userId>"
        yourUserProvider.accessToken = "<accessToken>" // or idTracking
        InAppMessaging.instance().logEvent(LoginSuccessfulEvent())
    }
    
    fun onUserLogout() {
        yourUserProvider.userId = ""
        yourUserProvider.accessToken = "" // or idTracking
    }
}

Using the SDK


1. registerMessageDisplayActivity() and unregisterMessageDisplayActivity()

Decide which activities in your app can display messages, then, call registerMessageDisplayActivity() method in those activities’ onResume. It should be paired with unregisterMessageDisplayActivity() in onPause.

The activities will be kept in a WeakReference object, so it will not cause any memory leaks.

override fun onResume() {
    super.onResume()
    InAppMessaging.instance().registerMessageDisplayActivity(this)
}

override fun onPause() {
    super.onPause()
    InAppMessaging.instance().unregisterMessageDisplayActivity()
}

2. logEvent()

This method initiates the display of a message whenever a specific event or a set of events occur. Call this method at appropriate locations in your app, and based on your use-case.

For each logged event, the SDK will match it with the ongoing message’s triggers that are configured in the Dashboard. Once all of the required events are logged by the app, the message will be displayed in the current registered activity. If no activity is registered, it will be displayed in the next registered activity.

Pre-defined event classes:

AppStartEvent

Log this event on app launch from terminated state. Recommended to log this event in app’s main activity’s Activity#onStart().

App Start Event is persistent, meaning, once it’s logged it will always satisfy corresponding trigger in a message. All subsequent logs of this event are ignored. Messages that require only AppStartEvent are shown once per app session.

class MainActivity: AppCompatActivity() {

    override fun onStart() {
        super.onStart()
        InAppMessaging.instance().logEvent(AppStartEvent())
    }
}

Note: Because this event is logged almost instantly after app launch, there may be situation wherein user information is not yet available due to some delay, and may cause unexpected behavior. Therefore we recommend to ensure user information is up-to-date (see User Targeting section for details) when using AppStart-only as trigger, or combine it with other event wherein user information is guaranteed to be available.

LoginSuccessfulEvent

Log this every time the user logs in successfully.

InAppMessaging.instance().logEvent(LoginSuccessfulEvent())

PurchaseSuccessfulEvent

Log this event after every successful purchase.

InAppMessaging.instance().logEvent(PurchaseSuccessfulEvent())

Custom event class:

CustomEvent

Log this after app-defined states are reached or conditions are met. Example use-case is an event based on tabs or certain screens in your app.

Custom events can have attributes with names and values. Attributes can be integer, double, String, boolean, or java.util.Date type.

InAppMessaging.instance().logEvent(CustomEvent("search").addAttribute("keyword", "book").addAttribute("number_of_keyword", 1))

3. registerPreference()

Required if you want to target messages to specific users.

To identify users, you must provide the following user information (the required data varies based on the login SDK used):

User Info Description For Mobile Login SDK users For ID SDK users
User ID ID when registering a Rakuten account (e.g. email address or username) Required Optional
Access Token Access token value provided by the internal Mobile Login SDK Required Do not override or leave empty
ID Tracking Identifier Tracking identifier value provided by the internal ID SDK Do not override or leave empty Required

1. Create a new class that implements UserInfoProvider:

import com.rakuten.tech.mobile.inappmessaging.runtime.UserInfoProvider

class YourUserInfoProvider: UserInfoProvider() {

    // Update during login or logout
    var userId = ""
    var accessToken = ""
    var idTracking = ""

    override fun provideUserId() = userId

    override fun provideAccessToken() = accessToken

    override fun provideIdTrackingIdentifier() = idTracking
}

You must provide the relevant information through this class. It will be retrieved by SDK on demand, so make sure values are up-to-date.

After logout is complete, please ensure that all UserInfoProvider methods in the preference object return null or empty string.

Notes:

2. Register your UserInfoProvider

Call registerPreference() method right after configure().

class MainApplication : Application() {

    override fun onCreate() {

        // configure called here...

        InAppMessaging.instance().registerPreference(YourUserInfoProvider())
    }
}                                  

Advanced Features


1. Context verification

Message contexts are used to add more control on when messages are displayed. A context can be defined as the text inside “[]” within the Dashboard’s “Campaign Name” e.g. the message name is “[ctx1] title” so the context is “ctx1”. Multiple contexts are supported. In order to handle defined contexts in the code an optional callback is called before a message is displayed:

InAppMessaging.instance().onVerifyContext = { contexts: List<String>, campaignTitle: String -> Boolean
    if /* check your condition e.g. are you on the correct screen to display this message? */ {
        true // campaign message will be displayed
    } else {
        false // campaign message won't be displayed
    }
}

2. closeCampaign()

There may be cases where apps need to manually close the messages without user interaction.

An example is when a different user logs-in and the currently displayed message does not target the new user. It is possible that the new user did not close the message (tapping the ‘X’ button) when logging in. The app can force-close the message by calling this method.

An optional parameter, clearQueuedCampaigns, can be set to true (false by default) which will additionally remove all messages that were queued to be displayed.

InAppMessaging.instance().closeMessage(clearQueuedCampaigns = true|false)

Note: Calling this method will not increment the campaign’s impression (i.e not counted as displayed)

3. Custom fonts

The SDK can optionally use custom fonts on the message’ header and body texts, and button texts. The default Android system font will be used if custom fonts are not added.

To use custom fonts:

  1. Add the font files, ttf or otf format, to the font resource folder of your app.
  2. To use custom font for the following message parts, define a string resource in the app’s res/values/string.xml:

Note: You can set the same font filename for the different string resources to use the same font.

...
├── res
     ├── font
          ├── your_app_font.otf // or ttf format
          ├── your_app_other_font.otf // or ttf format

in strings.xml:

    <string name="iam_custom_font_header">your_app_font</string>
    <string name="iam_custom_font_body">your_app_font</string>
    <string name="iam_custom_font_button">your_app_other_font</string>

4. Tooltips

Tooltips are messages attached to particular anchor views within the app. To enable this feature, refer to the Configuration section.

This feature is in beta testing, therefore its features and behavior might change in the future. Please refer to the internal guide for more information.

SDK Logic


(click to expand)

Client-side opt-out handling

If user (with valid identifiers in UserInfoProvider) opts out from a message, that information is saved in user cache locally on the device and the message won’t be shown again for that user on the same device. The opt-out status is not shared between devices. The same applies for anonymous user.

Client-side max impressions handling

Message impressions (displays) are counted locally for each user. Meaning that a message with maxImpression value of 3 will be displayed to each user (different identifiers in UserInfoProvider class) max 3 times. Max impression number can be modified in the Dashboard. Then the SDK, after next ping call, will compare new value with old max impression number and add the difference to the current impression counter. The max impression data is not shared between devices. The same applies for anonymous user.

Troubleshooting


(click to expand)

Proguard ParseException

Caused by: java.io.IOException: proguard.ParseException: Unknown option '-if' in line 84 of file
This error will be thrown during compilation if `minifyEnabled = true`, and your Proguard version is below 6.0.
(click to expand) Recommendation: Update your project's Android Gradle Plugin to the latest version, it includes the latest version of Proguard.

Less optimal solution: Force Gradle to use the latest version of Proguard(https://sourceforge.net/p/proguard/discussion/182455/thread/89a4d63d/):

buildscript {
    ...
    configurations.all {
      resolutionStrategy {
        force 'net.sf.proguard:proguard-gradle:6.0.3'
        force 'net.sf.proguard:proguard-base:6.0.3'
      }
    }
}

Duplicate class ManifestConfig

Build Error: java.lang.RuntimeException: Duplicate class com.rakuten.tech.mobile.manifestconfig.annotations.ManifestConfig

(click to expand)

This build error could occur if you are using older versions of other libraries from com.rakuten.tech.mobile. Some of the dependencies in this SDK have changed to a new Group ID of io.github.rakutentech (due to the JCenter shutdown). This means that if you have another library in your project which depends on the older dependencies using the Group ID com.rakuten.tech.mobile, then you will have duplicate classes.

To avoid this, please add the following to your build.gradle in order to exclude the old com.rakuten.tech.mobile dependencies from your project.

configurations.all {
    exclude group: 'com.rakuten.tech.mobile', module: 'manifest-config-processor'
    exclude group: 'com.rakuten.tech.mobile', module: 'manifest-config-annotations'
}

Other Issues

Rakuten developers experiencing any other problems should refer to the Troubleshooting Guide on the internal developer documentation portal.

Frequently Asked Questions


(click to expand)

Q: How do I send message based on app version?

When creating messages, you can specify the app version - such as debug, staging, or production version. <versionName>.<versionCode> is the format when specifying the versions; for example, 1.0.0-staging.101, 1.0.0-prod.203, or 0.x.x.4.

Q: How many times the message is sent to the device? Does it depends on Max Lifetime Impressions?

The max impression is handled by SDK and is bound to user per device.

  1. Scenario- Max impression is set to 2. User does not login with any ID. So It will be shown 2 times.
  2. Scenario- Max impression is set to 2. User login with any ID for 2 devices. It will show 2 times for each device.
  3. The message start time can be shown

Please refer to max impression handling for more details.

Q: Is the message displayed if ALL or ANY of triggers are satisfied?

All the events “launch the app event, login event, purchase successful event, custom event” work as AND. It will send to the device only all defined event are triggered.

Changelog


7.x

(click to expand)

7.7.0 (2024-10-22)

7.6.0 (2024-09-17)

7.5.0 (2023-12-12)

7.4.0 (2023-04-24)

7.3.0 (2022-12-13)

7.2.0 (2022-09-28)

7.1.0 (2022-06-24)

7.0.0 (2022-04-25)

6.x

(click to expand)

6.1.0 (2022-02-09)

6.0.0 (2021-12-03)

5.x and older

(click to expand)

5.0.0 (2021-09-10)

4.0.0 (2021-08-04)

3.0.0 (2021-03-24)

2.3.0 (2021-02-24)

2.2.0 (2020-11-10)

2.1.0 (2020-09-18)

2.0.0 (2020-06-11)

1.4.0

1.3.0

1.2.0