# Widget - 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.

<figure><img src="https://688917408-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MLoGG8m6bokS9YTmS7m%2Fuploads%2Fgit-blob-7142104843ec41e98c6c89626afd6b97b4c5d03e%2Fphoto_2023-03-23%2012.08.36.jpeg?alt=media" alt=""><figcaption><p>Story block integrated into the layout</p></figcaption></figure>

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

{% hint style="danger" %}
**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
  {% endhint %}

## Lifecycle

{% hint style="danger" %}
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.
{% endhint %}

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

<figure><img src="https://688917408-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MLoGG8m6bokS9YTmS7m%2Fuploads%2Fgit-blob-01ac5d46700c8ebb4f649f59881adaba91678fac%2Fimage%20(29).png?alt=media" alt=""><figcaption></figcaption></figure>

## Integration

### XML Layout

Add `FwStoryBlockView` to your layout:

```xml
<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:

```kotlin
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:

```kotlin
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"`:

```xml
<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>
```

```kotlin
val viewOptions = viewOptions {
    playerOptions {
        playerMode(PlayerMode.FIT_MODE)
    }
}
```

#### FULL\_BLEED\_MODE

Fills the entire container following its width and height:

```kotlin
val viewOptions = viewOptions {
    playerOptions {
        playerMode(PlayerMode.FULL_BLEED_MODE)
    }
}
```

### StoryBlock Specific Options

You can configure StoryBlock-specific behavior using `StoryBlockOption`:

```kotlin
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](https://docs.firework.com/firework-for-developers/android-sdk/integration-guide/configuration/story-block-options).

### Pause When Not Visible

The `pauseWhenNotVisible` parameter controls automatic pause/resume:

```kotlin
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

{% hint style="danger" %}
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.
{% endhint %}

`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:

```kotlin
storyBlock.openFullscreen()
```

## Playback Control

Control video playback programmatically:

### Play

```kotlin
storyBlock.play()
```

### Pause

```kotlin
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:

```kotlin
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:

```kotlin
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

```kotlin
storyBlock.removeOnErrorListener(errorListener)
```

## Complete Example

```kotlin
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

* [StoryBlockOption Configuration](https://docs.firework.com/firework-for-developers/android-sdk/integration-guide/configuration/story-block-options) - Detailed StoryBlock-specific options
* [ViewOptions Configuration](https://docs.firework.com/firework-for-developers/android-sdk/integration-guide/configuration) - Complete configuration options
* [PlayerOption](https://docs.firework.com/firework-for-developers/android-sdk/integration-guide/configuration/player-options) - Player customization
* [BaseOption](https://docs.firework.com/firework-for-developers/android-sdk/integration-guide/configuration/base-options) - Feed source configuration
* [Widgets Overview](https://docs.firework.com/firework-for-developers/android-sdk/integration-guide/widgets) - All available widgets
