A StorytellerDelegate has methods for managing Storyteller events. It is used as global object for handling all events when opening a Story Player with and without the StorytellerStoriesRowView or StorytellerStoriesGridView. Please see the dedicated StorytellerListViewDelegate below for handling events related to rows and grids.
The onUserActivityOccurred(type: StorytellerUserActivity.EventType, data: UserActivityData) method is is called when an analytics event is triggered. See the dedicated Analytics page for more information on analytic events.
The getAd(adRequestInfo: StorytellerAdRequestInfo, onComplete: (StorytellerAd) -> Unit = {}, onError: () -> Unit) method is called when the tenant is configured to request ads from the containing app and the SDK requires ad data from the containing app. For more information on how to supply ads to the Storyteller SDK, see the dedicated Ads page.
The userNavigatedToApp(url: String) method is called when a user taps on an action button on a Page which should direct the user to a specific place within the integrating app. More information on In App links Navigating to App. For more information on deep linking, see the dedicated Deep Linking page.
The onShareButtonTapped(text: String, title: String, url: String) method is called when Storyteller.useCustomShareHandling = true and the user taps a Story or Clip share entry point.
When custom share handling is enabled:
The SDK does not open the Android system share sheet internally.
The SDK calls onShareButtonTapped instead.
Existing share-tap analytics continue to be emitted by the SDK.
Share success/completion remains the responsibility of the integrating app.
Payload details:
text - the SDK-generated share text for the selected Story or Clip
title - the Story or Clip title used for display in the host app
url - the deeplink URL for the selected Story or Clip
The callback onUserActivityOccurred provides analytics events and corresponding data triggered internally by the SDK. This information can be used in your app.
The following parameters are passed to the callback method:
type - type of event that occurred, as a StorytellerUserActivity.EventType enum
data - an object containing data about the event which occurred
Example:
...overridefunonUserActivityOccurred(type:StorytellerUserActivity.EventType,data:UserActivityData){if(type==StorytellerUserActivity.EventType.OPENED_STORY){// Retrieve the story id valuevalopenStoryId=data.storyId// Retrieve the story title valuevalopenStoryTitle=data.storyTitle// Report retrieved values from your app}}
For a detailed discussion of all the relevant events and properties please see the dedicated Analytics page.
By implementing getAd, you can provide custom ad data for the SDK to render, this is only applicable when the ad configuration is set to Integrating App in the CMS. Ad data can be obtained asynchronously, and should be provided using the onComplete closure parameter.
Example:
...overridefungetAd(adRequestInfo:StorytellerAdRequestInfo,onComplete:(StorytellerAd)->Unit={},onError:()->Unit){// Action to get some adsvalad=getMyAd()// Convert the ad to a StorytellerAdvalstorytellerAd=convertToStorytellrAd(ad)// Provide the ad to the SDKonComplete(ad)}
For a detailed discussion of all the relevant considerations, please see the dedicated Ads page.
This method allows you to configure the WebView with custom settings or actions when the
Storyteller SDK, is to display a WebView.
It takes three parameters: the WebView to configure, an optional URL string, and an optional
favicon Bitmap. This method is called from the onPageStarted method of a custom WebViewClient
when a new page starts loading in the WebView.
To intercept and customize WebViews created by the Storyteller SDK, you can implement your own version
of the configureWebView method.
Here is an example of how to do this:
overridefunconfigureWebView(view:WebView,url:String?,favicon:Bitmap?){// Your custom configuration code goes here// The following line shows a simple alert in the WebView with the message "test webview javascript".view.evaluateJavascript("javascript: alert('test webview javascript');",null)}
The method fun categoryFollowActionTaken(category: Category, isFollowing: Boolean) is invoked when a user adds or removes a category from within SDK's UI.
The callback method receives the following parameters:
category - An object representing the clip category
isFollowing - A boolean value indicating whether the user is following or unfollowing the specified category
The fun customScreenForCategory(): @Composable (StorytellerFollowableCategoryCustomScreen) -> Unit method is called when a user navigates to a followable category screen. The callback receives a StorytellerFollowableCategoryCustomScreen object. with the following parameters:
pendingModifier - a Modifier object that is used to apply custom styling to the screen, this must be applied to the root composable of your custom screen
category - a Category object that is the category that the user is navigating to
onBackClicked - a () -> Unit a callback that should be called when the user clicks the back button from the custom screen, this is to let Storyteller know that the user has navigated back to the previous screen. If this is not called, the user will be stuck on the custom screen and unable to navigate back to the previous screen.
Example:
overridefuncustomScreenForCategory():@Composable()((StorytellerFollowableCategoryCustomScreen)->Unit){return{(pendingModifier,category,onBackClicked)->Scaffold(modifier=pendingModifier,topBar={CenterAlignedTopAppBar(title={Text("${category.displayTitle}")},navigationIcon={IconButton(onClick=onBackClicked){Icon(imageVector=Icons.AutoMirrored.Default.ArrowBack,contentDescription="Back")}})}){paddingValues->Surface(modifier=Modifier.padding(paddingValues)){Box(modifier=Modifier.fillMaxSize(),contentAlignment=androidx.compose.ui.Alignment.Center){Text("Custom layout for ${category.displayTitle}")}}}}}
Alongside the customScreenForCategory callback, the Storyteller object also has a useCustomScreenForCategory property that is a (Category) -> Boolean callback. This is to allow the developer to conditionally show the custom screen for a specific category or default to the original implementation of Followable Categories by Storyteller.
The callback must return a Boolean value, which determines whether the custom screen should be shown for the given category.
Example:
Storyteller.useCustomScreenForCategory={if(it.type=="custom"){// return true if you want to show the custom followable category screentrue}else{// return false if you want to show the default followable category screenfalse}}
The customScreenForCategory will only be called if the useCustomScreenForCategory callback returns true for the given category. Otherwise, if it's not set or returns false, the default implementation of the Followable Categories by Storyteller will be used.
Similarly, Storyteller.useCustomShareHandling = true enables host-managed share handling through onShareButtonTapped. If it remains false, the SDK keeps the default Android share sheet behavior.
The fun bottomSheetScreen(urlProvider: () -> String, onDismiss: () -> Unit): @Composable () -> Unit method is called when the deep link includes the query parameter shouldUseModal=true.
urlProvider A callback that provides the URL to be loaded in the WebView or used for other purposes.
onDismiss A callback invoked when the user dismisses the bottom sheet.
The onTileTapped(tileType: StorytellerTileType) method is called when a user taps on a tile inside a row or grid. This callback is executed before the player Activity is opened.
tileType is a sealed class describing the tapped Story or Clip. Common SDK callback fields are
id, nullable title, nullable 1-based tileIndex, and nullable custom metadata. Story adds
categories; Clip adds collectionId and categories.
Example:
overridefunonTileTapped(tileType:StorytellerTileType){valtitle=tileType.titlevaloneBasedIndex=tileType.tileIndexvalpublisher=tileType.metadata?.get("publisher")when(tileType){isStorytellerTileType.Story->{// Handle story tile tapvalstoryId=tileType.idvalcategories=tileType.categories}isStorytellerTileType.Clip->{// Handle clip tile tapvalclipId=tileType.idvalcollectionId=tileType.collectionIdvalcategories=tileType.categories}}}
The SDK snapshots custom metadata from the originating content. metadata is null when the source omitted it or refreshed content can no longer be resolved, and empty when the source supplied no entries. title or tileIndex can also be null if the tapped item is removed during a refresh; a resolved title can be an empty string when the source supplies no title text. Application-created or data-class-copied StorytellerTileType values have no SDK callback context and return null for all three fields. The generated subtype equals, hashCode, and toString functions retain their legacy constructor-only behavior and do not include callback context.
Note: When theme.lists.enablePlayerOpen is set to false, this callback becomes the primary way to handle tile interactions, as the SDK will not automatically open the player.
By using the callback function onDataLoadComplete and the data it provides, you can handle the current state of the StorytellerStoriesRowView appropriately in your app.
Note: dataCount is the total number of Stories in the existing StorytellerStoriesRowView at any given time
Example:
...overridefunonDataLoadComplete(success:Boolean,error:StorytellerError?,dataCount:Int){if(success){// stories data has been loaded successfully// dataCount is the current total number of stories, including newly added/removed data}elseif(error!=null){// an error has occurred, unwrap the error value for more information// dataCount is the total number of stories before loading began}}
Another example:
...overridefunonDataLoadComplete(success:Boolean,error:StorytellerError?,dataCount:Int){if(error!=null&&dataCount==0){// stories have failed to load with error and there is no data to show// you may wish to hide the `StorytellerStoriesRowView` instance hereStorytellerStoriesRowView.visibility=View.GONE}}
{"slug": "storyteller-delegates", "page_title": "Storyteller Delegates", "page_url": "StorytellerDelegates/", "canonical_url": "/android/StorytellerDelegates/", "markdown": "# Implementing Storyteller Delegate Callbacks\n\n## Table of Contents\n\n- [StorytellerDelegate](#storytellerdelegate)\n- [StorytellerListViewDelegate](#storytellerlistviewdelegate)\n\nA `StorytellerDelegate` has methods for managing `Storyteller` events. It is used as global object for handling all events when opening a Story Player with and without the `StorytellerStoriesRowView` or `StorytellerStoriesGridView`. Please see the dedicated `StorytellerListViewDelegate` below for handling events related to rows and grids.\n\n## Showcase examples\n\n- [Compose \u2014 global delegate (`ShowcaseStorytellerDelegate`)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/compose/app/src/main/java/com/getstoryteller/storytellershowcaseapp/data/ShowcaseStorytellerDelegate.kt#L32)\n- [Compose \u2014 list delegate example (`PageItemStorytellerDelegate`)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/compose/app/src/main/java/com/getstoryteller/storytellershowcaseapp/ui/features/storyteller/PageItemStorytellerDelegate.kt#L14)\n- [XML \u2014 global delegate (`ShowcaseStorytellerDelegate`)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/xml/app/src/main/java/com/getstoryteller/storytellershowcaseapp/data/ShowcaseStorytellerDelegate.kt#L28)\n- [XML \u2014 list delegate example (`StorytellerViewDelegate`)](https://github.com/getstoryteller/storyteller-showcase-android/blob/main/xml/app/src/main/java/com/getstoryteller/storytellershowcaseapp/ui/features/dashboard/adapter/StorytellerViewDelegate.kt#L13)\n\n## StorytellerDelegate\n\nTo use global `StorytellerDelegate`, implement the `StorytellerDelegate` interface by overriding the required methods and set it in `Storyteller` object.\n\nExample:\n\n```kotlin\n Storyteller.storytellerDelegate = myCustomStorytellerDelegate\n```\n\n### onUserActivityOccured\n\nThe `onUserActivityOccurred(type: StorytellerUserActivity.EventType, data: UserActivityData)` method is is called when an analytics event is triggered. See the dedicated [Analytics](Analytics.md) page for more information on analytic events.\n\n### getAd\n\nThe `getAd(adRequestInfo: StorytellerAdRequestInfo, onComplete: (StorytellerAd) -> Unit = {}, onError: () -> Unit)` method is called when the tenant is configured to request ads from the containing app and the SDK requires ad data from the containing app. For more information on how to supply ads to the Storyteller SDK, see the dedicated [Ads](Ads.md) page.\n\n### userNavigatedToApp\n\nThe `userNavigatedToApp(url: String)` method is called when a user taps on an action button on a Page which should direct the user to a specific place within the integrating app. More information on `In App` links [Navigating to App](NavigatingToApp.md). For more information on deep linking, see the dedicated [Deep Linking](DeepLinking.md) page.\n\n### onShareButtonTapped\n\nThe `onShareButtonTapped(text: String, title: String, url: String)` method is called when `Storyteller.useCustomShareHandling = true` and the user taps a Story or Clip share entry point.\n\nWhen custom share handling is enabled:\n\n- The SDK does not open the Android system share sheet internally.\n- The SDK calls `onShareButtonTapped` instead.\n- Existing share-tap analytics continue to be emitted by the SDK.\n- Share success/completion remains the responsibility of the integrating app.\n\nPayload details:\n\n- `text` - the SDK-generated share text for the selected Story or Clip\n- `title` - the Story or Clip title used for display in the host app\n- `url` - the deeplink URL for the selected Story or Clip\n\nExample:\n\n```kotlin\nStoryteller.useCustomShareHandling = true\n\noverride fun onShareButtonTapped(\n text: String,\n title: String,\n url: String,\n) {\n openCustomShareSheet(\n title = title,\n text = text,\n url = url,\n )\n}\n```\n\n## Analytics\n\nThe callback `onUserActivityOccurred` provides analytics events and corresponding data triggered internally by the SDK. This information can be used in your app.\n\nThe following parameters are passed to the callback method:\n\n- `type` - type of event that occurred, as a `StorytellerUserActivity.EventType` enum\n- `data` - an object containing data about the event which occurred\n\nExample:\n\n```kotlin\n ...\n override fun onUserActivityOccurred(type: StorytellerUserActivity.EventType, data: UserActivityData) {\n if (type == StorytellerUserActivity.EventType.OPENED_STORY) {\n // Retrieve the story id value\n val openStoryId = data.storyId\n // Retrieve the story title value\n val openStoryTitle = data.storyTitle\n\n // Report retrieved values from your app\n }\n }\n```\n\nFor a detailed discussion of all the relevant events and properties please see the dedicated [Analytics](Analytics.md) page.\n\n## Client Ads\n\nBy implementing `getAd`, you can provide custom ad data for the SDK to render, this is only applicable when the ad configuration is set to `Integrating App` in the CMS. Ad data can be obtained asynchronously, and should be provided using the `onComplete` closure parameter.\n\nExample:\n\n```kotlin\n ...\n override fun getAd(adRequestInfo: StorytellerAdRequestInfo, onComplete: (StorytellerAd) -> Unit = {}, onError: () -> Unit) {\n // Action to get some ads\n val ad = getMyAd()\n // Convert the ad to a StorytellerAd\n val storytellerAd = convertToStorytellrAd(ad)\n // Provide the ad to the SDK\n onComplete(ad)\n }\n```\n\nFor a detailed discussion of all the relevant considerations, please see the dedicated [Ads](Ads.md) page.\n\n### configureWebView\n\nThis method allows you to configure the WebView with custom settings or actions when the\nStoryteller SDK, is to display a WebView.\n\nIt takes three parameters: the WebView to configure, an optional URL string, and an optional\nfavicon Bitmap. This method is called from the `onPageStarted` method of a custom `WebViewClient`\nwhen a new page starts loading in the WebView.\n\n#### Parameters\n\n- `view: WebView` - The WebView instance to be configured.\n- `url: String?` - (Optional) The URL string associated with the WebView page that is being loaded.\n- `favicon: Bitmap?` - (Optional) The favicon Bitmap to be displayed for the WebView page.\n\n#### Example Usage\n\nTo intercept and customize WebViews created by the Storyteller SDK, you can implement your own version\nof the `configureWebView` method.\n\nHere is an example of how to do this:\n\n```kotlin\noverride fun configureWebView(\n view: WebView,\n url: String?,\n favicon: Bitmap?\n) {\n // Your custom configuration code goes here\n // The following line shows a simple alert in the WebView with the message \"test webview javascript\".\n view.evaluateJavascript(\"javascript: alert('test webview javascript');\", null)\n}\n```\n\n## Followable Categories\n\nThe method `fun categoryFollowActionTaken(category: Category, isFollowing: Boolean)` is invoked when a user adds or removes a category from within SDK's UI.\n\nThe callback method receives the following parameters:\n\n- `category` - An object representing the clip category\n- `isFollowing` - A boolean value indicating whether the user is following or unfollowing the specified category\n\n### customScreenForCategory\n\nThe `fun customScreenForCategory(): @Composable (StorytellerFollowableCategoryCustomScreen) -> Unit` method is called when a user navigates to a followable category screen. The callback receives a `StorytellerFollowableCategoryCustomScreen` object. with the following parameters:\n\n- `pendingModifier` - a `Modifier` object that is used to apply custom styling to the screen, this must be applied to the root composable of your custom screen\n- `category` - a `Category` object that is the category that the user is navigating to\n- `onBackClicked` - a `() -> Unit` a callback that should be called when the user clicks the back button from the custom screen, this is to let Storyteller know that the user has navigated back to the previous screen. If this is not called, the user will be stuck on the custom screen and unable to navigate back to the previous screen.\n\nExample:\n\n```kotlin\n override fun customScreenForCategory(): @Composable()\n ((StorytellerFollowableCategoryCustomScreen) -> Unit) {\n return { (pendingModifier, category, onBackClicked) ->\n Scaffold(\n modifier = pendingModifier,\n topBar = {\n CenterAlignedTopAppBar(\n title = {\n Text(\"${category.displayTitle}\")\n },\n navigationIcon = {\n IconButton(onClick = onBackClicked) {\n Icon(\n imageVector = Icons.AutoMirrored.Default.ArrowBack,\n contentDescription = \"Back\"\n )\n }\n }\n )\n }\n ) { paddingValues ->\n Surface(\n modifier = Modifier.padding(paddingValues)\n ) {\n Box(\n modifier = Modifier.fillMaxSize(), contentAlignment = androidx.compose.ui.Alignment.Center\n ) {\n Text(\"Custom layout for ${category.displayTitle}\")\n }\n }\n }\n }\n }\n\n```\n\nAlongside the `customScreenForCategory` callback, the `Storyteller` object also has a `useCustomScreenForCategory` property that is a `(Category) -> Boolean` callback. This is to allow the developer to conditionally show the custom screen for a specific category or default to the original implementation of Followable Categories by Storyteller.\n\nThe callback must return a `Boolean` value, which determines whether the custom screen should be shown for the given category.\n\nExample:\n\n```kotlin\n Storyteller.useCustomScreenForCategory = {\n if (it.type == \"custom\") {\n // return true if you want to show the custom followable category screen\n true\n } else {\n // return false if you want to show the default followable category screen\n false\n }\n }\n```\n\nThe `customScreenForCategory` will only be called if the `useCustomScreenForCategory` callback returns `true` for the given category. Otherwise, if it's not set or returns `false`, the default implementation of the Followable Categories by Storyteller will be used.\n\nSimilarly, `Storyteller.useCustomShareHandling = true` enables host-managed share handling through `onShareButtonTapped`. If it remains `false`, the SDK keeps the default Android share sheet behavior.\n\n### bottomSheetScreen\n\nThe `fun bottomSheetScreen(urlProvider: () -> String, onDismiss: () -> Unit): @Composable () -> Unit` method is called when the deep link includes the query parameter `shouldUseModal=true`.\n\n- `urlProvider` A callback that provides the URL to be loaded in the WebView or used for other purposes.\n- `onDismiss` A callback invoked when the user dismisses the bottom sheet.\n\nExample:\n\n```kotlin\n override fun bottomSheetScreen(\n urlProvider: () -> String,\n onDismiss: () -> Unit\n ): @Composable (() -> Unit) {\n return {\n DemoBottomSheet(urlProvider, onDismiss)\n }\n }\n```\n\n## StorytellerListViewDelegate\n\nA `StorytellerListViewDelegate` has methods for managing `StorytellerStoriesRowView` and `StorytellerStoriesGridView` events.\n\n### How to Use\n\nTo use `StorytellerListViewDelegate`, implement the `StorytellerListViewDelegate` interface and override the required methods:\n\n#### onDataLoadStarted\n\nThe `onDataLoadStarted()` method is called when the network request to load data for all Stories has started.\n\n#### onDataLoadComplete\n\nThe `onDataLoadComplete(success: Boolean, error: Error?, dataCount: Int)` method is called when the data loading network request is complete.\n\n| Property | Description |\n| ----------- | ------------------------------------------------------- |\n| `success` | This confirms whether or not the request was successful |\n| `error` | The HTTP error if the request was not successful |\n| `dataCount` | The number of Stories loaded |\n\n#### onTileTapped\n\nThe `onTileTapped(tileType: StorytellerTileType)` method is called when a user taps on a tile inside a row or grid. This callback is executed before the player Activity is opened.\n\n`tileType` is a sealed class describing the tapped Story or Clip. Common SDK callback fields are\n`id`, nullable `title`, nullable 1-based `tileIndex`, and nullable custom `metadata`. Story adds\n`categories`; Clip adds `collectionId` and `categories`.\n\nExample:\n\n```kotlin\noverride fun onTileTapped(tileType: StorytellerTileType) {\n val title = tileType.title\n val oneBasedIndex = tileType.tileIndex\n val publisher = tileType.metadata?.get(\"publisher\")\n\n when (tileType) {\n is StorytellerTileType.Story -> {\n // Handle story tile tap\n val storyId = tileType.id\n val categories = tileType.categories\n }\n is StorytellerTileType.Clip -> {\n // Handle clip tile tap\n val clipId = tileType.id\n val collectionId = tileType.collectionId\n val categories = tileType.categories\n }\n }\n}\n```\n\nThe SDK snapshots custom metadata from the originating content. `metadata` is `null` when the source omitted it or refreshed content can no longer be resolved, and empty when the source supplied no entries. `title` or `tileIndex` can also be `null` if the tapped item is removed during a refresh; a resolved title can be an empty string when the source supplies no title text. Application-created or data-class-copied `StorytellerTileType` values have no SDK callback context and return `null` for all three fields. The generated subtype `equals`, `hashCode`, and `toString` functions retain their legacy constructor-only behavior and do not include callback context.\n\n> Note: When `theme.lists.enablePlayerOpen` is set to `false`, this callback becomes the primary way to handle tile interactions, as the SDK will not automatically open the player.\n\n#### onPlayerDismissed\n\nThe `onPlayerDismissed()` method is called when user closes Storyteller Player.\n\nExample:\n\n```kotlin\n val StorytellerStoriesRowView = StorytellerStoriesRowView()\n StorytellerStoriesRowView.delegate = myDelegate\n```\n\n#### Error Handling\n\nBy using the callback function `onDataLoadComplete` and the data it provides, you can handle the current state of the `StorytellerStoriesRowView` appropriately in your app.\n\n> Note: `dataCount` is the total number of Stories in the existing `StorytellerStoriesRowView` at any given time\n\nExample:\n\n```kotlin\n ...\n override fun onDataLoadComplete(success: Boolean, error: StorytellerError?, dataCount: Int) {\n if (success) {\n // stories data has been loaded successfully\n // dataCount is the current total number of stories, including newly added/removed data\n } else if (error != null) {\n // an error has occurred, unwrap the error value for more information\n // dataCount is the total number of stories before loading began\n }\n }\n```\n\nAnother example:\n\n```kotlin\n ...\n override fun onDataLoadComplete(success: Boolean, error: StorytellerError?, dataCount: Int) {\n if (error != null && dataCount == 0) {\n // stories have failed to load with error and there is no data to show\n // you may wish to hide the `StorytellerStoriesRowView` instance here\n StorytellerStoriesRowView.visibility = View.GONE\n }\n }\n```\n", "copy_markdown_include_header": false, "base_path": "android", "ai_dir": "ai", "missing_payload_behavior": "empty"}