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.

Story block integrated into the 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.

Lifecycle

FwStoryBlockView has a simple lifecycle:

  1. Created - Instantiate the view in your layout

  2. Init - Call init() once to start playback

  3. Live - View is active and playing videos

  4. 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

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 loading

  • FeedLoadState.FeedLoaded - Feed loaded successfully and ready

  • FeedLoadState.EmptyFeed - Feed loaded but contains no content

  • FeedLoadState.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 resources

  • Do not call init() or destroy() more than once

  • Set listeners before calling init() to receive all callbacks

  • Shopping and livestream chat features only work in fullscreen mode

  • The pause() method does not affect livestreams

  • Use pauseWhenNotVisible=true for better battery and performance

See Also

Last updated