Skip to content

Implementing Storyteller Delegate Callbacks#

Table of Contents#

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.

Showcase examples#

StorytellerDelegate#

To use global StorytellerDelegate, implement the StorytellerDelegate interface by overriding the required methods and set it in Storyteller object.

Example:

    Storyteller.storytellerDelegate = myCustomStorytellerDelegate

onUserActivityOccured#

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.

getAd#

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.

userNavigatedToApp#

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.

Analytics#

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:

    ...
    override fun onUserActivityOccurred(type: StorytellerUserActivity.EventType, data: UserActivityData) {
        if (type == StorytellerUserActivity.EventType.OPENED_STORY) {
            // Retrieve the story id value
            val openStoryId = data.storyId
            // Retrieve the story title value
            val openStoryTitle = 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.

Client Ads#

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:

    ...
    override fun getAd(adRequestInfo: StorytellerAdRequestInfo, onComplete: (StorytellerAd) -> Unit = {}, onError: () -> Unit) {
        // Action to get some ads
        val ad = getMyAd()
        // Convert the ad to a StorytellerAd
        val storytellerAd = convertToStorytellrAd(ad)
        // Provide the ad to the SDK
        onComplete(ad)
    }

For a detailed discussion of all the relevant considerations, please see the dedicated Ads page.

configureWebView#

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.

Parameters#

  • view: WebView - The WebView instance to be configured.
  • url: String? - (Optional) The URL string associated with the WebView page that is being loaded.
  • favicon: Bitmap? - (Optional) The favicon Bitmap to be displayed for the WebView page.

Example Usage#

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:

override fun configureWebView(
  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)
}

Followable Categories#

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

customScreenForCategory#

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:

  override fun customScreenForCategory(): @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 screen
        true
      } else {
        // return false if you want to show the default followable category screen
        false
      }
    }

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.

bottomSheetScreen#

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.

Example:

    override fun bottomSheetScreen(
      urlProvider: () -> String,
      onDismiss: () -> Unit
    ): @Composable (() -> Unit) {
      return {
        DemoBottomSheet(urlProvider, onDismiss)
      }
    }

StorytellerListViewDelegate#

A StorytellerListViewDelegate has methods for managing StorytellerStoriesRowView and StorytellerStoriesGridView events.

How to Use#

To use StorytellerListViewDelegate, implement the StorytellerListViewDelegate interface and override the required methods:

onDataLoadStarted#

The onDataLoadStarted() method is called when the network request to load data for all Stories has started.

onDataLoadComplete#

The onDataLoadComplete(success: Boolean, error: Error?, dataCount: Int) method is called when the data loading network request is complete.

Property Description
success This confirms whether or not the request was successful
error The HTTP error if the request was not successful
dataCount The number of Stories loaded

onTileTapped#

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.

Property Description
tileType A sealed class that contains tile information. Can be either StorytellerTileType.Story (with storyId and categories) or StorytellerTileType.Clip (with clipId, collectionId, and categories). Categories are List<StorytellerCategory>.

Example:

override fun onTileTapped(tileType: StorytellerTileType) {
    when (tileType) {
        is StorytellerTileType.Story -> {
            // Handle story tile tap
            val storyId = tileType.id
            val categories = tileType.categories
        }
        is StorytellerTileType.Clip -> {
            // Handle clip tile tap
            val clipId = tileType.id
            val collectionId = tileType.collectionId
            val categories = tileType.categories
        }
    }
}

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.

onPlayerDismissed#

The onPlayerDismissed() method is called when user closes Storyteller Player.

Example:

    val StorytellerStoriesRowView = StorytellerStoriesRowView()
    StorytellerStoriesRowView.delegate = myDelegate

Error Handling#

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:

    ...
    override fun onDataLoadComplete(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
        } else if (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:

    ...
    override fun onDataLoadComplete(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 here
            StorytellerStoriesRowView.visibility = View.GONE
        }
    }