Skip to main content

Jetpack Compose integration

Requirements

Minimum Android SDK - 23 Minimum IAS SDK version - 1.25.5 (to implement IAS Compose SDK - you need to use same version as main SDK's)

Adding to the project

In the build.gradle (app level or module level) in the dependencies section add:

implementation 'com.github.inappstory:android-sdk-compose:1.25.5'

InAppStorySDK

InAppStorySDK is the main class in IAS Compose SDK. It is an kotlin object and it contains submodules (subclasses) for different SDK features. Also it uses to init SDK, clear cached data and pass backpress operations to SDK

object InAppStorySDK {
val inAppStoryManager: IASManager
val settings: IASSettings
val stories: IASStories
val games: IASGames
val inAppMessages: IASInAppMessages
val banners: IASBanners
val callbacks: IASCallbacks

fun initSdk(context: Context) /* equals to InAppStoryManager.initSdk(context: Context) */
fun clearCache() /* equals to InAppStoryManager.getInstance()?.clearCache() */
fun handleBackPress(): Boolean /* equals to InAppStoryManager.getInstance()?.clearCache() */
}

Class has next submodules:

IASManager

Submodule is used to build an instance of InAppStoryManager. Also it allows to force close all readers.

class IASManager { /*uses to set up InAppStoryManager instance.*/
fun closeReaders(complete: () -> Unit) /*equals to InAppStoryManager.closeStoryReader(true, complete: ForceCloseReaderCallback)*/

fun create(
apiKey: String,
userId: String? = null,
userSign: String? = null,
lang: Locale = Locale.getDefault(),
tags: ArrayList<String?>? = null,
placeholders: MutableMap<String?, String?>? = null,
imagePlaceholders: MutableMap<String?, ImagePlaceholderValue?>? = null,
extraOptions: MutableMap<String?, String?>? = null,
testKey: String? = null,
gameDemoMode: Boolean = false,
deviceIdEnabled: Boolean = true,
sandbox: Boolean = false
): InAppStoryManager? /* equals to InAppStoryManager.Builder().create() */
}

IASSettings

Submodule is used to set InAppStoryManager settings in runtime (after main instance already was built) such as user id and sign, language, tags, placeholders, options and common [AppearanceManager]

class IASSettings {
/* All methods are equals to InAppStoryManager.getInstance()?.{ methodName }() */

fun inAppStorySettings(settings: InAppStoryUserSettings?)

fun deviceId(deviceId: String?)

fun userId(userId: String?, sign: String? = null)

fun externalAppVersion(externalAppVersion: IAppVersion)

fun gameDemoMode(gameDemoMode: Boolean)

fun lang(lang: Locale?, changeLayoutDirection: Boolean)

fun placeholder(key: String?, value: String?)

fun imagePlaceholder(key: String?, value: ImagePlaceholderValue?)

fun placeholders(newPlaceholders: MutableMap<String?, String?>)

fun imagePlaceholders(newPlaceholders: MutableMap<String?, ImagePlaceholderValue?>)

fun setCommonAppearanceManager(appearanceManager: AppearanceManager?)

fun tags(tags: MutableList<String?>?)

fun addTags(tags: MutableList<String?>?)

fun removeTags(tags: MutableList<String?>?)

fun options(extraOptions: MutableMap<String?, String?>?)
}

IASStories

Submodule is used to launch story reader besides feed widgets (single stories and onboardings) and to set callbacks for story reader

class IASStories {
val single: IASSingle
val onboardings: IASOnboardings

fun onClickOnShareStory(shareClick: (SlideData?) -> Unit = {})

fun onStoryWidget(
widgetEvent: (
slideData: SlideData?,
widgetEventName: String?,
widgetData: Map<String?, String?>?
) -> Unit
)

fun onCloseStory(
closeStory: (
slideData: SlideData?,
action: CloseReader?
) -> Unit
)

fun onFavoriteStory(
favoriteStory: (
slideData: SlideData?,
value: Boolean
) -> Unit
)

fun onLikeDislikeStory(
likeStory: (
slideData: SlideData?,
value: Boolean
) -> Unit,
dislikeStory: (
slideData: SlideData?,
value: Boolean
) -> Unit
)
}

class IASSingle {
fun showOnce(
context: Context,
storyId: String,
appearanceManager: AppearanceManager = AppearanceManager(),
show: () -> Unit = {},
error: () -> Unit = {},
alreadyShown: () -> Unit = {}
): CancellationToken

fun show(
context: Context,
storyId: String,
appearanceManager: AppearanceManager = AppearanceManager(),
show: () -> Unit = {},
error: () -> Unit = {},
slide: Int = 0
): CancellationToken

fun callback(
success: (storyData: StoryData?) -> Unit = {},
error: (storyId: String?, reason: String?) -> Unit
)
}

class IASOnboardings {
fun show(
context: Context,
feed: String = "onboardings",
appearanceManager: AppearanceManager = AppearanceManager(),
tags: MutableList<String>? = arrayListOf(),
limit: Int = 100
): CancellationToken

fun callback(
success: (count: Int, feed: String?) -> Unit = { _, _ -> },
error: (feed: String?, reason: String?) -> Unit = { _, _ -> }
)
}

Below described how to set stories feed widgets (common and favorites).

IASGames

Submodule is used to launch game reader separately from another widgets with method open. It also can be user to preload games, force close current game reader if necessary and to set game reader callbacks

class IASGames {
fun open(context: Context, gameId: String)

fun preloadGames()

fun close()

fun callback(
startGame: (
gameLaunchSourceData: ContentData?,
gameId: String?
) -> Unit,
closeGame: (
gameLaunchSourceData: ContentData?,
gameId: String?
) -> Unit,
eventGame: (
gameLaunchSourceData: ContentData?,
gameId: String?,
eventName: String?,
payload: String?
) -> Unit,
gameLoadError: (
gameLaunchSourceData: ContentData?,
gameId: String?
) -> Unit,
gameOpenError: (
gameLaunchSourceData: ContentData?,
gameId: String?
) -> Unit
)
}

IASInAppMessages

Submodule is used to preload in-app messages and to set all reliable callbacks.

class IASInAppMessages {
fun preload(
inAppMessagePreloadSettings: InAppMessagePreloadSettings?,
iamLoaded: (id: Int) -> Unit = {},
allIamLoaded: () -> Unit = {},
iamLoadError: (id: Int) -> Unit = {},
allIamLoadError: () -> Unit = {},
isEmpty: () -> Unit = {},
)

fun onShowInAppMessageSlide(showSlide: (iamSlideData: InAppMessageSlideData?) -> Unit)

fun onShowInAppMessage(showIAM: (iamData: InAppMessageData?) -> Unit)

fun onCloseInAppMessage(closeIAM: (iamData: InAppMessageData?) -> Unit)

fun onInAppMessageWidget(
widgetEvent: (
inAppMessageData: InAppMessageData?,
widgetEventName: String?,
widgetData: Map<String?, String?>?
) -> Unit
)
}

Below described how to set container and how to show in-app messages.

IASBanners

Submodule is used to preload banners and to set all reliable callbacks.

class IASBanners {
fun onShowBanner(showBanner: (bannerData: BannerData?) -> Unit)

fun onBannerWidget(
widgetEvent: (
bannerData: BannerData?,
widgetEventName: String?,
widgetData: Map<String?, String?>?
) -> Unit
)

fun preloadBannerPlace(
bannerPlace: String,
error: () -> Unit,
loadedCurrent: () -> Unit
)
}

Below described how to set banner place widget.

IASCallbacks

Submodule is used to set rest of SDK callbacks (errors and callToAction).

class IASCallbacks {
fun onError(
loadListError: (feed: String?) -> Unit = {},
cacheError: () -> Unit = {},
emptyLinkError: () -> Unit = {},
sessionError: () -> Unit = {},
noConnection: () -> Unit = {},
)

fun onCallToAction(
cta: (
context: Context?,
slideData: ContentData?,
link: String?,
action: ClickAction?
) -> Unit
)
}

Stories

Widgets

To implement stories list use next composable:


@Composable
fun StoryListRV(
modifier: Modifier = Modifier.fillMaxWidth().wrapContentHeight(),
storyListController: StoryListController,
layoutManager: RecyclerView.LayoutManager? = null,
cacheId: String? = null,
feed: String = "default",
appearanceManager: AppearanceManager = AppearanceManager(),
customItem: (@Composable (StoryListItemState) -> Unit)? = null,
customFavItem: (@Composable (StoryListFavoriteItemState) -> Unit)? = null,
listItemTouchDown: (view: View?, position: Int) -> Unit = { _, _ -> },
listItemTouchUp: (view: View?, position: Int) -> Unit = { _, _ -> },
favoriteCellClick: () -> Unit = {},
listItemClick: (storyData: StoryData?, index: Int) -> Unit = { _, _ -> },
listScrollStart: () -> Unit = {},
listScrollEnd: () -> Unit = {},
visibleAreaUpdated: (shownStoriesListItemData: List<ShownStoriesListItem?>?) -> Unit = {}
)

Here StoryListController is a class where you can overload events like storiesLoaded, storiesUpdated and loadError.

var storiesLoaded: (feed: String?, size: Int, storyData: List<StoryData?>?) -> Unit =
{ _, _, _ -> }

var storiesUpdated: (feed: String?, size: Int, storyData: List<StoryData?>?) -> Unit =
{ _, _, _ -> }

var loadError: (feed: String?) -> Unit = {}

Also it has methods to load/refresh data in stories widget and method updateVisibleArea to trigger callback visibleAreaUpdated

fun load() //triggers by default on widget creation
fun refresh()
fun updateVisibleArea(triggerScrollCallback: Boolean)

Here is an example:

@Composable
fun ScreenStoryListRV(iamSettings: InAppMessageOpenSettings) {
val controller: StoryListController = remember {
StoryListController()
}
StoryListRV(storyListController = storyListController)
}

Items customization with composables

Customization interfaces like IStoriesListItem, IGetFavoriteListItem in AppearanceManager etc..., contains methods that demands you to create and pass Views If you want to create this views with also with compose instead of classic way - you can use parameter customItem and customFavItem in StoryListRV. Here StoryListItemState and StoryListFavoriteItemState are next classes:

data class StoryListItemState(
val title: String = "",
val backgroundColor: Int = Color.BLACK,
val titleColor: Int = Color.BLACK,
val imageLocalPath: String? = null,
val videoLocalPath: String? = null,
val opened: Boolean = false,
val hasAudio: Boolean = false,
val storyId: Int = -1
)

data class StoryListFavoriteItemState(
val count: Int = 0,
val backgroundColors: List<Int> = emptyList(),
val images: List<String?> = emptyList()
)

Cache

StoryListRV use same set of parameters to cache data as StoriesList - tags, cacheId, feed. On widget creation it calls storyListController.load (in LaunchedEffect) and gets cached data by key if possible. Besides refresh method you can also clear cached data with one of next methods:

val feed = "feed"
val cacheId = "cacheId"

InAppStoryManager.getInstance()?.clearCachedLists()
InAppStoryManager.getInstance()?.clearCachedListByFeed(feed)
InAppStoryManager.getInstance()?.clearCachedListById(cacheId)
InAppStoryManager.getInstance()?.clearCachedListByIdAndFeed(feed, cacheId)

Favorite stories

To implement favorite stories list use next composable:

@Composable
fun FavoriteStoryListRV(
modifier: Modifier = Modifier.fillMaxWidth().wrapContentHeight(),
storyListController: StoryListController,
layoutManager: RecyclerView.LayoutManager? = null,
appearanceManager: AppearanceManager = AppearanceManager(),
customItem: (@Composable (StoryListItemState) -> Unit)? = null,
listItemTouchDown: (view: View?, position: Int) -> Unit = { _, _ -> },
listItemTouchUp: (view: View?, position: Int) -> Unit = { _, _ -> },
listItemClick: (storyData: StoryData?, index: Int) -> Unit = { _, _ -> },
listScrollStart: () -> Unit = {},
listScrollEnd: () -> Unit = {},
visibleAreaUpdated: (shownStoriesListItemData: List<ShownStoriesListItem?>?) -> Unit = {}
)

In-App Messages

To show In-App Messages you need to define a container, where it will be shown. For this you can use widget:

@Composable
fun InAppMessageScreen(screenController: InAppMessageScreenController)

Here InAppMessageScreenController is a class that allows you to control lifecycle of In-App Message container. It has next methods:

fun pauseView()
fun resumeView()
fun closeView()
fun openInAppMessage(
openSettings: InAppMessageOpenSettings,
readerOpened: () -> Unit = {},
readerClosed: () -> Unit = {},
readerOpenErr: (error: String?) -> Unit = {},
): CancellationToken?

Here is an example:

@Composable
fun ScreenWithIAMLauncher(iamSettings: InAppMessageOpenSettings) {
val controller: InAppMessageScreenController = remember {
InAppMessageScreenController()
}
InAppMessageScreen(controller)
DisposableEffect(true) {
controller.openInAppMessage(
openSettings = iamSettings,
readerOpened = {},
readerClosed = {},
readerOpenErr = {}
)
}
}

Banners

To implement banner place (carousel) use next composable:

@Composable
fun BannerCarousel(
modifier: Modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
bannerCarouselController: BannerCarouselController,
placeId: String,
uniqueId: String? = null,
appearance: ICustomBannerCarouselAppearance = CustomBannerCarouselAppearance(),
pageScrolled: (
current: Int,
total: Int,
offsetFraction: Float,
offsetInPx: Int
) -> Unit = { _, _, _, _ -> },
pageSelected: (
current: Int,
total: Int
) -> Unit = { _, _ -> }
)

Here BannerCarouselController is a class where you can overload events like bannerPlaceLoaded, bannerLoaded, loadError and bannerLoadError.

var bannerPlaceLoaded: (size: Int, bannerData: List<BannerData?>?, widgetHeight: Int) -> Unit =
{ _, _, _ -> }

var loadError: () -> Unit = { }

var bannerLoaded: (bannerId: Int, isCurrent: Boolean) -> Unit = { _, _ -> }

var bannerLoadError: (bannerId: Int, isCurrent: Boolean) -> Unit = { _, _ -> }

Also it has methods to load/refresh data in widget, navigation methods : showNext,showPrevious,showByIndex, and place autoscroll control methods : pauseAutoscroll and resumeAutoscroll

fun load() //triggers by default on widget creation
fun refresh()
fun showNext()
fun showPrevious()
fun showByIndex(index: Int)
fun pauseAutoscroll()
fun resumeAutoscroll()

Custom share

To customize share panel, you need to initialize class CustomShare:

class CustomShare(
val shareContent: @Composable (
shareData: HashMap<String, Any>,
actions: OverlappingContainerActions
) -> Unit,
val updateViewVisibility: (Boolean) -> Unit,
val getViewActions: (data: Map<String, Any?>?) -> Unit = {},
)

Then you can use static method share of CustomShare:

fun <T : BroadcastReceiver> share(
context: Context,
data: IASShareData,
packageName: String?,
receiver: Class<T>
)

fun shareWithSpecificApp(
context: Context,
shareData: IASShareData
) {
CustomShare.share(context, shareData, SPECIFIC_APP_PACKAGE, ShareBroadcastReceiver::class.java)
}

fun shareDefault(
context: Context,
shareData: IASShareData
) {
CustomShare.share(context, shareData, null, ShareBroadcastReceiver::class.java)
}

To restore default panel - you need to use set share callback manually from InAppStory SDK:

InAppStoryManager.getInstance()?.setShareCallback(null)

Fragment reader presentation (stories and games)

By default story reader and game reader shows in new activity. If you want to prevent this behaviour and show reader in same activity, you can open stories screen as a fragment (but you need to set root activity as FragmentActivity successor. To implement container for readers you need to use next composable:

fun StoryAndGameFragmentScreen(
onBackPressed: () -> Unit
)

Here onBackPressed triggers only when game and story readers are closed. If any reader opened, StoryAndGameFragmentScreen catch all BackPress actions.