> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trypillow.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Present studies

> Show research studies to users inside your Android app

You control when and where to present studies inside your app.

For the concepts behind this, see [About presenting studies](/sdk/concepts/presenting-studies).

## Basic usage

Present a study by its ID. You can find the ID in the **Integration** tab of your study settings.

```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
import so.pillow.sdk.PillowStudy

PillowSDK.presentStudy(
    activity = this,
    study = PillowStudy(id = "your-study-id-here")
)
```

You must pass the current `Activity` so the SDK can present the modal.

## Session resumption

By default, the SDK remembers where a user left off. If a user partially completes a study and you call `presentStudy()` again with the same ID, they resume from where they stopped.

## Start a fresh session

To force a new interview instead of resuming, pass presentation options with `forceFreshSession` set to `true`:

```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
PillowSDK.presentStudy(
    activity = this,
    study = PillowStudy(id = "your-study-id-here"),
    options = PillowStudyPresentationOptions(forceFreshSession = true)
)
```

This clears the stored session for that study and starts a new interview from the beginning.

## Skip repeat exposure

To ask the backend to no-op when the current SDK user was already exposed to the same study, pass presentation options:

```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
import so.pillow.sdk.PillowStudyPresentationOptions

PillowSDK.presentStudy(
    activity = this,
    study = PillowStudy(id = "your-study-id-here"),
    options = PillowStudyPresentationOptions(skipIfAlreadyExposed = true)
)
```

`skipIfAlreadyExposed` defaults to `false`, so existing calls keep presenting unless you opt in.

## Monitor lifecycle

Pass a listener to `presentStudy()` to receive lifecycle callbacks:

```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
import android.util.Log
import so.pillow.sdk.PillowSDK
import so.pillow.sdk.PillowStudy
import so.pillow.sdk.PillowStudyListener

PillowSDK.presentStudy(
    activity = this,
    study = PillowStudy(id = "your-study-id-here"),
    options = PillowStudyPresentationOptions(),
    listener = object : PillowStudyListener {
        override fun studyDidPresent(study: PillowStudy) {
            // study modal appeared on screen
        }
        override fun studyDidSkip(study: PillowStudy) {
            // study was intentionally skipped and not presented
        }
        override fun studyDidFinish(study: PillowStudy) {
            // user finished or dismissed the study
        }
        override fun studyDidFailToLoad(study: PillowStudy, error: Throwable) {
            Log.e("Pillow", "Study failed to load: ${error.message}")
        }
    },
)
```

Passing an anonymous object at the call site is the standard Android pattern here. Unlike SwiftUI, you do not need a separate retained coordinator.

| Method                             | Description                                                                                                                                                    |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `studyDidPresent(study)`           | The study modal appeared on screen                                                                                                                             |
| `studyDidSkip(study)`              | The study was intentionally skipped and not presented                                                                                                          |
| `studyDidFinish(study)`            | The user finished or dismissed the study                                                                                                                       |
| `studyDidFailToLoad(study, error)` | The study could not be loaded or presented. Access `error` for the cause: a network error, the Activity was destroyed, or another study is already being shown |

All listener methods are invoked on the main thread, so you can safely update your UI from them. In Kotlin, all methods have no-op defaults, so override only the ones you need.

<Tip>
  Use the listener to track study engagement, trigger follow-up actions after an interview, or show a fallback if the study fails to load.
</Tip>

## Launch studies

Launch studies are presented automatically to targeted SDK users based on audience filters you configure in the dashboard. Instead of calling `presentStudy()` in your code, the backend tells the SDK which study to show.

### Enable automatic presentation

Call `onReadyToPresentStudy()` from your Activity's `onResume()`. The SDK then automatically presents pending launch studies when the app comes to the foreground.

```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
override fun onResume() {
    super.onResume()
    PillowSDK.onReadyToPresentStudy(activity = this)
}
```

### Manual check

To force an immediate check (for example, after a specific user action), call `presentLaunchStudyIfAvailable()`:

```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
PillowSDK.presentLaunchStudyIfAvailable(activity = this)
```

You can pass an optional delegate to receive lifecycle callbacks, just like `presentStudy()`.

If no launch study is pending, the call is a no-op.

### Presentation

Launch studies are presented as the same native modal as manual `presentStudy()` calls. Any `web_display` settings configured in the dashboard are forwarded to the hosted web experience only. They do not change the native Android presentation.

<Tip>
  To set up audience targeting and launch distributions, see the [audience targeting guide](/guides/audience-targeting).
</Tip>

## Best practices

* **Call `setExternalId()` before presenting** so the study is linked to the right user
* **Copy the study ID from the Integration tab** in your study settings
* **Test in test mode first**: test interviews don't appear in your dashboard data

<Warning>
  Make sure the study is set to **live mode** before presenting it to real users. Studies in test mode work for testing but data won't appear in your production dashboard.
</Warning>
