---
title: "How to add guides and surveys to an Android app with Kotlin"
description: "Add Android guides and surveys with the analytics plugin, screen targeting, tagged views, and preview links for cold and running launches."
canonical: "https://amplitude.com/en-us/build/android-guides-surveys"
---

# How to add guides and surveys to an Android app with Kotlin

Add Android guides and surveys with the analytics plugin, screen targeting, tagged views, and preview links for cold and running launches.

Source: https://amplitude.com/en-us/build/android-guides-surveys

---

# How to add guides and surveys to an Android app with Kotlin

Connect a Kotlin report screen to Amplitude guides and surveys with an analytics plugin, screen targeting, and preview links. Allow about 30 minutes for app setup.

- Android API 24+
- Kotlin
- Engagement SDK 4.x
- 30 min

Chris Tam · Last updated 26 October 2026

Android preview links can launch a fresh activity or arrive while it is already running. I handle both paths and keep guide screen targeting separate from analytics event names.

In this tutorial, I connect [Amplitude guides and surveys](https://amplitude.com/guides-and-surveys) to a native Views report screen. The complete sample includes one analytics client, the engagement plugin, a stable screen name, and a tagged button for targeting.

A UsageReport screen with a report-open view tag, report action events, and a preview-link handler. You’ll create a guide or survey in Amplitude and preview it on the app before publishing the experience.

## Prerequisites

- Android Studio, Android SDK platform 36 and Build Tools 36.0.0, JDK 17, and an API 24+ device or emulator. This Views sample uses AGP 9.2.0 and Gradle 9.4.1.
- A project API key from [Organization settings > API Keys](https://amplitude.com/docs/admin/account-management/manage-your-api-keys-and-secret-keys) and its matching US or EU region.
- Access to guides and surveys and a mobile app configured in the project. Use an active internet connection for loading experiences. Set the project’s mobile URL scheme for device previews.

No Amplitude account yet? [Sign up for free](https://app.amplitude.com/signup) to create a project, and [compare plans](https://amplitude.com/pricing) for the product access and limits your team needs.

## 1. Create the Android project

I create an Android Studio No Activity project named AmplitudeReports, with Kotlin DSL, package com.example.amplitudereports, and minimum SDK 24. Keep the generated gradlew, gradlew\.bat, gradle-wrapper.jar, and machine-specific local.properties. Every custom file is included below; no earlier tutorial is required.

For an existing app, merge the additions into its current application and screen. Preserve build variants, supported plugin versions, authentication, routes, permissions, and instrumentation. Keep one analytics instance across all Android integrations. The complete files describe this fresh sample.

I pin analytics-android 1.35.0 and amplitude-engagement-android 4.1.0. The [Android engagement plugin](https://amplitude.com/docs/sdks/guides-and-surveys/guides-and-surveys-android-sdk) connects to an existing analytics instance. API 24 is the guides and surveys floor.

**settings.gradle.kts**

settings.gradle.kts

```kotlin
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
rootProject.name = "AmplitudeReports"
include(":app")
```

**build.gradle.kts**

build.gradle.kts

```kotlin
plugins {
    id("com.android.application") version "9.2.0" apply false
}
```

**gradle.properties**

gradle.properties

```properties
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.useAndroidX=true
```

**gradle/wrapper/gradle-wrapper.properties**

gradle/wrapper/gradle-wrapper.properties

```properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
```

**app/build.gradle.kts**

app/build.gradle.kts

```kotlin
plugins {
    id("com.android.application")
}
android {
    namespace = "com.example.amplitudereports"
    compileSdk = 36
    defaultConfig {
        applicationId = "com.example.amplitudereports"
        minSdk = 24
        targetSdk = 36
        versionCode = 1
        versionName = "1.0"
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
dependencies {
    implementation("com.amplitude:analytics-android:1.35.0")
    implementation("com.amplitude:amplitude-engagement-android:4.1.0")
}
```

## 2. Initialize once in the application

I register an Application subclass and initialize with application context. Fill API\_KEY with the project key and choose ServerZone.EU for an EU project. An empty key leaves this sample inactive. Mobile project keys ship in the app; management secrets stay on your server.

I add getPlugin() to the same analytics client. The plugin manages boot and shares analytics identity; do not boot a second standalone client or forward the same report event manually. Both SDKs use the project’s region.

**app/src/main/AndroidManifest.xml**

app/src/main/AndroidManifest.xml

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:name=".ReportsApplication"
        android:allowBackup="false"
        android:label="Amplitude Reports"
        android:supportsRtl="true"
        android:theme="@android:style/Theme.Material.Light.NoActionBar">
        <activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="amp-report-demo" />
            </intent-filter>
        </activity>
    </application>
</manifest>
```

**app/src/main/java/com/example/amplitudereports/AmplitudeConfig.kt**

app/src/main/java/com/example/amplitudereports/AmplitudeConfig.kt

```kotlin
package com.example.amplitudereports

import com.amplitude.core.ServerZone

object AmplitudeConfig {
    const val API_KEY = ""
    val SERVER_ZONE = ServerZone.US
}
```

**app/src/main/java/com/example/amplitudereports/ReportsApplication.kt**

app/src/main/java/com/example/amplitudereports/ReportsApplication.kt

```kotlin
package com.example.amplitudereports

import android.app.Application
import com.amplitude.android.Amplitude
import com.amplitude.android.engagement.AmplitudeEngagement
import com.amplitude.android.engagement.AmplitudeInitOptions
import com.amplitude.android.engagement.AmplitudeServerZone
import com.amplitude.core.ServerZone

class ReportsApplication : Application() {
    lateinit var analytics: ReportAnalytics
        private set
    var engagement: AmplitudeEngagement? = null
        private set

    override fun onCreate() {
        super.onCreate()
        val client = AmplitudeConfig.API_KEY.takeIf { it.isNotBlank() }?.let { key ->
            Amplitude(key, applicationContext) {
                serverZone = AmplitudeConfig.SERVER_ZONE
                autocapture = emptySet()
                enableAutocaptureRemoteConfig = false
            }
        if (client != null) {
            engagement = AmplitudeEngagement(
                context = applicationContext,
                apiKey = AmplitudeConfig.API_KEY,
                options = AmplitudeInitOptions(serverZone =
                    if (AmplitudeConfig.SERVER_ZONE == ServerZone.EU) AmplitudeServerZone.EU
                    else AmplitudeServerZone.US),
            )
            client.add(engagement!!.getPlugin())
        }
        analytics = ReportAnalytics(client)
    }

    // Main-thread callbacks from confirmed auth. Loading auth is not logout.
    fun accountSignedIn(userId: String, plan: String) {
        engagement?.closeAll()
        analytics.accountSignedIn(userId, plan)
    }
    fun accountSignedOut() {
        engagement?.closeAll()
        analytics.accountSignedOut()
    }
```

## 3. Connect report events and account changes

I keep explicit report events in one adapter. In an authenticated app, call ReportsApplication.accountSignedIn from verified authentication before enabling report actions. A null or loading account is not logout. On a confirmed account switch, call accountSignedOut before accountSignedIn for the next account. The sample has no login UI; these are hooks for your existing authentication.

**app/src/main/java/com/example/amplitudereports/ReportAnalytics.kt**

app/src/main/java/com/example/amplitudereports/ReportAnalytics.kt

```kotlin
package com.example.amplitudereports

import com.amplitude.android.Amplitude
import com.amplitude.core.events.Identify

class ReportAnalytics(private val amplitude: Amplitude?) {
    val configured: Boolean get() = amplitude != null
    fun deviceId(): String? = amplitude?.getDeviceId()

    fun reportPreviewed(days: Int) {
        amplitude?.track("Report Previewed", reportProperties(days))
    }

    fun reportRangeChanged(days: Int) {
        amplitude?.track("Report Range Changed", reportProperties(days))
    }

    // Call after your existing authentication confirms the account, before its events.
    fun accountSignedIn(canonicalUserId: String, plan: String) {
        require(canonicalUserId.length >= 5) { "Use a canonical ID of at least five characters" }
        amplitude?.setUserId(canonicalUserId)
        amplitude?.identify(Identify().set("plan", plan))
    }

    // Call only after confirmed logout, never for a loading or anonymous session.
    fun accountSignedOut() {
        amplitude?.reset()
    }

    fun flush() {
        amplitude?.flush()
    }

    private fun reportProperties(days: Int): Map<String, Any> = mapOf(
        "report_type" to "usage",
        "days" to days,
        "platform" to "Android",
    )
}
```

Call account lifecycle hooks on the main thread. closeAll dismisses the current experience before switching identity; analytics.reset runs only after confirmed logout. It issues a new device identity. Keep actions disabled while authentication is unresolved, and preserve your existing deep-link routes alongside the preview handler.

## 4. Build the report screen

I keep seven- and 30-day report state across activity recreation without emitting another report event from onCreate. The platform Views UI handles system bars and display cutouts, including landscape. The device-ID and flush control is for development; it requests an upload without proving delivery. Remove it from your production screen.

I call screen("UsageReport") when the screen resumes and tag the Open report button as report-open. Preview intents are processed after the screen is ready for both a fresh launch and onNewIntent. Replace amp-report-demo in the manifest with the exact mobile URL scheme configured for this project; rebuild and reinstall afterward.

**app/src/main/java/com/example/amplitudereports/MainActivity.kt**

app/src/main/java/com/example/amplitudereports/MainActivity.kt

```kotlin
package com.example.amplitudereports

import android.content.Intent
import android.app.Activity
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.WindowInsets
import android.widget.Button
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView

class MainActivity : Activity() {
    private val analytics: ReportAnalytics
        get() = (application as ReportsApplication).analytics
    private val app: ReportsApplication get() = application as ReportsApplication
    private var days = 7
    private var opened = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState == null) pendingPreview = intent
        days = savedInstanceState?.getInt("days", 7) ?: 7
        opened = savedInstanceState?.getBoolean("opened", false) ?: false
        val padding = (24 * resources.displayMetrics.density).toInt()
        val column = LinearLayout(this).apply {
            orientation = LinearLayout.VERTICAL
            setPadding(padding, padding, padding, padding)
        }
        val scroll = ScrollView(this).apply { addView(column) }
        scroll.setOnApplyWindowInsetsListener { view: View, insets: WindowInsets ->
            if (Build.VERSION.SDK_INT >= 30) {
                val safe = insets.getInsets(
                    WindowInsets.Type.systemBars() or WindowInsets.Type.displayCutout(),
                )
                view.setPadding(safe.left, safe.top, safe.right, safe.bottom)
            } else if (Build.VERSION.SDK_INT >= 28) {
                val cutout = insets.displayCutout
                view.setPadding(
                    maxOf(insets.systemWindowInsetLeft, cutout?.safeInsetLeft ?: 0),
                    maxOf(insets.systemWindowInsetTop, cutout?.safeInsetTop ?: 0),
                    maxOf(insets.systemWindowInsetRight, cutout?.safeInsetRight ?: 0),
                    maxOf(insets.systemWindowInsetBottom, cutout?.safeInsetBottom ?: 0),
                )
            } else {
                view.setPadding(
                    insets.systemWindowInsetLeft, insets.systemWindowInsetTop,
                    insets.systemWindowInsetRight, insets.systemWindowInsetBottom,
                )
            }
            insets
        }
        setContentView(scroll)
        scroll.requestApplyInsets()
        column.addView(TextView(this).apply {
            text = "Usage reports"
            textSize = 28f
        })
        val status = TextView(this).apply {
            text = if (analytics.configured) "Analytics configured; verify delivery separately"
                else "Analytics off: set the project API key"
        }
        column.addView(status)
        val report = TextView(this)
        fun render() {
            report.text = if (opened) "$days-day report: ${days * 12} sample actions"
                else "Open the sample report"
        }
        render()
        column.addView(report)
        column.addView(Button(this).apply {
            tag = "report-open"
            text = "Open report"
            setOnClickListener {
                opened = true
                render()
                analytics.reportPreviewed(days)
            }
        })
        column.addView(Button(this).apply {
            text = "Change date range"
            setOnClickListener {
                days = if (days == 7) 30 else 7
                opened = true
                render()
                analytics.reportRangeChanged(days)
            }
        })
        column.addView(Button(this).apply {
            text = "Flush and show device ID"
            setOnClickListener {
                analytics.flush()
                status.text = "Device ID: ${analytics.deviceId() ?: "not available yet"}; flush requested"
            }
        })
    }

    override fun onResume() {
        super.onResume()
        app.engagement?.screen("UsageReport")
        pendingPreview?.let { app.engagement?.handleLinkIntent(it) }
        pendingPreview = null
    }

    private var pendingPreview: Intent? = null
    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
        pendingPreview = intent
    }

    override fun onSaveInstanceState(outState: Bundle) {
        outState.putInt("days", days)
        outState.putBoolean("opened", opened)
        super.onSaveInstanceState(outState)
    }
```

## 5. Configure and run the development check

Set the scheme under Settings > Projects > your project > General > URL scheme (mobile). In Guides and Surveys > App Management, add the Android app. Create a guide or survey for the UsageReport screen. Use report-open when pinning a step to the button, and Report Previewed if the experience uses that event as its trigger. Configure the content, audience, and display rules. Save it for preview first; publishing the app code does not publish the guide. Use the mobile preview QR code or deep link on the device with this app installed.

Sync Gradle, then run these commands from the sample project. On Windows, use gradlew\.bat.

**Terminal**

Terminal

```sh
./gradlew :app:assembleDebug
./gradlew :app:installDebug
adb shell am start -n com.example.amplitudereports/.MainActivity
```

## Verify it worked

1. Build and install the app, use a development key, and open the report. Tap the Amplitude logo in mobile preview to open the Super Debugger, then use its Details and Setup views to confirm the screen is UsageReport and the Report Previewed trigger arrives. Verify a pinned step targets report-open and can be dismissed.
2. Open the preview link once with the app fully closed and again while MainActivity is running. Both paths should open the intended preview. Check normal application links still work after you merge the handler into your existing router.
3. Confirm the configured audience and screen rules with synthetic signed-in and signed-out identities. Close the experience, complete confirmed logout, and check the next account cannot see the previous account’s experience state. Do not use reset(guideKey,stepIndex) as an identity reset.
4. Inspect ingestion counts in Data > Sources > Ingestion Debugger, then the individual Report Previewed and Report Range Changed events in Users > User Profiles using the displayed device ID. Separately inspect the guide’s or survey’s results after a real development interaction. Local screen rendering does not establish targeting, delivery, or processed responses.

## Troubleshooting

### The preview link opens nothing

Match the installed manifest scheme to the project’s mobile URL scheme and reinstall after changing it. Preserve both initial intent and onNewIntent paths.

### The guide is not shown on the report

Confirm the exact UsageReport screen target, trigger, audience, and saved preview. SDK initialization alone does not make an experience eligible.

### A pinned step misses the button

Use the exact report-open view tag and confirm the tagged view is visible before the step is displayed. Keep accessibility labels meaningful.

### Events or experiences appear twice

Reuse the existing analytics client, add the plugin once, and remove manual boot or duplicate forwardEvent calls for this plugin integration.

## Frequently asked questions

### Do I call boot with this plugin?

No. Adding the plugin integrates initialization with analytics. The standalone boot flow is a different integration path.

### Does the preview link replace app navigation?

No. Merge preview handling with your existing links and router. Keep normal application navigation intact.

## Full code reference

10 files. Keep the Android Studio wrapper launchers/JAR and local SDK configuration alongside these custom files.

10 files

```text
// settings.gradle.kts
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
rootProject.name = "AmplitudeReports"
include(":app")

// build.gradle.kts
plugins {
    id("com.android.application") version "9.2.0" apply false
}

// gradle.properties
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.useAndroidX=true

// gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

// app/build.gradle.kts
plugins {
    id("com.android.application")
}
android {
    namespace = "com.example.amplitudereports"
    compileSdk = 36
    defaultConfig {
        applicationId = "com.example.amplitudereports"
        minSdk = 24
        targetSdk = 36
        versionCode = 1
        versionName = "1.0"
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
dependencies {
    implementation("com.amplitude:analytics-android:1.35.0")
    implementation("com.amplitude:amplitude-engagement-android:4.1.0")
}

// app/src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        android:name=".ReportsApplication"
        android:allowBackup="false"
        android:label="Amplitude Reports"
        android:supportsRtl="true"
        android:theme="@android:style/Theme.Material.Light.NoActionBar">
        <activity android:name=".MainActivity" android:exported="true" android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="amp-report-demo" />
            </intent-filter>
        </activity>
    </application>
</manifest>

// app/src/main/java/com/example/amplitudereports/AmplitudeConfig.kt
package com.example.amplitudereports

import com.amplitude.core.ServerZone

object AmplitudeConfig {
    const val API_KEY = ""
    val SERVER_ZONE = ServerZone.US
}

// app/src/main/java/com/example/amplitudereports/ReportsApplication.kt
package com.example.amplitudereports

import android.app.Application
import com.amplitude.android.Amplitude
import com.amplitude.android.engagement.AmplitudeEngagement
import com.amplitude.android.engagement.AmplitudeInitOptions
import com.amplitude.android.engagement.AmplitudeServerZone
import com.amplitude.core.ServerZone

class ReportsApplication : Application() {
    lateinit var analytics: ReportAnalytics
        private set
    var engagement: AmplitudeEngagement? = null
        private set

    override fun onCreate() {
        super.onCreate()
        val client = AmplitudeConfig.API_KEY.takeIf { it.isNotBlank() }?.let { key ->
            Amplitude(key, applicationContext) {
                serverZone = AmplitudeConfig.SERVER_ZONE
                autocapture = emptySet()
                enableAutocaptureRemoteConfig = false
            }
        if (client != null) {
            engagement = AmplitudeEngagement(
                context = applicationContext,
                apiKey = AmplitudeConfig.API_KEY,
                options = AmplitudeInitOptions(serverZone =
                    if (AmplitudeConfig.SERVER_ZONE == ServerZone.EU) AmplitudeServerZone.EU
                    else AmplitudeServerZone.US),
            )
            client.add(engagement!!.getPlugin())
        }
        analytics = ReportAnalytics(client)
    }

    // Main-thread callbacks from confirmed auth. Loading auth is not logout.
    fun accountSignedIn(userId: String, plan: String) {
        engagement?.closeAll()
        analytics.accountSignedIn(userId, plan)
    }
    fun accountSignedOut() {
        engagement?.closeAll()
        analytics.accountSignedOut()
    }

// app/src/main/java/com/example/amplitudereports/ReportAnalytics.kt
package com.example.amplitudereports

import com.amplitude.android.Amplitude
import com.amplitude.core.events.Identify

class ReportAnalytics(private val amplitude: Amplitude?) {
    val configured: Boolean get() = amplitude != null
    fun deviceId(): String? = amplitude?.getDeviceId()

    fun reportPreviewed(days: Int) {
        amplitude?.track("Report Previewed", reportProperties(days))
    }

    fun reportRangeChanged(days: Int) {
        amplitude?.track("Report Range Changed", reportProperties(days))
    }

    // Call after your existing authentication confirms the account, before its events.
    fun accountSignedIn(canonicalUserId: String, plan: String) {
        require(canonicalUserId.length >= 5) { "Use a canonical ID of at least five characters" }
        amplitude?.setUserId(canonicalUserId)
        amplitude?.identify(Identify().set("plan", plan))
    }

    // Call only after confirmed logout, never for a loading or anonymous session.
    fun accountSignedOut() {
        amplitude?.reset()
    }

    fun flush() {
        amplitude?.flush()
    }

    private fun reportProperties(days: Int): Map<String, Any> = mapOf(
        "report_type" to "usage",
        "days" to days,
        "platform" to "Android",
    )
}

// app/src/main/java/com/example/amplitudereports/MainActivity.kt
package com.example.amplitudereports

import android.content.Intent
import android.app.Activity
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.WindowInsets
import android.widget.Button
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView

class MainActivity : Activity() {
    private val analytics: ReportAnalytics
        get() = (application as ReportsApplication).analytics
    private val app: ReportsApplication get() = application as ReportsApplication
    private var days = 7
    private var opened = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState == null) pendingPreview = intent
        days = savedInstanceState?.getInt("days", 7) ?: 7
        opened = savedInstanceState?.getBoolean("opened", false) ?: false
        val padding = (24 * resources.displayMetrics.density).toInt()
        val column = LinearLayout(this).apply {
            orientation = LinearLayout.VERTICAL
            setPadding(padding, padding, padding, padding)
        }
        val scroll = ScrollView(this).apply { addView(column) }
        scroll.setOnApplyWindowInsetsListener { view: View, insets: WindowInsets ->
            if (Build.VERSION.SDK_INT >= 30) {
                val safe = insets.getInsets(
                    WindowInsets.Type.systemBars() or WindowInsets.Type.displayCutout(),
                )
                view.setPadding(safe.left, safe.top, safe.right, safe.bottom)
            } else if (Build.VERSION.SDK_INT >= 28) {
                val cutout = insets.displayCutout
                view.setPadding(
                    maxOf(insets.systemWindowInsetLeft, cutout?.safeInsetLeft ?: 0),
                    maxOf(insets.systemWindowInsetTop, cutout?.safeInsetTop ?: 0),
                    maxOf(insets.systemWindowInsetRight, cutout?.safeInsetRight ?: 0),
                    maxOf(insets.systemWindowInsetBottom, cutout?.safeInsetBottom ?: 0),
                )
            } else {
                view.setPadding(
                    insets.systemWindowInsetLeft, insets.systemWindowInsetTop,
                    insets.systemWindowInsetRight, insets.systemWindowInsetBottom,
                )
            }
            insets
        }
        setContentView(scroll)
        scroll.requestApplyInsets()
        column.addView(TextView(this).apply {
            text = "Usage reports"
            textSize = 28f
        })
        val status = TextView(this).apply {
            text = if (analytics.configured) "Analytics configured; verify delivery separately"
                else "Analytics off: set the project API key"
        }
        column.addView(status)
        val report = TextView(this)
        fun render() {
            report.text = if (opened) "$days-day report: ${days * 12} sample actions"
                else "Open the sample report"
        }
        render()
        column.addView(report)
        column.addView(Button(this).apply {
            tag = "report-open"
            text = "Open report"
            setOnClickListener {
                opened = true
                render()
                analytics.reportPreviewed(days)
            }
        })
        column.addView(Button(this).apply {
            text = "Change date range"
            setOnClickListener {
                days = if (days == 7) 30 else 7
                opened = true
                render()
                analytics.reportRangeChanged(days)
            }
        })
        column.addView(Button(this).apply {
            text = "Flush and show device ID"
            setOnClickListener {
                analytics.flush()
                status.text = "Device ID: ${analytics.deviceId() ?: "not available yet"}; flush requested"
            }
        })
    }

    override fun onResume() {
        super.onResume()
        app.engagement?.screen("UsageReport")
        pendingPreview?.let { app.engagement?.handleLinkIntent(it) }
        pendingPreview = null
    }

    private var pendingPreview: Intent? = null
    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)
        pendingPreview = intent
    }

    override fun onSaveInstanceState(outState: Bundle) {
        outState.putInt("days", days)
        outState.putBoolean("opened", opened)
        super.onSaveInstanceState(outState)
    }
```

## Hand it to your agent

Use this prompt with your coding agent.

**Agent prompt**

Agent prompt

```text
Follow https://amplitude.com/build/android-guides-surveys. Build the complete native Kotlin Views sample, or merge it into the existing app while preserving authentication, navigation, build variants, and instrumentation. Keep one analytics client initialized with application context in Application.onCreate. Match key and region. Use the exact pinned dependencies shown on the page. Keep report events and properties unchanged. Connect verified auth callbacks; reset only after confirmed logout, never from loading or anonymous state. Add the engagement plugin once to the existing analytics client, without manual boot or duplicate event forwarding. Use API 24+, match both SDK regions, screen UsageReport, and tag report-open. Merge initial-launch and running-activity preview links with the existing router. Close experiences before confirmed account changes. Report compilation, local tests, real-device behavior, and live-service outcomes separately. Do not claim a build or stubbed test proves delivery, experiment analysis, masking, or guide targeting.
```

## What this unlocks

[Guides and surveys](https://amplitude.com/guides-and-surveys) can explain the UsageReport screen or collect feedback after Report Previewed. [Product analytics](https://amplitude.com/amplitude-analytics) lets you compare report usage after the experience is delivered.

## Next steps

[MobileTrack Android report actionsReview the explicit event and account lifecycle pattern.](https://amplitude.com/build/android-product-analytics)[MobileAdd analytics to React NativeCompare the React Native lifecycle and native dependency setup.](https://amplitude.com/build/react-native-product-analytics)[PlatformDebug analyticsFollow request counts to individual profile events.](https://amplitude.com/docs/analytics/debug-analytics)

Reference docs

[Guides and surveys android sdk →](https://amplitude.com/docs/sdks/guides-and-surveys/guides-and-surveys-android-sdk)[Android kotlin sdk →](https://amplitude.com/docs/sdks/analytics/android/android-kotlin-sdk)[Debug analytics →](https://amplitude.com/docs/analytics/debug-analytics)[AGP 9.2 compatibility →](https://developer.android.com/build/releases/agp-9-2-0-release-notes)

Amplitude is the leading [all-in-one analytics platform](https://amplitude.com/) for building better products. Product and engineering teams use Amplitude to connect [product analytics](https://amplitude.com/amplitude-analytics) and [web analytics](https://amplitude.com/web-analytics) with [session replay](https://amplitude.com/session-replay), [heatmaps](https://amplitude.com/heatmaps), [experimentation](https://amplitude.com/amplitude-experiment), [feature flags](https://amplitude.com/feature-management), and [guides and surveys](https://amplitude.com/guides-and-surveys) in one unified workflow, with [AI agents](https://amplitude.com/ai-agents) working across all of it. Learn how Amplitude is building [self-improving products](https://amplitude.com/self-improving-products) with [Wave](https://amplitude.com/wave), helping teams turn product data into PRs.

For agents

[View raw .md](/build/android-guides-surveys.md)[llms.txt](/llms.txt)

## Ship the instrumentation, then read the data

Use verified report interactions to decide what to improve next.

[Create a free account](https://app.amplitude.com/signup)[Browse all Build tutorials](https://amplitude.com/build)
