Skip to content

Releases: bambuser/bambuser-commerce-sdk-android

v4.0.0

Choose a tag to compare

@zeyadsadaqa zeyadsadaqa released this 05 Oct 09:50

v4.0.0

Hub-configured layouts come to Android: a single composable, GetAppearanceView, renders a whole collection of shoppable videos as a grid, row, story or floating button using the appearance you configure in Bam Hub, with paging, autoplay, fullscreen and product hydration handled by the SDK.

This is a major release because of one breaking change: videoScaleMode is now nullable and no longer defaults to FIT. Most integrations need no code changes; see the migration note below to be sure.

Upgrade

// gradle/libs.versions.toml
bambuserCommerceSDK = "4.0.0"
implementation("com.bambuser:commerce-sdk:4.0.0")

⚠️ Breaking change

videoScaleMode on BambuserVideoConfiguration is now BambuserVideoScaleMode? and defaults to null (previously FIT).

  • A value you set always wins, exactly as before.
  • When left null, the video follows the scale mode requested by the player's own resize events, falling back to FIT until the first one arrives. This lets a player switch between fit and fill as it moves between its preview and full UI.

Do I need to change anything?

Your code today Action
Passes videoScaleMode = FIT or FILL explicitly Nothing. Behavior is unchanged.
Omits videoScaleMode and wants the player to decide Nothing. This is the new default.
Omits videoScaleMode and relies on always-letterboxed video Pass videoScaleMode = BambuserVideoScaleMode.FIT explicitly.
Reads configuration.videoScaleMode as non-null, e.g. an exhaustive when Add a null branch.

✨ New

Appearance view

BambuserSDK.GetAppearanceView renders a collection of shoppable videos with the appearance configured in Bam Hub. The SDK fetches the collection, picks the layout from the appearance's mode (grid, row, story or fab), applies its sizing, spacing, autoplay and tap behavior, and loads further pages as the user scrolls.

val appearanceState = rememberBambuserAppearanceViewState()

bambuserSdk.GetAppearanceView(
    videoConfiguration = BambuserVideoConfiguration(
        videoType = BambuserCollectionInfo.Playlist(
            orgId = "<orgId>",
            componentId = "<placement id from Bam Hub>",
        ),
        configuration = mapOf(
            "playerConfig" to mapOf(
                "softLimit" to "6",   // appearance override, same keys as the hub
                "currency" to "USD",  // anything else goes to the web player as-is
            ),
            "thumbnail" to mapOf("showPlayButton" to false), // optional
            "preload" to true,                               // optional, the default
        ),
        events = listOf("*"),
        videoScaleMode = BambuserVideoScaleMode.FILL,        // optional, applies to every player
    ),
    pageSize = 15,
    state = appearanceState,
    appearanceDelegate = object : BambuserAppearanceViewDelegate {
        override fun onPlayerSelected(item: BambuserAppearanceItem) { /* ... */ }
    },
    videoPlayerDelegate = myPlayerDelegate,  // per-player events, same delegate as a single video
    modifier = Modifier.fillMaxWidth(),
)

What to render. videoType must be a BambuserCollectionInfo, otherwise the composable throws IllegalArgumentException:

  • Playlist(orgId, componentId) renders a placement with the appearance configured for it in Bam Hub.
  • SKU(orgId, sku) and GroupId(orgId, groupId) render the videos attached to a product or a product group.

Overriding the appearance. configuration["playerConfig"] accepts the keys the hub uses (mode, focusMode, autoplay, cornerRadius, playerWidth, playlistGap, softLimit, ...). Precedence, lowest first: the collection's player config, the default appearance, the app appearance, then your map. Null and blank values are ignored. Entries that are not appearance keys, such as currency, locale or buttons, are forwarded unchanged to every player's web configuration. Any other top-level key in configuration is copied into every player as well.

Every player, one config. events and videoScaleMode apply to every player in the collection, inline and fullscreen. configuration["thumbnail"] tunes every preview thumbnail with the keys a single shoppable player accepts (enabled, showPlayButton, showLoadingIndicator, preview, contentMode). configuration["preload"] (default true) controls whether players boot the web player behind their thumbnail; set it to false on long collections to defer loading until a player is tapped or played.

Observing the collection. rememberBambuserAppearanceViewState() returns a BambuserAppearanceViewState the SDK keeps up to date as Compose state: the resolved mode (BambuserAppearanceMode.GRID, ROW, STORY, FAB or UNKNOWN), the loaded videoIds, and the current pagination. Use it to size or decorate the container per layout.

Collection-level callbacks. BambuserAppearanceViewDelegate has eight methods, all with empty defaults, all on the main thread:

override fun onWillLoadPage(page: Int) {}
override fun onPageLoaded(page: Int, totalPages: Int?) {}
override fun onPageFailedToLoad(page: Int, error: Exception) {}
override fun onPlayerSelected(item: BambuserAppearanceItem) {}
override fun onEnterFullscreen(item: BambuserAppearanceItem) {}
override fun onExitFullscreen(item: BambuserAppearanceItem) {}
override fun onCurrentChanged(item: BambuserAppearanceItem) {}
override fun onDismiss() {}

Item callbacks carry a BambuserAppearanceItem with the video's index, its playerId and its VideoMetadata. Per-player events still arrive through the BambuserVideoPlayerDelegate you pass to the same composable, so product hydration works exactly as it does for a single shoppable video.

Lifecycle. The collection lives as long as the composable is in the composition. Leaving the composition cancels any fetch in flight and releases the players; there is nothing to clean up.

Note on the FAB layout: the floating button behaves and looks different from the other layouts. Create a separate appearance in Bam Hub for it, on its own placement, instead of reusing the one for grid, row or story.

Supporting API

  • BambuserCollectionInfo now implements BambuserVideoType, so a Playlist, SKU or GroupId can be the videoType of a BambuserVideoConfiguration.
  • New public types: BambuserAppearanceViewDelegate, BambuserAppearanceViewState, rememberBambuserAppearanceViewState(), BambuserAppearanceMode, BambuserAppearancePagination and BambuserAppearanceItem.

🐛 Fixed

  • Resize events from the web player that carry no dimensions are now ignored. They are sent while the player switches between its preview and full UI and previously collapsed the video view for a moment, which showed as a flash when a player expanded to fullscreen.

Requirements

Unchanged: minSdk 26, compileSdk 36, Compose BOM 2025.02.00.

Full changelog: v3.7.0...v4.0.0

v3.7.0

Choose a tag to compare

@zeyadsadaqa zeyadsadaqa released this 15 Sep 15:11

[3.7.0] - 2026-09-15

Added

  • The player UI now follows the system font size accessibility setting. The player's root font size (chat, product tiles, and overlays) scales with the user's chosen font size, including the non-linear scaling curve on Android 14+, and updates live when the setting is changed while a player is open. No integration work is required.

fixed

  • Fixed a packaging problem in 3.6.0 to keep supporting 16KB requirement

v3.6.0

Choose a tag to compare

@zeyadsadaqa zeyadsadaqa released this 15 Sep 13:54

What's New

✨ Added

Video scale mode — BambuserVideoConfiguration now accepts a videoScaleMode parameter, for both live and shoppable videos:

  • BambuserVideoScaleMode.FIT (default) — keeps the current behavior: the whole video is visible, letterboxed when the aspect ratios differ.
  • BambuserVideoScaleMode.FILL — the video covers the entire player view, keeping its aspect ratio and cropping the overflow.

Only the video surface is affected; player controls and overlays are unchanged. The parameter is optional and defaults to FIT, so existing integrations require no changes.

val config = BambuserVideoConfiguration(
    // ...
    videoScaleMode = BambuserVideoScaleMode.FILL,
)

Tip: for a full-bleed look, pair FILL with a player view that applies no system bar padding of its own, and hide the system bars in the host activity so they don't cover the player's controls.

🐛 Fixed

The soft keyboard no longer covers the chat input in the player.

v3.5.0

Choose a tag to compare

@zeyadsadaqa zeyadsadaqa released this 19 Aug 11:20

A stability-focused release: players now recover cleanly when opened and closed repeatedly, never hang on a stalled network, and always report either ready or an error. Plus live viewer counts, collections by product group ID, and control over thumbnail scaling.

No breaking changes. Drop-in upgrade from 3.4.x — every new callback ships with a default implementation.

Upgrade
// gradle/libs.versions.toml
bambuserCommerceSDK = "3.5.0"

✨ New

Live viewer counts — two optional callbacks on BambuserVideoPlayerDelegate:

override fun onCurrentViewerCountChanged(playerId: String, viewers: Long) { }
override fun onTotalViewerCountChanged(playerId: String, viewers: Long) { }

Only live broadcasts report meaningful values; counts are pushed by the backend rather than polled, and aren't affected by the events filter. onTotalViewerCountChanged isn't deduplicated per viewer. The native player can also display the count for live shows once enabled in BamHub — no integration work.

BambuserCollectionInfo.GroupId(orgId, groupId) — fetch collections by product item group ID.

contentMode in the shoppable thumbnail config — "scaleToFill" / "scaleAspectFit" / "scaleAspectFill", with a table of behaviors; omit to keep existing behavior.

🐛 Fixed

Black screen after reopening players · reopening the same video · infinite waits on stalled connections · guaranteed ready-or-error within 60s · lost first events · full teardown on close · thumbnails filling the host view · mute control resetting · NetworkOnMainThreadException on collection fetches · coroutine cancellation actually cancelling.

🔧 Changed

Explicit 10s/15s network timeouts on all requests · actionable error messages instead of Exception: null.

v3.4.1

Choose a tag to compare

@zeyadsadaqa zeyadsadaqa released this 28 Jul 12:04

Bambuser Commerce SDK for Android 3.4.1

This is a small housekeeping release with no API or behavior changes.

Removed

  • Leftover launcher icon resources removed from the SDK AAR.

v3.4.0

Choose a tag to compare

@zeyadsadaqa zeyadsadaqa released this 24 Jul 15:38

Bambuser Commerce SDK for Android — v3.4.0

This release focuses on player stability and more accurate commerce tracking.

🐛 Fixes

  • Resolved a race condition in the video player. Player actions triggered immediately after the player was created could previously be lost or applied to a stale player instance. Player actions and events
    are now reliably handled once the player is fully ready, including during rapid re-initialization and recomposition.

v3.3.0

Choose a tag to compare

@zeyadsadaqa zeyadsadaqa released this 13 Jul 11:57

✨ Added

  • getShoppableVideoPlayerCollectionMetadata — a new BambuserSDK API for retrieving collection metadata. It supports fetching either by playlist/page ID or by SKU, and returns a BambuserCollectionMetadata:

data class BambuserCollectionMetadata(
val videoMetadataList: List,
val pagination: Pagination?,
)

data class VideoMetadata(
val videoId: String, // Unique video identifier
val preview: String?, // Preview image URL
val title: String?, // Video title
val length: Int?, // Video length
val hasAudio: Boolean?, // Whether the video has an audio track
)

data class Pagination(
val pageSize: Int?, // Number of videos per page
val total: Int?, // Total number of videos
val totalPages: Int?, // Total number of pages
val page: Int?, // Current page number
)

⚠️ Deprecated

  • getShoppableVideoPlayerCollection — deprecated in favor of getShoppableVideoPlayerCollectionMetadata. The previously returned BambuserCollection (which exposed only a videoIdList) is likewise deprecated in favor of
    BambuserCollectionMetadata, which provides full per-video metadata instead of just IDs. Migrate with:

// Before:
getShoppableVideoPlayerCollection(collectionInfo, pageSize, page)

// After:
getShoppableVideoPlayerCollectionMetadata(collectionInfo, pageSize, page)

v3.2.0

Choose a tag to compare

@zeyadsadaqa zeyadsadaqa released this 29 Jun 13:16

What's New

Added

  • seek(to: Long) — New player action to seek the player to a given position (in seconds). Available on PlayerActions

Usage

// Seek the player to 30 seconds
playerActions.seek(to = 30)

v3.1.0

Choose a tag to compare

@zeyadsadaqa zeyadsadaqa released this 15 Jun 09:29

🐛 Bug Fix: Robust player event parsing

Previously, the SDK could throw a "malformed event" error when a player event delivered its event data in an unexpected shape (for example, a JSON array or a primitive value rather than an object).
This release introduces a custom deserializer for BambuserEventPayload that gracefully handles all event payload shapes:

  • Object → parsed into the event data map as before
  • Array → wrapped under a "list" key
  • Primitive (string/number/boolean) → wrapped under a "value" key
  • null / missing → treated as empty data

3.0.0

Choose a tag to compare

@zeyadsadaqa zeyadsadaqa released this 26 May 15:13

🛠 What's New

  • SDK Target Update: Bumped compileSdk and targetSdk to 36.

⚠️ Breaking Changes

  • Removed playerId: The playerId parameter has been removed from GetLiveView and GetLShoppableVideoView. The SDK now uses the video asset ID internally.
  • Use componentId alongside orgId to get your shoppable video playlists

🗑 Deprecated

  • pageId, containerId, and title fields in BambuserCollectionInfo.Playlist are now deprecated. Use componentId instead.

🐛 Bug Fixes

  • Internal improvements and stability fixes.