StoryBlock
FwStoryBlockView is an inline video player that can be embedded directly into your view hierarchy. It provides a story-like video experience where videos play automatically within your layout.

Overview
FwStoryBlockView allows you to embed a Firework video player directly into your app's view hierarchy without launching a separate activity. It's ideal for creating immersive, story-like video experiences.
Important Limitations:
Heavy resource usage: contains multiple player instances and intensive background tasks
Recommended: 1 instance per screen
Maximum supported: 2 instances on newer devices
Exceeding this limit may cause unexpected behavior
Lifecycle
StoryBlock lifecycle is very simple. The host app needs to create an instance of StoryBlock and initialize it once, then the StoryBlock goes to live mode and works as expected until the host app decides to kill the lifecycle by calling the destroy function. The init function must not be called more than once, as same as destroy function. Any violation of the lifecycle use might lead to unexpected behavior of StoryBlock.
FwStoryBlockView has a simple lifecycle:
Created - Instantiate the view in your layout
Init - Call
init()once to start playbackLive - View is active and playing videos
Destroy - Call
destroy()once when done
Important: Do not call init() or destroy() more than once.

Integration
XML Layout
Add FwStoryBlockView to your layout:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="300dp"
android:layout_height="400dp"
android:layout_gravity="center"
android:layout_marginVertical="16dp">
<com.firework.storyblock.FwStoryBlockView
android:id="@+id/storyBlock"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="9:16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>Initialization
Initialize the StoryBlock with required parameters:
class MainActivity : AppCompatActivity() {
private lateinit var storyBlock: FwStoryBlockView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
storyBlock = findViewById(R.id.storyBlock)
val viewOptions = viewOptions {
baseOptions {
feedResource(FeedResource.Discovery)
}
playerOptions {
playerMode(PlayerMode.FIT_MODE)
}
storyBlockOptions {
enableAutoPlay(true)
showFullScreenIcon(true)
}
}
storyBlock.init(
fragmentManager = supportFragmentManager,
lifecycle = lifecycle,
viewOptions = viewOptions,
pauseWhenNotVisible = true
)
}
override fun onDestroy() {
super.onDestroy()
storyBlock.destroy()
}
}Using Builder Pattern
For Java compatibility or when you prefer builders:
val baseOption = BaseOption.Builder()
.feedResource(FeedResource.Discovery)
.build()
val playerOption = PlayerOption.Builder()
.playerMode(PlayerMode.FIT_MODE)
.build()
val storyBlockOption = StoryBlockOption.Builder()
.enableAutoPlay(true)
.showFullScreenIcon(true)
.build()
val viewOptions = ViewOptions.Builder()
.baseOption(baseOption)
.playerOption(playerOption)
.storyBlockOption(storyBlockOption)
.build()
storyBlock.init(
supportFragmentManager,
lifecycle,
viewOptions,
true // pauseWhenNotVisible
)Configuration
Player Modes
FwStoryBlockView supports two player modes:
FIT_MODE
Scales video with a 9:16 aspect ratio. The StoryBlock container should be placed in a ConstraintLayout with app:layout_constraintDimensionRatio="9:16":
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="300dp"
android:layout_gravity="center">
<com.firework.storyblock.FwStoryBlockView
android:id="@+id/storyBlock"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="9:16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>val viewOptions = viewOptions {
playerOptions {
playerMode(PlayerMode.FIT_MODE)
}
}FULL_BLEED_MODE
Fills the entire container following its width and height:
val viewOptions = viewOptions {
playerOptions {
playerMode(PlayerMode.FULL_BLEED_MODE)
}
}StoryBlock Specific Options
You can configure StoryBlock-specific behavior using StoryBlockOption:
val viewOptions = viewOptions {
storyBlockOptions {
enableAutoPlay(true) // Auto-play videos (default: true)
showFullScreenIcon(true) // Show fullscreen icon (default: true)
enableVerticalSwipe(false) // Enable vertical swipe navigation (default: false)
}
}For detailed configuration options, see StoryBlockOption Configuration.
Pause When Not Visible
The pauseWhenNotVisible parameter controls automatic pause/resume:
storyBlock.init(
fragmentManager = supportFragmentManager,
lifecycle = lifecycle,
viewOptions = viewOptions,
pauseWhenNotVisible = true // Pauses when view is not visible
)When set to true, the StoryBlock automatically:
Pauses (or mutes for livestreams) when the view becomes invisible
Resumes normal playback when the view becomes visible again
Fullscreen and Compact Modes
When the StoryBlock is in the compact mode it displays a limited set of components. For example, shopping and livestream chat components are supported only in fullscreen mode.
FwStoryBlockView can display in two states:
Compact Mode
The default embedded state within your layout. In compact mode:
Limited UI components are displayed
Shopping and livestream chat are not available
Video plays within the container bounds
Fullscreen Mode
Expanded to fill the screen. In fullscreen mode:
All UI components are available
Shopping and livestream chat are supported
Full player controls are shown
Programmatic Fullscreen
Open fullscreen mode programmatically:
storyBlock.openFullscreen()Playback Control
Control video playback programmatically:
Play
storyBlock.play()Pause
storyBlock.pause()Note: The pause() method does not work for livestreams (they will continue playing or switch to mute mode).
Feed Loading State
Monitor the feed loading state:
storyBlock.setFeedLoadListener { state ->
when (state) {
is FeedLoadState.Loading -> {
// Show loading indicator
progressBar.visibility = View.VISIBLE
}
is FeedLoadState.FeedLoaded -> {
// Hide loading indicator and enable interactions
progressBar.visibility = View.GONE
}
is FeedLoadState.EmptyFeed -> {
// Show empty feed message
emptyView.visibility = View.VISIBLE
}
is FeedLoadState.EndOfFeed -> {
// Handle end of feed
}
}
}Feed Load States
FeedLoadState.Loading- Feed is currently loadingFeedLoadState.FeedLoaded- Feed loaded successfully and readyFeedLoadState.EmptyFeed- Feed loaded but contains no contentFeedLoadState.EndOfFeed- End of feed reached
Best Practice: Set the listener before calling init() to receive all state changes.
Error Handling
Set an error listener to handle StoryBlock errors:
storyBlock.setOnErrorListener { error ->
when (error) {
is SdkLevelError.SdkNotInitialized -> {
// SDK not initialized
showError("Please initialize Firework SDK first")
}
is StoryBlockError.InitializationFailed -> {
// StoryBlock initialization failed
showError("Failed to initialize StoryBlock: ${error.message}")
}
is StoryBlockError.DestructionFailed -> {
// StoryBlock destruction failed
showError("Failed to destroy StoryBlock")
}
is NetworkError -> {
// Network-related error
showError("Network error occurred")
retryConnection()
}
is PlayerError -> {
// Video playback error
showError("Playback error occurred")
}
is LivestreamError -> {
// Livestream-specific error
showError("Livestream error occurred")
}
else -> {
// Other error types
showError("An error occurred: ${error::class.simpleName}")
}
}
}Remove Error Listener
storyBlock.removeOnErrorListener(errorListener)Complete Example
class StoryBlockActivity : AppCompatActivity() {
private lateinit var storyBlock: FwStoryBlockView
private lateinit var progressBar: ProgressBar
private lateinit var errorView: TextView
private lateinit var emptyView: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_story_block)
storyBlock = findViewById(R.id.storyBlock)
progressBar = findViewById(R.id.progressBar)
errorView = findViewById(R.id.errorView)
emptyView = findViewById(R.id.emptyView)
setupListeners()
initializeStoryBlock()
}
private fun setupListeners() {
// Feed loading listener
storyBlock.setFeedLoadListener { state ->
handleFeedLoadState(state)
}
// Error listener
storyBlock.setOnErrorListener { error ->
handleError(error)
}
}
private fun initializeStoryBlock() {
val viewOptions = viewOptions {
baseOptions {
feedResource(FeedResource.Discovery)
}
playerOptions {
playerMode(PlayerMode.FIT_MODE)
showShareButton(true)
enablePipMode(false)
}
storyBlockOptions {
enableAutoPlay(true)
showFullScreenIcon(true)
}
}
storyBlock.init(
fragmentManager = supportFragmentManager,
lifecycle = lifecycle,
viewOptions = viewOptions,
pauseWhenNotVisible = true
)
}
private fun handleFeedLoadState(state: FeedLoadState) {
when (state) {
is FeedLoadState.Loading -> {
progressBar.visibility = View.VISIBLE
errorView.visibility = View.GONE
emptyView.visibility = View.GONE
}
is FeedLoadState.FeedLoaded -> {
progressBar.visibility = View.GONE
// StoryBlock is ready for interaction
}
is FeedLoadState.EmptyFeed -> {
progressBar.visibility = View.GONE
emptyView.visibility = View.VISIBLE
}
is FeedLoadState.EndOfFeed -> {
// Handle end of feed if needed
}
}
}
private fun handleError(error: FwError) {
progressBar.visibility = View.GONE
val errorMessage = when (error) {
is SdkLevelError.SdkNotInitialized ->
"SDK not initialized. Please initialize the SDK first."
is NetworkError ->
"Network error. Please check your connection."
else ->
"An error occurred: ${error::class.java.simpleName}"
}
errorView.text = errorMessage
errorView.visibility = View.VISIBLE
}
override fun onDestroy() {
super.onDestroy()
storyBlock.setFeedLoadListener(null)
storyBlock.destroy()
}
}Important Notes
StoryBlock is a heavy component - limit to 1-2 instances per screen
Call
destroy()when the StoryBlock is no longer needed to free resourcesDo not call
init()ordestroy()more than onceSet listeners before calling
init()to receive all callbacksShopping and livestream chat features only work in fullscreen mode
The
pause()method does not affect livestreamsUse
pauseWhenNotVisible=truefor better battery and performance
See Also
StoryBlockOption Configuration - Detailed StoryBlock-specific options
ViewOptions Configuration - Complete configuration options
PlayerOption - Player customization
BaseOption - Feed source configuration
Widgets Overview - All available widgets
Last updated