Shoppable Videos (Android)

Shopping feature allows your audience to buy products while watching the videos. With this feature enabled, a user can browse products, scroll through available options ( e.g colours, sizes, etc ) and make a purchase while watching the video. At present, the SDK doesn't provide a payment solution, but it provides the callbacks for your app to manage the shopping cart as well as the checkout process.

UI/UX

Firework SDK provides the user interface to display the product list and handles user interactions to browse products and add an item to the shopping cart. Currently, we don't provide a way to customize UX/UI except for the 'Add to Cart' button. You can customize the look and feel of the Add to cart button by overriding FwShoppingAddToCartButtonStyle. Name of the style and the parent style must be exactly as specified in the example below:

<resources xmlns:tools="http://schemas.android.com/tools"> 
   <style name="FwShoppingAddToCartButtonStyle" parent="FwShoppingAddToCartButtonParentStyle">
        <item name="android:backgroundTint">@color/backgroundColor</item>
        <item name="android:textColor">@color/myTextColor</item>
        <item name="android:textSize">16sp</item>
    </style>
</resources>

To override the button shape properties (rounded corners radius for example) declare the separate style and apply it to shapeAppearanceOverlay style attribute:

<resources>
        <style name="FwShoppingAddToCartButtonStyle" parent="FwShoppingAddToCartButtonParentStyle">
                <item name="shapeAppearanceOverlay">@style/MyAddToCartButtonShapeStyle</item>
        </style>
        
        <style name="MyAddToCartButtonShapeStyle">
                <item name="cornerFamily">rounded</item> <!-- possible values "rounded" and "cut" -->
                <item name="cornerSizeTopLeft">6dp</item>
                <item name="cornerSizeBottomLeft">6dp</item>
                <item name="cornerSizeBottomRight">6dp</item>
                <item name="cornerSizeTopRight">6dp</item>
        </style>
</resources>

There are two available themes for the shopping - ShoppingTheme.DARK and ShoppingTheme.LIGHT. This is configured by setting theme property of ShoppingViewOptions:

FireworkSdk.shopping.setShoppingViewOptions(
    viewOptions = ShoppingViewOptions(theme = ShoppingTheme.LIGHT)
)

Configure default product cards

Product cards are represented as a clickable list of items. Every item represents a product available in the video. Currently, the following elements of the product cards are configurable:

  • Product card Radius. Defines rounded corners radius of the product card.

  • Label for the product cards CTA button. Currently, possible values are: "Shop now" and "Buy now"

  • Product card CTA button visibility. Hide or show the "Shop now" button.

  • Product card click action

Product card Radius

The default value for the product card radius is 4dp in pixels. Use the following code to change the radius of the cards.

FireworkSdk.shopping.setShoppingViewOptions(
    ShoppingViewOptions(
        productCardsOptions = ProductCardsOptions.Default(
            cornerRadius = resources.getDimensionPixelSize(R.dimen.corner_radius)
        ),
    ),
)

Product cards CTA button label The default value for the product card CTA (1 on the picture below) label is "Shop now".

Use the following code to change the label to "Buy now":

FireworkSdk.shopping.setShoppingViewOptions(
    ShoppingViewOptions(
        productCardsOptions = ProductCardsOptions.Default(
            ctaButtonText = ProductCardsOptions.Text.BUY_NOW
            // ctaButtonText = ProductCardsOptions.Text.SHOP_NOW
        ),
    ),
)

Product card CTA button visibility

The default value for the product CTA button visibility is true. Use the following code to change the visibility of the product card CTA button.

FireworkSdk.shopping.setShoppingViewOptions(
    ShoppingViewOptions(
        productCardsOptions = ProductCardsOptions.Default(
            isCtaVisible = true
            // isCtaVisible = false
        ),
    ),
)

Product card click action

By default when user clicks the product card it opens shopping PDP page. This behaviour can be changed by setting custom click listener:

 FireworkSdk.shopping.setOnProductCardClickListener { productId, unitId, productWebUrl, videoInfo ->
    // Perform custom navigation here
    // Return true to indicate that the navigation is handled by the host app
    return true
 }

Configure custom product cards

FireworkSdk.shopping.setShoppingViewOptions(
    ShoppingViewOptions(
        theme = getProductCardsTheme(),
        productCardsOptions = ProductCardsOptions.Custom(
            widthPx = width,
            heightPx = height,
            spaceBetweenItems = spaceBetweenItems,
            cardViewStartEndPadding = padding,
            customViewProvider = { MyCustomProductCard(this) },
        ),
    ),
)

** Provide a view for the custom product card ** To pass a view for the custom product card, the host app should provide a customViewProvider parameter. This parameter is a factory function with the following signature:

val customViewProvider: () -> FwProductCardView

The function returns implementation of FwProductCardView. FwProductCardView is an abstract class with a single abstract method that gets called when the card is inflated by FireworkSDK.

    abstract fun bind(product: ProductCardDetails, videoInfo: VideoInfo)

ProductCardDetails and VideoInfo contain information about the product and the video related to the product card. Here is an example of FwProductCardView implementation (initialization of the fields is omitted):

class MyCustomProductCard @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyle: Int = 0,
) : FwProductCardView(context, attrs, defStyle) {
    
    init {
        inflate(context, R.layout.custom_product_card_item, this)
    }

    override fun bind(product: ProductCardDetails, videoInfo: VideoInfo) {
        title.text = product.productTitle
        price.text = "${product.price?.amount ?: 0.0} ${product.price?.currencyCode}"
        Glide.with(image.context)
            .load(product.productImageUrl)
            .placeholder(R.drawable.ic_product_placeholder)
            .error(R.drawable.ic_product_placeholder)
            .into(image)
    }
}

** Width and height of the custom product card ** Width and height of the custom product card should be specified in pixels. The FireworkSDK can limit the size of the product card if it is not respecting the safe zone. Details of the Safe Zone Specifications for Firework Videos can be found here - https://help.firework.com/safe-zone-specifications-for-firework-videos

** Clickable elements on the custom product card ** FireworkSDK do not allow to have clickable elements on the custom product card. If the custom product card has clickable elements, the click will be intercepted by the FireworkSDK and the default behaviour will be triggered.

Shopping "Call to Action" (CTA) button

Shopping CTA button click listener

OnCtaButtonClickListener is called when the user clicks the shopping CTA button (3 on the picture below).

Here is an example of how to use this interface:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        FireworkSdk.shopping.setOnCtaButtonClicked { productId, unitId, productWebUrl, videoInfo ->
           // Handle CTA click here
        }
     }

    override fun onDestroy() {
        // Remove OnCtaButtonClickListener from the SDK
        FireworkSdk.shopping.setOnCtaButtonClicked(null)
        super.onDestroy()
    }
}

In case the host app needs to do long-running operations after the shopping CTA button is clicked, Firework SDK should be notified about the status of the operation by calling:

fun setCtaButtonStatus(status: CtaButtonStatus)
FireworkSdk.shopping.setOnCtaButtonClickedListener { productId, unitId, productWebUrl, videoInfo ->
    FireworkSdk.shopping.setCtaButtonStatus(CtaButtonStatus.Loading)
        val result = api.doLongOpeartion()
        if(result.isSuccess) {
            FireworkSdk.shopping.setCtaButtonStatus(CtaButtonStatus.Success)
        } else {
            FireworkSdk.shopping.setCtaButtonStatus(CtaButtonStatus.Error)
        }
    }
}
  • CtaButtonStatus**.Loading:** to indicate that the CTA operation is in progress.

  • CtaButtonStatus**.Error:** to indicate that the CTA operation failed.

  • CtaButtonStatus**.Success:** to indicate that the CTA operation has proceeded successfully.

If the host app does not provide status to the CTA button for 10 seconds, SDK considers the action failed. This

To remove the listener:

FireworkSdk.shopping.setOnCtaButtonClickedListener(null)

Change shopping CTA button text

Currently, Firework SDK provides two labels to the shopping CTA button: Add to cart and Shop now. The Add to cart label should be used when shopping is configured to use a shopping cart. The Shop now label should be used when shopping is configured to work without a cart. In this case, after clicking the shopping CTA button, users are redirected directly to the product page. To change the label, the following code is used:

FireworkSdk.shopping.setShoppingViewOptions(
    ShoppingViewOptions(
        ProductDetailsOptions(
                shoppingCtaButtonOptions = ShoppingCtaButtonOptions(
                        text = ShoppingCtaButtonOptions.Text.SHOP_NOW
                     // text = ShoppingCtaButtonOptions.Text.ADD_TO_CART
                ),
        ),
     )
 )

Shopping cart

Firework SDK does not manage the shopping cart flow. As the host application, it is up to you to manage and maintain the shopping cart flow. Firework SDK provides callbacks when the shopping CTA button is clicked or when the user wants to view the shopping cart.

Customise shopping cart behaviour

You can define the behaviour of the shopping cart by using:

fun setShoppingCartBehaviour(behaviour: CartBehaviour)
  • CartBehaviour.NoCart: The shopping cart icon is not shown on the UI. This is the default cart behaviour if setShoppingCartBehaviour was not called by the host app. If applied shopping cart icon will not be shown.

  • CartBehaviour.Callback: When the shopping cart icon is clicked OnCartActionListener.onCartClicked callback is triggered. This is useful when the host app wants to open the checkout screen as a separate activity.

  • CartBehaviour.Embedded: When the shopping cart is clicked, the fragment generated using EmbeddedCartFactory will be shown. You must set EmbeddedCartFactory by calling setEmbeddedCartFactory function when using this cart behaviour. Otherwise IllegalStateException is thrown when the shopping cart icon is clicked.

Example of customise shopping cart:

class MainActivity : AppCompatActivity() {

    // Declare EmbeddedCartFactory
    private val embeddedCartFactory = object : EmbeddedCartFactory {
        override fun getInstance(): Fragment {
            // Host app fragmnet which will be shown when shopping cart is clicked
            return CustomShoppingCartFragment()
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Set EmbeddedCartFactory
        FireworkSdk.shopping.setEmbeddedCartFactory(checkoutFragmentFactory)
        // Set Shopping.CartBehaviour.Embedded
        FireworkSdk.shopping.setShoppingCartBehaviour(Shopping.CartBehaviour.Embedded(title = "Custom cart"))
    }
}

Shopping cart click listener

OnCartActionListener is called when the shopping cart icon (1 on the picture below) is clicked.

interface OnCartActionListener {
        fun onCartClicked(videoInfo: VideoInfo)
}
FireworkSdk.shopping.setShoppingCartBehaviour(Shopping.CartBehaviour.Callback)

With other Cart behaviours like CartBehaviour.Embedded or CartBehaviour.NoCart the callback will not be triggered.

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Set CartBehaviour.Callback
        FireworkSdk.shopping.setShoppingCartBehaviour(Shopping.CartBehaviour.Callback)
        FireworkSdk.shopping.setOnCartClickListener {
           // Handle click on shopping cart
        }
    }

    override fun onDestroy() {
        // Remove OnCartClickListener from the SDK
        FireworkSdk.shopping.setOnCartClickListener(null)
        super.onDestroy()
    }
}

OnProductLinkClickListener is called when users click the shopping PDP button (2 on the picture below).

fun interface OnProductLinkClickListener {
    fun onProductLinkClick(productId: String, unitId: String, productWebUrl: String?, videoInfo: VideoInfo): Boolean
}

The host app has an opportunity to handle the click by itself or delegate it to the Firework SDK. Return true in onProductLinkClick() to handle PDP flow after click action, on the contrary, return false in onProductLinkClick() to let Firework SDK handles the open product page flow.

Here is an example of how to use this interface:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        FireworkSdk.shopping.setOnProductLinkClickListener { productId, unitId, productWebUrl, videoInfo ->
            // Handle click on pdp link button
            return true
        }
    }

    override fun onDestroy() {
        // Remove OnProductLinkClickListener from the SDK
        FireworkSdk.shopping.setOnProductLinkClickListener(null)
        super.onDestroy()
    }
}

Change PDP button visibility by setting ShoppingViewOptions, see the example below:

FireworkSdk.shopping.setShoppingViewOptions(
        ShoppingViewOptions(
                ProductDetailsOptions(
                    linkButtonOptions = LinkButtonOptions(isVisible = false),
                )
        )
 )

Product hydration

Please reference to Product Hydration

Errors

To start receiving shopping error events, host app should implement the OnShoppingErrorListener interface.

/**
* Implement the interface to receive a callback when a shopping error occurs.
*/
interface OnShoppingErrorListener {
    fun onShoppingError(error: ShoppingError)
}

Firework SDK provides the following functions to add and remove OnProductActionListener:

   /**
     * Registers callback to be invoked when shopping-related errors occur.
     * @param listener – The callback that will run
     */
    fun setOnShoppingErrorListener(listener: OnShoppingErrorListener?)

To remove the listener call:

Firework.shopping.setOnShoppingErrorListener(null)
/**
 * All possible errors that can occur with shopping
 */
sealed interface ShoppingError {
    sealed interface AddToCartError : ShoppingError {
        object NullProductId : AddToCartError
        object NullProductUnitId : AddToCartError
        object NullCartActionListener : AddToCartError
        object Timeout : AddToCartError
    }

    sealed interface ShowProductInfoError : ShoppingError {
        object FailedToLaunchUrl : ShowProductInfoError
        object NullProductId : ShowProductInfoError
        object NullUnitId : ShowProductInfoError
        object NullProductWebUrl : ShowProductInfoError
    }
}

Dismiss shopping UI

To dismiss shopping UI programmatically call:

FireworkSdk.shopping.dismiss()

Open the shopping cart programmatically

To open a shopping cart programmatically:

FireworkSdk.shopping.openShoppingCart()

Configure 'Shop now' functionality

Some apps do not want to navigate the user to the shopping cart and instead prefer to open the product page right after the user clicked the shopping CTA button. Here is the example of the setup where after clicking on the CTA product page is opened:\

private fun setupCtaModeShopping() {
        with(FireworkSdk.shopping) {
            setShoppingCartBehaviour(Shopping.CartBehaviour.NoCart) // Hides the cart button in top right corner
            setShoppingViewOptions(
                ShoppingViewOptions(
                    ProductDetailsOptions(
                        linkButtonOptions = LinkButtonOptions(false), // hides pdp link button
                        shoppingCtaButtonOptions = ShoppingCtaButtonOptions(text = ShoppingCtaButtonOptions.Text.SHOP_NOW),
                    ),
                ),
            )
            setOnCtaButtonClicked { productId, unitId, productWebUrl, videoInfo ->
                openWebUrlInBrowser(productWebUrl)
                FireworkSdk.enterPip() // move player to PIP when another screen is opened
            }
        }
    }

Purchase tracking

The host app can record a purchase which will help get a full picture of the user journey flow. In order to do this, call FireworkSDK.trackPurchase whenever the purchase happens.

FireworkSdk.shopping.trackPurchase(
    orderId = unit.id ?: "",
    value = unit.price.amount,
    currencyCode = unit.price.currencyCode.name,
    countryCode = Locale.getDefault().country.ifEmpty { "County not available" },
    additionalInfo = mutableMapOf(
        "additionalKey1" to "additionalValue1",
        "additionalKey2" to "additionalValue2",
        "additionalKey3" to "additionalValue3",
    ),
)

Last updated