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.