1"use strict";(globalThis.webpackChunkbambuser_docs||=[]).push([[1586],{62962(e,n,t){t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>d,default:()=>u,frontMatter:()=>i,metadata:()=>r,toc:()=>l});const r=JSON.parse('{"id":"mobileSDK/android/sv-android-product-hydration","title":"Product Hydration","description":"Handle the provide-product-data event in the Android SDK. Extract product refs from the payload and respond with enriched variant, price, and stock data.","source":"@site/shoppable-video/mobileSDK/android/product-hydration.mdx","sourceDirName":"mobileSDK/android","slug":"/mobileSDK/android/mobile-sdk/android/product-hydration","permalink":"/docs/shoppable-video/mobileSDK/android/mobile-sdk/android/product-hydration","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"id":"sv-android-product-hydration","title":"Product Hydration","description":"Handle the provide-product-data event in the Android SDK. Extract product refs from the payload and respond with enriched variant, price, and stock data.","sidebar_label":"Product Hydration","slug":"mobile-sdk/android/product-hydration"},"sidebar":"someSidebars","previous":{"title":"Cart Integration","permalink":"/docs/shoppable-video/mobileSDK/android/mobile-sdk/android/cart-integration"},"next":{"title":"Conversion Tracking","permalink":"/docs/shoppable-video/mobileSDK/android/mobile-sdk/android/tracking"}}');var a=t(74848),o=t(28453);const i={id:"sv-android-product-hydration",title:"Product Hydration",description:"Handle the provide-product-data event in the Android SDK. Extract product refs from the payload and respond with enriched variant, price, and stock data.",sidebar_label:"Product Hydration",slug:"mobile-sdk/android/product-hydration"},d="Product Hydration",s={},l=[{value:"Why Hydrate?",id:"why-hydrate",level:2},{value:"How It Works",id:"how-it-works",level:2},{value:"Event Payload",id:"event-payload",level:2},{value:"Providing Hydrated Data",id:"providing-hydrated-data",level:2},{value:"Async Hydration",id:"async-hydration",level:2},{value:"Full Delegate Example",id:"full-delegate-example",level:2},{value:"Required Configuration",id:"required-configuration",level:2},{value:"Next Steps",id:"next-steps",level:2}];function c(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",ul:"ul",...(0,o.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.header,{children:(0,a.jsx)(n.h1,{id:"product-hydration",children:"Product Hydration"})}),"\n",(0,a.jsxs)(n.p,{children:["Product hydration lets the player display live product data \u2014 variants, prices, stock availability \u2014 sourced from your own catalogue instead of relying solely on the BamHub metadata. When the player needs product information, it fires a ",(0,a.jsx)(n.code,{children:'"provide-product-data"'})," event; your app responds with the enriched data."]}),"\n",(0,a.jsx)(n.h2,{id:"why-hydrate",children:"Why Hydrate?"}),"\n",(0,a.jsx)(n.p,{children:"Without hydration the player shows only the basic product information stored in BamHub at publish time. With hydration you can:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Show real-time prices (including sale prices and personalised promotions)."}),"\n",(0,a.jsx)(n.li,{children:"Reflect live stock availability per variant."}),"\n",(0,a.jsx)(n.li,{children:"Surface variant options (colour, size) directly in the player shelf."}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"how-it-works",children:"How It Works"}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsx)(n.li,{children:"The player loads and encounters products that need data."}),"\n",(0,a.jsxs)(n.li,{children:["The SDK fires ",(0,a.jsx)(n.code,{children:'"provide-product-data"'})," in ",(0,a.jsx)(n.code,{children:"onNewEventReceived"}),"."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.code,{children:"event.data"})," contains a list of product references your app needs to resolve."]}),"\n",(0,a.jsxs)(n.li,{children:["Your app fetches or looks up the products and calls ",(0,a.jsx)(n.code,{children:'viewAction.invoke("updateProductWithData", ...)'})," with the enriched response."]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"event-payload",children:"Event Payload"}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.code,{children:"event.data"})," for ",(0,a.jsx)(n.code,{children:'"provide-product-data"'})," contains a ",(0,a.jsx)(n.code,{children:"productRefs"})," list. Each entry has at minimum a ",(0,a.jsx)(n.code,{children:"sku"})," (or ",(0,a.jsx)(n.code,{children:"productId"}),") field:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-kotlin",metastring:'title="Reading product refs"',children:'override fun onNewEventReceived(\n playerId: String,\n event: BambuserEventPayload,\n viewAction: ViewActions,\n) {\n if (event.event == "provide-product-data") {\n val data = event.data as? Map<*, *> ?: return\n @Suppress("UNCHECKED_CAST")\n val productRefs = data["products"] as? List<Map<String, Any?>> ?: return\n hydrateProducts(productRefs, viewAction)\n }\n}\n'})}),"\n",(0,a.jsx)(n.h2,{id:"providing-hydrated-data",children:"Providing Hydrated Data"}),"\n",(0,a.jsxs)(n.p,{children:["Call ",(0,a.jsx)(n.code,{children:'viewAction.invoke("updateProductWithData", products)'})," with a list of product maps structured to the ",(0,a.jsx)(n.a,{href:"/shoppable-video/shopper-events-tracking/",children:"Bambuser product data schema"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-kotlin",metastring:'title="HydrationHelper.kt"',children:'private fun hydrateProducts(\n productRefs: List<Map<String, Any?>>,\n viewAction: ViewActions,\n) {\n val hydrated = productRefs.mapNotNull { ref ->\n val sku = ref["sku"] as? String ?: return@mapNotNull null\n val catalogueProduct = productRepository.findBySku(sku) ?: return@mapNotNull null\n\n mapOf(\n "sku" to sku,\n "name" to catalogueProduct.name,\n "brandName" to catalogueProduct.brand,\n "currency" to "USD",\n "variations" to catalogueProduct.variants.map { variant ->\n mapOf(\n "id" to variant.id,\n "name" to variant.name,\n "price" to variant.price,\n "inStock" to variant.inStock,\n "imageUrls" to listOf(variant.imageUrl),\n "attributes" to listOf(\n mapOf("type" to "color", "value" to variant.color),\n mapOf("type" to "size", "value" to variant.size),\n ),\n )\n },\n )\n }\n\n viewAction.invoke("updateProductWithData", hydrated)\n}\n'})}),"\n",(0,a.jsx)(n.h2,{id:"async-hydration",children:"Async Hydration"}),"\n",(0,a.jsxs)(n.p,{children:["If your catalogue lookup is asynchronous, launch a coroutine and call ",(0,a.jsx)(n.code,{children:'invoke("updateProductWithData", ...)'})," once the data is ready. The player waits for a response, so respond as quickly as possible to avoid a loading delay for the shopper:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-kotlin",metastring:'title="Async hydration"',children:'private val scope = CoroutineScope(Dispat
1chers.IO + SupervisorJob())\n\noverride fun onNewEventReceived(\n playerId: String,\n event: BambuserEventPayload,\n viewAction: ViewActions,\n) {\n if (event.event != "provide-product-data") return\n\n val data = event.data as? Map<*, *> ?: return\n @Suppress("UNCHECKED_CAST")\n val productRefs = data["products"] as? List<Map<String, Any?>> ?: return\n\n scope.launch {\n val hydrated = productRefs.mapNotNull { ref ->\n val sku = ref["sku"] as? String ?: return@mapNotNull null\n productRepository.fetchBySku(sku)?.toHydrationMap()\n }\n // invoke is safe to call from any thread\n viewAction.invoke("updateProductWithData", hydrated)\n }\n}\n'})}),"\n",(0,a.jsx)(n.h2,{id:"full-delegate-example",children:"Full Delegate Example"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-kotlin",metastring:'title="HydrationDelegate.kt"',children:'val delegate = object : BambuserVideoPlayerDelegate {\n\n override fun onNewEventReceived(\n playerId: String,\n event: BambuserEventPayload,\n viewAction: ViewActions,\n ) {\n when (event.event) {\n "preview-should-expand" -> viewAction.switchScreenMode(ScreenMode.FullScreenMode)\n "close" -> viewAction.switchScreenMode(ScreenMode.PreviewMode)\n\n "provide-product-data" -> {\n val data = event.data as? Map<*, *> ?: return\n @Suppress("UNCHECKED_CAST")\n val refs = data["products"] as? List<Map<String, Any?>> ?: return\n\n val hydrated = refs.mapNotNull { ref ->\n val sku = ref["sku"] as? String ?: return@mapNotNull null\n productRepo.findBySku(sku)?.toHydrationMap()\n }\n viewAction.invoke("updateProductWithData", hydrated)\n }\n }\n }\n\n override fun onErrorOccurred(playerId: String, error: Exception) {\n Log.e("Bambuser", "Error [$playerId]: ${error.message}")\n }\n}\n'})}),"\n",(0,a.jsx)(n.admonition,{type:"warning",children:(0,a.jsxs)(n.p,{children:["If you return an empty list or fail to call ",(0,a.jsx)(n.code,{children:'invoke("updateProductWithData", ...)'}),", the player will continue showing the unhydrated BamHub product data. Always call it \u2014 even with an empty list \u2014 to signal to the player that your app has processed the request."]})}),"\n",(0,a.jsx)(n.h2,{id:"required-configuration",children:"Required Configuration"}),"\n",(0,a.jsxs)(n.p,{children:["Product hydration requires the ",(0,a.jsx)(n.code,{children:"currency"})," key in ",(0,a.jsx)(n.code,{children:"playerConfig"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-kotlin",metastring:'title="Required config for hydration"',children:'"playerConfig" to mapOf(\n "currency" to "USD", // ISO 4217 code matching your prices\n)\n'})}),"\n",(0,a.jsx)(n.h2,{id:"next-steps",children:"Next Steps"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.a,{href:"/docs/shoppable-video/mobileSDK/android/mobile-sdk/android/cart-integration",children:"Cart integration"})," \u2014 handle add-to-cart events after the user selects a hydrated variant."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.a,{href:"/shoppable-video/vod-player-api-reference",children:"Player API Reference"})," \u2014 full product data schema."]}),"\n"]})]})}function u(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(c,{...e})}):c(e)}},28453(e,n,t){t.d(n,{R:()=>i,x:()=>d});var r=t(96540);const a={},o=r.createContext(a);function i(e){const n=r.useContext(o);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function d(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),r.createElement(o.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.