Skip to main content

In-App-Messaging

Starting from 1.21.0 In-App-Messaging was added to the InAppStory SDK.

This documentation provides a detailed guide on how to use the InAppMessaging (IAM) module, which allows you to manage in-app messages (such as popups, fullscreens and bottomsheets) in your application. The module provides methods for preloading messages, displaying them, and handling related events.

Show In-app Message

If you want to show In-App Message by message id or event, you can use method InAppStoryManager.getInstance()?.showInAppMessage(...)

class InAppMessageOpenSettings {
fun id(id: Int?)
fun showOnlyIfLoaded(showOnlyIfLoaded: Boolean?)
fun tags(tags: List<String>?)
fun event(event: String?)
}

//id or event has to be passed as non null

fun showInAppMessageById(
iamId: Int,
// Pass this as true if you don't want to show not preloaded In-app messages and as false otherwise
showOnlyIfLoaded: Boolean,
// It can be activity.supportFragmentManager or childFragmentManager of another fragment.
fragmentManager: FragmentManager,
// Here you need to pass container id where in-app message will be placed. F.e. R.id.iam_fragment_container
// It is better if this container will be full screen or occupy as much screen area as possible
containerId: Int,
readerIsOpened: () -> Unit,
readerOpenError: () -> Unit,
readerIsClosed: () -> Unit,
) {
val cancellationToken: CancellationToken? =
InAppStoryManager.getInstance()?.showInAppMessage(
//It's an object with builder pattern setters, but can be used as data class described before
InAppMessageOpenSettings()
.id(iamId)
.showOnlyIfLoaded(showOnlyIfLoaded),
fragmentManager,
containerId,
//This callback you can use to show your own intermediate loader before reader will be opened
object : InAppMessageScreenActions {
override fun readerIsOpened() {
readerIsOpened()
}

override fun readerOpenError(error: String?) {
readerOpenError()
}

override fun readerIsClosed() {
readerIsClosed()
}
}
)
}

fun showInAppMessageByEvent(
event: String,
// Pass this as true if you don't want to show not preloaded In-app messages and as false otherwise
showOnlyIfLoaded: Boolean,
// It can be activity.supportFragmentManager or childFragmentManager of another fragment.
fragmentManager: FragmentManager,
// Here you need to pass container id where in-app message will be placed. F.e. R.id.iam_fragment_container
// It is better if this container will be full screen or occupy as much screen area as possible
containerId: Int,
readerIsOpened: () -> Unit,
readerOpenError: () -> Unit,
readerIsClosed: () -> Unit,
) {
InAppStoryManager.getInstance()?.let { manager ->
manager.showInAppMessage(
//It's an object with builder pattern setters, but can be used as data class described before
InAppMessageOpenSettings()
.event(event)
.showOnlyIfLoaded(showOnlyIfLoaded),
fragmentManager,
containerId,
//This callback you can use to show your own intermediate loader before reader will be opened
object : InAppMessageScreenActions {
override fun readerIsOpened() {
readerIsOpened()
}

override fun readerOpenError(error: String?) {
readerOpenError()
}

override fun readerIsClosed() {
readerIsClosed()
}
}
)
}
}

More about CancellationToken objects you can read here

Providing presentation container

Started from 1.24.2 you can specify container in runtime (before In-app Message will be shown). It allows to provide different containers for different cases (f.e. - in case of different In-app Message types). Also you can use FrameLayout as provided container if you don't support fragments (FragmentActivity) in your app

class InAppMessageContainerSettings {
fun fragment(
fragmentManager: FragmentManager,
containerId: Int
): InAppMessageContainerSettings

fun layout(
layout: FrameLayout
): InAppMessageContainerSettings
}

interface IAMViewController {
fun pauseView()
fun resumeView()
fun closeView()
}

interface InAppMessageContainerProvider {
fun provideContainer(messageData: InAppMessageData?): InAppMessageContainerSettings
fun layoutController(): IAMViewController?
}

fun showInAppMessageByIdInCustomContainer(
iamId: Int,
fragmentManager: FragmentManager,
layoutForToasts: FrameLayout,
containerIdForRest: Int,
readerOpened: () -> Unit,
readerError: () -> Unit,
readerClosed: () -> Unit,
) {
val cancellationToken = InAppStoryManager.getInstance()?.showInAppMessage(
InAppMessageOpenSettings()
.id(iamId)
.showOnlyIfLoaded(false),
object : InAppMessageContainerProvider {
override fun provideContainer(messageData: InAppMessageData?):
InAppMessageContainerSettings {
return if (messageData?.messageType() == InAppMessageType.TOAST) {
InAppMessageContainerSettings().layout(
layoutForToasts
)
} else {
InAppMessageContainerSettings().fragment(
fragmentManager,
containerIdForRest
)
}
}

override fun layoutController(): IAMViewController? {
return null
}
},
object : InAppMessageScreenActions {
override fun readerIsOpened() {
readerOpened()
}

override fun readerOpenError(errorStr: String?) {
readerError()
}

override fun readerIsClosed() {
readerClosed()
}
}
)
}

Managing In-app Message reader lifecycle

Started from 1.24.2 when you specify container with InAppMessageContainerProvider you also can set IAMViewController to control In-app Message reader lifecycle. To simple creation you can use SDK's InAppMessageViewController that implements IAMViewController interface.

val iamController = InAppMessageViewController()
showInAppMessageByIdWithContainerController(iamController)

fun showInAppMessageByIdWithContainerController(
iamId: Int,
fragmentManager: FragmentManager,
containerController: IAMViewController,
containerId: Int,
readerOpened: () -> Unit,
readerError: () -> Unit,
readerClosed: () -> Unit,
) {
val cancellationToken = InAppStoryManager.getInstance()?.showInAppMessage(
InAppMessageOpenSettings()
.id(iamId)
.showOnlyIfLoaded(false),
object : InAppMessageContainerProvider {
override fun provideContainer(messageData: InAppMessageData?):
InAppMessageContainerSettings {
return InAppMessageContainerSettings().fragment(
fragmentManager,
containerId
)
}

override fun layoutController(): IAMViewController {
return containerController
}
},
object : InAppMessageScreenActions {
override fun readerIsOpened() {
readerOpened()
}

override fun readerOpenError(errorStr: String?) {
readerError()
}

override fun readerIsClosed() {
readerClosed()
}
}
)
}

Preload In-app Messages

If you want to preload In-App Messages (f.e. on splash or another screen), you can use method InAppStoryManager.getInstance()?.preloadInAppMessages(...):

interface InAppMessageLoadCallback {
fun loaded(id: Int)

fun allLoaded()

fun loadError(id: Int)

fun loadError()

fun isEmpty()
}

fun preloadInAppMessages() {
InAppStoryManager.getInstance()?.preloadInAppMessages(
object : InAppMessageLoadCallback {
override fun loaded(id: Int) {
//It's not a main thread!!!
//Triggers when In-app Message is preloaded
}

override fun allLoaded() {
//It's not a main thread!!!
//Triggers when all In-app Messages are preloaded
success.invoke()
}

override fun loadError(id: Int) {
//It's not a main thread!!!
error.invoke()
}

override fun loadError() {
//It's not a main thread!!!
error.invoke()
}

override fun isEmpty() {
//It's not a main thread!!!
isEmpty.invoke()
}
}
)
}

InAppMessageLoadCallback can be null.

If you want to preload specific In-app messages, or a list with specific tags, they can be passed through InAppMessagePreloadSettings in preloadInAppMessages:

class InAppMessagePreloadSettings {
fun tags(tags: List<String>?)
fun inAppMessageIds(inAppMessageIds: List<String>?)
}

fun preloadInAppMessages(inAppMessageIds: List<String>, tags: List<String>) {
InAppStoryManager.getInstance()?.preloadInAppMessages(
InAppMessagePreloadSettings()
.tags(tags)
.inAppMessageIds(inAppMessageIds),
object : InAppMessageLoadCallback {
override fun loaded(id: Int) {
//It's not a main thread!!!
//Triggers when In-app Message is preloaded
}

override fun allLoaded() {
//It's not a main thread!!!
//Triggers when all In-app Messages are preloaded
success.invoke()
}

override fun loadError(id: Int) {
//It's not a main thread!!!
error.invoke()
}

override fun loadError() {
//It's not a main thread!!!
error.invoke()
}

override fun isEmpty() {
//It's not a main thread!!!
isEmpty.invoke()
}
}
)
}

Also you can listen preloading status of In-App Messages with external callback (but its events can be triggered not only from preload method):

fun preloadInAppMessages(
success: () -> Unit,
error: () -> Unit,
isEmpty: () -> Unit
) {
InAppStoryManager.getInstance()?.let { manager ->

manager.setInAppMessageLoadCallback(
object : InAppMessageLoadCallback {
override fun loaded(id: Int) {
//It's not a main thread!!!
//Triggers when In-app Message is preloaded
}

override fun allLoaded() {
//It's not a main thread!!!
//Triggers when all In-app Messages are preloaded
success.invoke()
}

override fun loadError(id: Int) {
//It's not a main thread!!!
error.invoke()
}

override fun loadError() {
//It's not a main thread!!!
error.invoke()
}

override fun isEmpty() {
//It's not a main thread!!!
isEmpty.invoke()
}
}
)
manager.preloadInAppMessages(null)
}
}

BackPress handling

All in-app messages opens as fragments in passed fragmentManager. That's why it's necessary to pass BackPress events from parent activity to in-app message fragment:

override fun onBackPressed() {
InAppStoryManager.getInstance()?.let {
if (it.onBackPressed())
return
}
parentActivityOnBackPressHandling()
}

private fun parentActivityOnBackPressHandling() {
//...
}

Events

enum class InAppMessageType {
FULLSCREEN, POPUP, BOTTOM_SHEET, TOAST, UNDEFINED
}

data class InAppMessageData(
val id: Int,
val title: String?,
val event: String?,
val messageType: InAppMessageType
)

data class InAppMessageSlideData(
val index: Int,
val inAppMessage: InAppMessageData,
val payload: String?
)

ShowInAppMessage

When you show in-app message reader

fun setIASShowInAppMessageCallback() {
InAppStoryManager.getInstance().setShowInAppMessageCallback(
object : ShowInAppMessageCallback {
override fun showInAppMessage(
iamData: InAppMessageData?
) {
TODO("Not yet implemented")
}
}
)
}

CloseInAppMessage

When you close in-app message reader

fun setIASCloseInAppMessageCallback() {
InAppStoryManager.getInstance().setCloseInAppMessageCallback(
object : CloseInAppMessageCallback {
override fun closeInAppMessage(
iamData: InAppMessageData?
) {
TODO("Not yet implemented")
}
}
)
}

InAppMessageWidget

When you get any events from widgets in in-app messages

fun setIASInAppMessageWidgetCallback() {
InAppStoryManager.getInstance().setInAppMessageWidgetCallback(
object : InAppMessageWidgetCallback {
override fun inAppMessageWidget(
iamData: InAppMessageData?,
widgetEventName: String?,
widgetData: Map<String?, String?>?
) {
TODO("Not yet implemented")
}
}
)
}

ShowInAppMessageSlideCallback

Starting from 1.22.0 multi-slide in-app messages support was added to SDK.

When you get any events from slide changing in in-app messages (single slide in-app messages also triggers this event)

fun setIASShowInAppMessageSlideCallback() {
InAppStoryManager.getInstance().setShowInAppMessageSlideCallback(
object : ShowInAppMessageSlideCallback {
override fun showSlide(
iamData: InAppMessageSlideData?
) {
TODO("Not yet implemented")
}
}
)
}

Reader customization

To customize close icon - use csCloseIcon property in global AppearanceManager instance. Here more about icons customization