# Video-Like Replay

{% hint style="danger" %}

#### Experimental API - PII Warning

Video-like Session Replay APIs require `@OptIn(ExperimentalVideoLikeReplay::class)`.

This feature may capture sensitive UI unmasked in some cases. **USE AT YOUR OWN DISCRETION TO PREVENT PII LEAKAGE.** Opt in only after validating masking/privacy in your app.
{% endhint %}

{% hint style="warning" %}

#### Min Required SDK Version

Video-like Session Replay is supported starting Android SDK version **19.1.0**
{% endhint %}

## Overview

Video-like Session Replay transforms your session recordings from simple screen-by-screen captures into smooth, video-like playback. This feature gives you complete visibility into user behavior by:

* **Capturing more frequent screenshots** - See every interaction, not just screen transitions
* **Providing configurable quality profiles** - Balance visual fidelity with storage efficiency
* **Supporting multiple capture modes** - Choose the right approach for your debugging needs

***

## Quick Start

Add these APIs before initializing the SDK for the best experience:

{% tabs %}
{% tab title="Kotlin" %}
{% code overflow="wrap" %}

```kotlin
import ai.luciq.library.Luciq
import ai.luciq.library.LuciqInvocationEvent
import ai.luciq.library.sessionreplay.SessionReplay
import ai.luciq.library.sessionreplay.CapturingMode
import ai.luciq.library.sessionreplay.ScreenshotQuality
import ai.luciq.library.sessionreplay.ExperimentalVideoLikeReplay

// Configure video-like replay
@OptIn(ExperimentalVideoLikeReplay::class)
fun configureVideoLikeReplay() {
    SessionReplay.setCapturingMode(CapturingMode.FREQUENCY)
    SessionReplay.setScreenshotCaptureInterval(1000) // 1 screenshot per second
}

// Screenshot quality does NOT require @OptIn
SessionReplay.setScreenshotQuality(ScreenshotQuality.NORMAL)

// Initialize SDK
Luciq.Builder(application, "YOUR_APP_TOKEN")
    .setInvocationEvents(LuciqInvocationEvent.SHAKE)
    .build()
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code overflow="wrap" %}

```java
import ai.luciq.library.Luciq;
import ai.luciq.library.LuciqInvocationEvent;
import ai.luciq.library.sessionreplay.SessionReplay;
import ai.luciq.library.sessionreplay.CapturingMode;
import ai.luciq.library.sessionreplay.ScreenshotQuality;

// Configure video-like replay (experimental APIs)
SessionReplay.setCapturingMode(CapturingMode.FREQUENCY);
SessionReplay.setScreenshotCaptureInterval(1000); // 1 screenshot per second

// Screenshot quality does NOT require @OptIn
SessionReplay.setScreenshotQuality(ScreenshotQuality.NORMAL);

// Initialize SDK
new Luciq.Builder(application, "YOUR_APP_TOKEN")
    .setInvocationEvents(LuciqInvocationEvent.SHAKE)
    .build();
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

## Capturing Modes

Control **when** screenshots are captured using the `setCapturingMode` API.

{% hint style="info" %}
`setCapturingMode()` requires `@OptIn(ExperimentalVideoLikeReplay::class)` when using `INTERACTIONS` or `FREQUENCY` modes.
{% endhint %}

### Navigation Mode (Default)

Captures screenshots only when users navigate between screens. This is the default behavior and provides the lowest overhead.

**Best for:** Apps where screen transitions are the primary user flow

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
// Default mode - no @OptIn needed for NAVIGATION
SessionReplay.setCapturingMode(CapturingMode.NAVIGATION)
```

{% endtab %}

{% tab title="Java" %}

```java
// Default mode
SessionReplay.setCapturingMode(CapturingMode.NAVIGATION);
```

{% endtab %}
{% endtabs %}

***

### Interactions Mode

Captures screenshots on screen navigation **and** user interactions. Includes debouncing to prevent excessive captures.

**Best for:** Debugging user interaction issues, understanding how users interact with complex screens

#### Supported Interactions

| **Legacy Views (XML)** | **Jetpack Compose** |
| ---------------------- | ------------------- |
| Tap                    | Tap                 |
| Double Tap             | Double Tap          |
| Long Press             | Long Press          |
| Swipe                  | Swipe               |
| Pinch                  | Pinch               |
| Scroll                 | Scroll              |

{% tabs %}
{% tab title="Kotlin" %}
{% code overflow="wrap" %}

```kotlin
@OptIn(ExperimentalVideoLikeReplay::class)
fun enableInteractionsMode() {
    SessionReplay.setCapturingMode(CapturingMode.INTERACTIONS)
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}

```java
SessionReplay.setCapturingMode(CapturingMode.INTERACTIONS);
```

{% endtab %}
{% endtabs %}

***

### Frequency Mode

Captures screenshots at a fixed time interval for true video-like playback. Also captures on screen navigation.

**Best for:** Full video-like replay experience, debugging visual issues, understanding complete user journeys

{% tabs %}
{% tab title="Kotlin" %}
{% code overflow="wrap" %}

```kotlin
@OptIn(ExperimentalVideoLikeReplay::class)
fun enableFrequencyMode() {
    SessionReplay.setCapturingMode(CapturingMode.FREQUENCY)
    SessionReplay.setScreenshotCaptureInterval(1000) // Capture every 1000ms (1 FPS)
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code overflow="wrap" %}

```java
SessionReplay.setCapturingMode(CapturingMode.FREQUENCY);
SessionReplay.setScreenshotCaptureInterval(1000); // Capture every 1000ms (1 FPS)
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="warning" %}
On low-end Android devices, Navigation mode is automatically enforced regardless of the configured mode. See [Low-End Device Handling](https://docs.luciq.ai/android/set-up-luciq-for-android/set-up-session-replay/video-like-replay#low-end-device-handling) for details.
{% endhint %}

***

## Screenshot Capture Interval

When using **Frequency mode**, configure how often screenshots are captured.

{% hint style="info" %}
`setScreenshotCaptureInterval()` requires `@OptIn(ExperimentalVideoLikeReplay::class)`.
{% endhint %}

| **Parameter** | **Description**                       |
| ------------- | ------------------------------------- |
| `intervalMs`  | Time between captures in milliseconds |
| **Default**   | 1000ms (1 screenshot per second)      |
| **Minimum**   | 500ms                                 |

{% tabs %}
{% tab title="Kotlin" %}
{% code overflow="wrap" %}

```kotlin
@OptIn(ExperimentalVideoLikeReplay::class)
fun configureInterval() {
    // Capture every 500ms (2 FPS)
    SessionReplay.setScreenshotCaptureInterval(500)

    // Capture every 2 seconds
    SessionReplay.setScreenshotCaptureInterval(2000)
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code overflow="wrap" %}

```java
// Capture every 500ms (2 FPS)
SessionReplay.setScreenshotCaptureInterval(500);

// Capture every 2 seconds
SessionReplay.setScreenshotCaptureInterval(2000);
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
Values below 500ms will be automatically set to 500ms (the minimum allowed). A warning will be logged to help you identify the issue.
{% endhint %}

### Timer Reset Behavior

The capture timer resets when:

* A manual screenshot is captured via the SDK API
* Screen navigation occurs

This ensures you always capture important moments regardless of the timer state.

***

## Screenshot Quality

Control the **visual quality** of captured screenshots. Higher quality provides better visuals but uses more storage.

{% hint style="success" %}
`setScreenshotQuality()` does **NOT** require `@OptIn` - this API is stable and not experimental.
{% endhint %}

### Quality Profiles

| **Profile**          | **Compression**                | **Use Case**                                  |
| -------------------- | ------------------------------ | --------------------------------------------- |
| **HIGH**             | 50% quality (WebP)             | Detailed debugging, visual regression testing |
| **NORMAL** (Default) | 25% quality (WebP)             | Balanced quality and storage                  |
| **GREYSCALE**        | 25% quality + grayscale (WebP) | Maximum storage efficiency, text-heavy apps   |

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
SessionReplay.setScreenshotQuality(ScreenshotQuality.HIGH)      // Best visual quality
SessionReplay.setScreenshotQuality(ScreenshotQuality.NORMAL)    // Balanced (default)
SessionReplay.setScreenshotQuality(ScreenshotQuality.GREYSCALE) // Smallest file size
```

{% endtab %}

{% tab title="Java" %}

```java
SessionReplay.setScreenshotQuality(ScreenshotQuality.HIGH);      // Best visual quality
SessionReplay.setScreenshotQuality(ScreenshotQuality.NORMAL);    // Balanced (default)
SessionReplay.setScreenshotQuality(ScreenshotQuality.GREYSCALE); // Smallest file size
```

{% endtab %}
{% endtabs %}

### Estimated Screenshots per Session

Based on the default 1MB session screenshot limit:

| **Quality Profile** | **Approx. Screenshots per Session** |
| ------------------- | ----------------------------------- |
| HIGH                | \~62 screenshots                    |
| NORMAL              | \~104 screenshots                   |
| GREYSCALE           | \~130 screenshots                   |

{% hint style="info" %}
For video-like replay at 1 FPS with NORMAL quality, you can capture approximately 1-2 minutes of continuous session activity.
{% endhint %}

***

## Low-End Device Handling

To ensure optimal user experience, the SDK automatically detects low-end Android devices and **enforces Navigation mode** regardless of the configured capturing mode.

### What This Means

* If you configure `CapturingMode.FREQUENCY` or `CapturingMode.INTERACTIONS`, low-end devices will still use `CapturingMode.NAVIGATION`
* Screenshot quality settings are still respected
* This behavior is automatic and requires no additional configuration

### Why This Matters

Low-end devices have limited:

* CPU resources for frequent screenshot capture
* Memory for storing compressed images
* Storage for session data

By enforcing Navigation mode, the SDK prevents negative impact on app performance and user experience.

For more details on how device classification affects all data types (not just Session Replay), see [Constrained Devices](https://docs.luciq.ai/product-guides-and-integrations/product-guides/session-replay#constrained-devices).

***

## Configuration Timing

### Recommended: Before SDK Initialization

For best results, configure Video-like Session Replay **before** calling `Luciq.Builder().build()`:

{% tabs %}
{% tab title="Kotlin" %}
{% code overflow="wrap" %}

```kotlin
@OptIn(ExperimentalVideoLikeReplay::class)
fun initializeLuciq() {
    // Configure video-like replay first
    SessionReplay.setCapturingMode(CapturingMode.FREQUENCY)
    SessionReplay.setScreenshotCaptureInterval(1000)
    SessionReplay.setScreenshotQuality(ScreenshotQuality.NORMAL)

    // Then initialize SDK
    Luciq.Builder(application, "YOUR_APP_TOKEN")
        .setInvocationEvents(LuciqInvocationEvent.SHAKE)
        .build()
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code overflow="wrap" %}

```java
// Configure video-like replay first
SessionReplay.setCapturingMode(CapturingMode.FREQUENCY);
SessionReplay.setScreenshotCaptureInterval(1000);
SessionReplay.setScreenshotQuality(ScreenshotQuality.NORMAL);

// Then initialize SDK
new Luciq.Builder(application, "YOUR_APP_TOKEN")
    .setInvocationEvents(LuciqInvocationEvent.SHAKE)
    .build();
```

{% endcode %}
{% endtab %}
{% endtabs %}

### Runtime Configuration

You can also change settings during the session. Changes take effect as follows:

| **Setting**        | **When Applied**                 |
| ------------------ | -------------------------------- |
| Capturing Mode     | Current session                  |
| Screenshot Quality | Next screenshot                  |
| Capture Interval   | After current interval completes |

{% tabs %}
{% tab title="Kotlin" %}
{% code overflow="wrap" %}

```kotlin
@OptIn(ExperimentalVideoLikeReplay::class)
fun changeSettingsAtRuntime() {
    // Change quality mid-session (not experimental)
    SessionReplay.setScreenshotQuality(ScreenshotQuality.HIGH)

    // Switch capturing mode (applies immediately)
    SessionReplay.setCapturingMode(CapturingMode.FREQUENCY)
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code overflow="wrap" %}

```java
// Change quality mid-session (not experimental)
SessionReplay.setScreenshotQuality(ScreenshotQuality.HIGH);

// Switch capturing mode (applies immediately)
SessionReplay.setCapturingMode(CapturingMode.FREQUENCY);
```

{% endcode %}
{% endtab %}
{% endtabs %}

***

## Privacy & Masking

Video-like Session Replay respects all existing privacy configurations:

* **Auto-masking** continues to work across all capturing modes
* **Private views** are masked in all captured screenshots
* **Secure flag** on windows is respected - content is not captured (can be overridden for debugging via\
  `ignoreFlagSecure`)

### Marking Views as Private

{% tabs %}
{% tab title="Kotlin" %}
{% code overflow="wrap" %}

```kotlin
// Legacy Views (XML)
Luciq.addPrivateViews(view) // pass legacy view

// Jetpack Compose
Box(
    modifier = Modifier.luciqPrivate()
) {
    Text("Sensitive Data")
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}

```java
// Legacy Views (XML)
LuciqPrivateView.setPrivateView(sensitiveView, true);
```

{% endtab %}
{% endtabs %}

### Using Secure Flag

Views in windows with the secure flag are automatically excluded from Session Replay:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
window.setFlags(
    WindowManager.LayoutParams.FLAG_SECURE,
    WindowManager.LayoutParams.FLAG_SECURE
)
```

{% endtab %}

{% tab title="Java" %}

```java
getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_SECURE,
    WindowManager.LayoutParams.FLAG_SECURE
);
```

{% endtab %}
{% endtabs %}

### Overriding Secure Flag

During development and testing, you may want Luciq to capture screenshots on screens marked with `FLAG_SECURE` to help\
with debugging. You can override this behavior during SDK initialization using `ignoreFlagSecure`:

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
Luciq.Builder(this, "APP_TOKEN")
    .ignoreFlagSecure(true)
    .build()
```

{% endtab %}

{% tab title="Java" %}

```java
new Luciq.Builder(this, "APP_TOKEN")
    .ignoreFlagSecure(true)
    .build();
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
This option is intended for debug and staging builds. Avoid enabling it in production, as it will allow the SDK to capture screenshots on secure screens that may contain sensitive content.
{% endhint %}

For more information on privacy controls, see [Session Replay for Android](https://docs.luciq.ai/android/set-up-luciq-for-android/set-up-session-replay#privacy-options).

***

## Best Practices

### Recommended Configurations

| **Use Case**      | **Configuration**                        |
| ----------------- | ---------------------------------------- |
| General debugging | Navigation mode + NORMAL quality         |
| UI/UX analysis    | Interactions mode + NORMAL quality       |
| Full video replay | Frequency mode (1000ms) + NORMAL quality |
| Visual debugging  | Frequency mode (500ms) + HIGH quality    |
| Storage-conscious | Navigation mode + GREYSCALE quality      |

### Code Examples

**Video-like Replay (Balanced)**

{% tabs %}
{% tab title="Kotlin" %}
{% code overflow="wrap" %}

```kotlin
@OptIn(ExperimentalVideoLikeReplay::class)
fun configureBalancedVideoReplay() {
    SessionReplay.setCapturingMode(CapturingMode.FREQUENCY)
    SessionReplay.setScreenshotCaptureInterval(1000)
    SessionReplay.setScreenshotQuality(ScreenshotQuality.NORMAL)
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code overflow="wrap" %}

```java
SessionReplay.setCapturingMode(CapturingMode.FREQUENCY);
SessionReplay.setScreenshotCaptureInterval(1000);
SessionReplay.setScreenshotQuality(ScreenshotQuality.NORMAL);
```

{% endcode %}
{% endtab %}
{% endtabs %}

**Interaction-Focused Debugging**

{% tabs %}
{% tab title="Kotlin" %}
{% code overflow="wrap" %}

```kotlin
@OptIn(ExperimentalVideoLikeReplay::class)
fun configureInteractionDebugging() {
    SessionReplay.setCapturingMode(CapturingMode.INTERACTIONS)
    SessionReplay.setScreenshotQuality(ScreenshotQuality.NORMAL)
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code overflow="wrap" %}

```java
SessionReplay.setCapturingMode(CapturingMode.INTERACTIONS);
SessionReplay.setScreenshotQuality(ScreenshotQuality.NORMAL);
```

{% endcode %}
{% endtab %}
{% endtabs %}

**Maximum Visual Fidelity**

{% tabs %}
{% tab title="Kotlin" %}
{% code overflow="wrap" %}

```kotlin
@OptIn(ExperimentalVideoLikeReplay::class)
fun configureMaxFidelity() {
    SessionReplay.setCapturingMode(CapturingMode.FREQUENCY)
    SessionReplay.setScreenshotCaptureInterval(500)
    SessionReplay.setScreenshotQuality(ScreenshotQuality.HIGH)
}
```

{% endcode %}
{% endtab %}

{% tab title="Java" %}
{% code overflow="wrap" %}

```java
SessionReplay.setCapturingMode(CapturingMode.FREQUENCY);
SessionReplay.setScreenshotCaptureInterval(500);
SessionReplay.setScreenshotQuality(ScreenshotQuality.HIGH);
```

{% endcode %}
{% endtab %}
{% endtabs %}

**Storage-Optimized**

{% tabs %}
{% tab title="Kotlin" %}

```kotlin
// No @OptIn needed - using default NAVIGATION mode
SessionReplay.setCapturingMode(CapturingMode.NAVIGATION)
SessionReplay.setScreenshotQuality(ScreenshotQuality.GREYSCALE)
```

{% endtab %}

{% tab title="Java" %}

```java
// Using default NAVIGATION mode
SessionReplay.setCapturingMode(CapturingMode.NAVIGATION);
SessionReplay.setScreenshotQuality(ScreenshotQuality.GREYSCALE);
```

{% endtab %}
{% endtabs %}

***

## API Reference

### SessionReplay.setCapturingMode()

Sets when screenshots are captured.

{% hint style="info" %}
Requires `@OptIn(ExperimentalVideoLikeReplay::class)` for `INTERACTIONS` and `FREQUENCY` modes.
{% endhint %}

```
SessionReplay.setCapturingMode(mode: CapturingMode)
```

| **Value**                    | **Description**                             |
| ---------------------------- | ------------------------------------------- |
| `CapturingMode.NAVIGATION`   | Capture on screen changes only (default)    |
| `CapturingMode.INTERACTIONS` | Capture on navigation and user interactions |
| `CapturingMode.FREQUENCY`    | Capture at fixed time intervals             |

***

### SessionReplay.setScreenshotQuality()

Sets the visual quality of captured screenshots.

{% hint style="success" %}
This API is **NOT** experimental - no `@OptIn` required.
{% endhint %}

```
SessionReplay.setScreenshotQuality(quality: ScreenshotQuality)
```

| **Value**                     | **Description**                  |
| ----------------------------- | -------------------------------- |
| `ScreenshotQuality.HIGH`      | 50% WebP compression             |
| `ScreenshotQuality.NORMAL`    | 25% WebP compression (default)   |
| `ScreenshotQuality.GREYSCALE` | Grayscale + 25% WebP compression |

***

### SessionReplay.setScreenshotCaptureInterval()

Sets the capture interval for Frequency mode.

{% hint style="info" %}
Requires `@OptIn(ExperimentalVideoLikeReplay::class)`.
{% endhint %}

```
SessionReplay.setScreenshotCaptureInterval(intervalMs: Int)
```

| **Parameter** | **Description**                                    |
| ------------- | -------------------------------------------------- |
| `intervalMs`  | Interval in milliseconds (min: 500, default: 1000) |

***

## Migration Guide

If you're upgrading from a previous SDK version:

1. **No breaking changes** - Default behavior remains Navigation mode with NORMAL quality
2. **Opt-in feature** - Video-like replay must be explicitly configured
3. **Repro Steps unaffected** - Bug and Crash report screenshots continue to use Navigation mode and NORMAL quality
4. **Experimental APIs** - Remember to add `@OptIn(ExperimentalVideoLikeReplay::class)` when using `setCapturingMode()` with non-default modes or `setScreenshotCaptureInterval()`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.luciq.ai/android/set-up-luciq-for-android/set-up-session-replay/video-like-replay.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
