---
title: "AI Assistant (Single Document)"
canonical_url: "https://www.nutrient.io/guides/android/samples/ai-assistant-single-document-kotlin/"
md_url: "https://www.nutrient.io/guides/android/samples/ai-assistant-single-document-kotlin.md"
last_updated: "2025-07-02T00:00:00.000Z"
description: "AI-powered document chat using the Compose API. Requires the AI Assistant demo server running locally."
---

# AI Assistant (Single Document)

AI-powered document chat using the Compose API. Requires the AI Assistant demo server running locally.

[Get Started](https://www.nutrient.io/sdk/android/getting-started.md)

[All Samples](https://www.nutrient.io/guides/android/samples.md)

[Download](https://www.nutrient.io/guides/android/downloads.md)

[Launch Demo](https://www.nutrient.io/demo/)

---

```kotlin

/*
 *   Copyright © 2025-2026 PSPDFKit GmbH. All rights reserved.
 *
 *   The PSPDFKit Sample applications are licensed with a modified BSD license.
 *   Please see License for details. This notice may not be removed from this file.
 */

package com.pspdfkit.catalog.examples.kotlin

import android.content.Context
import android.content.Intent
import android.graphics.RectF
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import com.pspdfkit.ai.createAiAssistant
import com.pspdfkit.catalog.R
import com.pspdfkit.catalog.SdkExample
import com.pspdfkit.catalog.SdkExample.Companion.WELCOME_DOC
import com.pspdfkit.catalog.ui.theming.CatalogTheme
import com.pspdfkit.catalog.utils.JwtGenerator
import com.pspdfkit.configuration.activity.PdfActivityConfiguration
import com.pspdfkit.document.providers.AssetDataProvider
import com.pspdfkit.jetpack.compose.interactors.DefaultListeners
import com.pspdfkit.jetpack.compose.interactors.DocumentState
import com.pspdfkit.jetpack.compose.interactors.getDefaultDocumentManager
import com.pspdfkit.jetpack.compose.interactors.rememberDocumentState
import com.pspdfkit.jetpack.compose.views.DocumentView
import com.pspdfkit.ui.DocumentDescriptor
import com.pspdfkit.ui.PdfActivity
import io.nutrient.domain.ai.AiAssistant
import io.nutrient.domain.ai.AiAssistantProvider

/**
 * Shows how to implement AI Assistant for the DocumentView in a Compose way.
 */
class AiAssistantComposeExample(context: Context) :
    SdkExample(
        context,
        R.string.jetpackAiAssistantExampleTitle,
        R.string.jetpackAiAssistantExampleDescription,
    ) {
    override val launchRequirements = setOf(SdkExample.LaunchRequirement.AI_ASSISTANT_SERVER)

    override fun launchExample(context: Context, configuration: PdfActivityConfiguration.Builder) {
        val intent = Intent(context, AiAssistantComposeActivity::class.java)
        context.startActivity(intent)
    }
}

class AiAssistantComposeActivity :
    AppCompatActivity(),
    AiAssistantProvider {
    private lateinit var documentState: DocumentState
    private val sessionId = AiAssistantComposeActivity::class.java.simpleName
    private val assetProvider = AssetDataProvider(WELCOME_DOC)
    private var ipAddressValue: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        enableEdgeToEdge()
        super.onCreate(savedInstanceState)

        val preferences = getSharedPreferences(PREFERENCES_NAME, MODE_PRIVATE)
        ipAddressValue = preferences.getString(PREF_AI_IP_ADDRESS, "")?: ""

        setContent {
            var enabled by remember { mutableStateOf(false) }
            var toolbarVisibility by remember { mutableStateOf(true) }

            CatalogTheme {
                val activityConfiguration =
                    PdfActivityConfiguration.Builder(LocalContext.current).setAiAssistantEnabled(true).defaultToolbarEnabled(false).theme(R.style.PSPDFCatalog_AIAssistantDialog).themeDark(R.style.PSPDFCatalog_AIAssistantDialog_Dark).build()

                documentState = rememberDocumentState(assetProvider, activityConfiguration)

                Box(
                    modifier =
                    Modifier.fillMaxSize().navigationBarsPadding(),
                ) {
                    DocumentView(
                        documentState = documentState,
                        documentManager =
                        getDefaultDocumentManager(
                            documentListener =
                            DefaultListeners.documentListeners(
                                onDocumentLoaded = {
                                    enabled = true
                                },
                            ),
                            uiListener =
                            DefaultListeners.uiListeners(
                                onImmersiveModeEnabled = { toolbarVisibility =!it },
                            ),
                        ),
                    )
                }
                CustomToolbar(documentState, enabled, toolbarVisibility)
            }
        }
    }

    var assistant: AiAssistant? = null

    override fun getAiAssistant(): AiAssistant {
        // Prefer the currently opened document descriptor so runtime state is preserved
        // (for example, a password-protected PDF that has already been unlocked in the viewer).
        // Fall back to a source-based descriptor when the UI document is not available yet.
        // For encrypted PDFs, this fallback path requires the password to be provided in DocumentSource.
        val documentDescriptor =
            runCatching { documentState.documentConnection.pdfUi.document?.let(DocumentDescriptor::fromDocument) }.getOrNull()?: DocumentDescriptor.fromDataProviders(listOf(assetProvider), null, null)

        return assistant?: run {
            createAiAssistant(
                context = this@AiAssistantComposeActivity,
                documentsDescriptors = listOf(documentDescriptor),
                serverUrl = "http://$ipAddressValue:4000",
                sessionId = sessionId,
                jwtToken = { documentIds ->
                    JwtGenerator.generateJwtToken(
                        this@AiAssistantComposeActivity,
                        claims =
                        mapOf(
                            "document_ids" to documentIds,
                            "session_ids" to listOf(sessionId),
                            "request_limit" to
                                mapOf(
                                    "requests" to 160,
                                    "time_period_s" to 1000 * 60 * 10,
                                ),
                        ),
                    )
                },
            ).also {
                // Enable/disable text selection in AI Assistant chat messages
                // false = disable, true = enable (default)
                // it.enableTextSelection(false)
                assistant = it
            }
        }
    }

    override fun navigateTo(documentRect: List<RectF>, pageIndex: Int, documentIndex: Int) {
        documentState.documentConnection.highlight(pageIndex, documentRect)
    }

    companion object {
        const val EXTRA_URI = "JetpackComposeImageActivity.DocumentUri"
        const val PREFERENCES_NAME = "Nutrient.AiAssistant"
        const val PREF_AI_IP_ADDRESS = "ai_ip_address"
    }
}

/**
 * A custom toolbar component for document viewer with animated visibility.
 *
 * @param documentState The current state of the document being viewed
 * @param enabled Whether the toolbar actions are enabled
 * @param toolbarVisibility Whether the toolbar should be visible
 */
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CustomToolbar(documentState: DocumentState, enabled: Boolean, toolbarVisibility: Boolean) {
    val localDensity = LocalDensity.current

    // Animate toolbar appearance/disappearance with slide, expand and fade effects
    AnimatedVisibility(
        visible = toolbarVisibility,
        enter =
        slideInVertically { with(localDensity) { -40.dp.roundToPx() } } +
            expandVertically(expandFrom = Alignment.Top) +
            fadeIn(initialAlpha = 0.3f),
        exit = slideOutVertically() + shrinkVertically() + fadeOut(),
    ) {
        TopAppBar(
            title = {
                // Display document title or empty string if null
                Text(
                    text = documentState.getTitle().orEmpty(),
                    color = Color.White,
                )
            },
            actions = {
                // AI Assistant action button
                IconButton(
                    onClick = {
                        documentState.toggleView(PdfActivity.MENU_OPTION_AI_ASSISTANT)
                    },
                    enabled = enabled,
                ) {
                    Icon(
                        painter = painterResource(id = R.drawable.ic_ai_assistant),
                        contentDescription = "AI Assistant",
                        tint = Color.White,
                    )
                }

                // Settings action button
                IconButton(
                    onClick = {
                        documentState.toggleView(PdfActivity.MENU_OPTION_SETTINGS)
                    },
                    enabled = enabled,
                ) {
                    Icon(
                        painter = painterResource(id = R.drawable.ic_topbar_settings),
                        contentDescription = "Settings",
                        tint = Color.White,
                    )
                }
            },
            colors =
            TopAppBarDefaults.topAppBarColors(
                containerColor = MaterialTheme.colorScheme.primary,
                titleContentColor = MaterialTheme.colorScheme.onPrimary,
            ),
        )
    }
}

```

This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.

---

## Related pages

- [Add LTV to Existing Signature](/guides/android/samples/add-ltv-to-existing-signature-kotlin.md)
- [AES Encrypted File](/guides/android/samples/aes-encrypted-file-java.md)
- [AI Assistant (Multiple Documents, Compose)](/guides/android/samples/ai-assistant-multiple-documents-compose-kotlin.md)
- [AI Assistant (Multiple Documents, ViewPager)](/guides/android/samples/ai-assistant-multiple-documents-viewpager-kotlin.md)
- [Annotation Configuration](/guides/android/samples/annotation-configuration-kotlin.md)
- [Annotation Creation](/guides/android/samples/annotation-creation-kotlin.md)
- [Annotation Flags](/guides/android/samples/annotation-flags-kotlin.md)
- [Annotation Overlay](/guides/android/samples/annotation-overlay-java.md)
- [Annotation Rendering](/guides/android/samples/annotation-rendering-kotlin.md)
- [Annotation Selection Styling](/guides/android/samples/annotation-selection-styling-kotlin.md)
- [Annotation Sidebar](/guides/android/samples/annotation-sidebar-kotlin.md)
- [Annotations with Transparency](/guides/android/samples/annotations-with-transparency-kotlin.md)
- [Application Policy](/guides/android/samples/application-policy-kotlin.md)
- [Bookmark Highlighting](/guides/android/samples/bookmark-highlighting-kotlin.md)
- [Compose Image Document](/guides/android/samples/compose-image-document-kotlin.md)
- [Compose Navigation](/guides/android/samples/compose-navigation-kotlin.md)
- [Construction Floor Plan](/guides/android/samples/construction-floor-plan-kotlin.md)
- [Custom ActionBar Actions](/guides/android/samples/custom-actionbar-actions-kotlin.md)
- [Custom Activity Form Editing](/guides/android/samples/custom-activity-form-editing-java.md)
- [Custom Activity Toolbars](/guides/android/samples/custom-activity-toolbars-java.md)
- [Custom Annotation Creation Toolbar](/guides/android/samples/custom-annotation-creation-toolbar-java.md)
- [Custom Annotation Inspector](/guides/android/samples/custom-annotation-inspector-java.md)
- [Custom Data Provider](/guides/android/samples/custom-data-provider-kotlin.md)
- [Custom Download Dialog](/guides/android/samples/custom-download-dialog-java.md)
- [Custom Electronic Signature](/guides/android/samples/custom-electronic-signature-java.md)
- [Custom Form Highlight Color](/guides/android/samples/custom-form-highlight-color-java.md)
- [Custom Layout](/guides/android/samples/custom-layout-kotlin.md)
- [Custom Main Toolbar](/guides/android/samples/custom-main-toolbar-kotlin.md)
- [Custom Note Hinter](/guides/android/samples/custom-note-hinter-kotlin.md)
- [Custom Outline Provider](/guides/android/samples/custom-outline-provider-kotlin.md)
- [Custom Page Templates](/guides/android/samples/custom-page-templates-java.md)
- [Custom Search UI (Compose)](/guides/android/samples/custom-search-ui-compose-kotlin.md)
- [Custom Search UI (Views)](/guides/android/samples/custom-search-ui-views-java.md)
- [Custom Sharing Dialog](/guides/android/samples/custom-sharing-dialog-java.md)
- [Custom Sharing Menu](/guides/android/samples/custom-sharing-menu-java.md)
- [Custom Stamp Annotations](/guides/android/samples/custom-stamp-annotations-java.md)
- [Custom Toolbar Grouping](/guides/android/samples/custom-toolbar-grouping-java.md)
- [Digital Signature (Basic)](/guides/android/samples/digital-signature-basic-kotlin.md)
- [Digital Signature (Manual)](/guides/android/samples/digital-signature-manual-kotlin.md)
- [Digital Signature (Third-Party)](/guides/android/samples/digital-signature-third-party-kotlin.md)
- [Digital Signature (Two-Step)](/guides/android/samples/digital-signature-two-step-kotlin.md)
- [Disabled Annotation Property](/guides/android/samples/disabled-annotation-property-java.md)
- [Document Comparison](/guides/android/samples/document-comparison-kotlin.md)
- [Document Download](/guides/android/samples/document-download-kotlin.md)
- [Document from Canvas](/guides/android/samples/document-from-canvas-kotlin.md)
- [Document Processing](/guides/android/samples/document-processing-kotlin.md)
- [Document Sharing](/guides/android/samples/document-sharing-java.md)
- [Document Switcher](/guides/android/samples/document-switcher-java.md)
- [DocumentView Composable](/guides/android/samples/documentview-composable-kotlin.md)
- [Download Progress](/guides/android/samples/download-progress-kotlin.md)
- [Dynamic Pages on Scroll](/guides/android/samples/dynamic-pages-on-scroll-kotlin.md)
- [E-Learning](/guides/android/samples/e-learning-kotlin.md)
- [Electronic + Digital Signing](/guides/android/samples/electronic-digital-signing-kotlin.md)
- [External Document](/guides/android/samples/external-document-kotlin.md)
- [File Annotation Creation](/guides/android/samples/file-annotation-creation-kotlin.md)
- [Filterable Thumbnail Grid](/guides/android/samples/filterable-thumbnail-grid-kotlin.md)
- [Form Click Intercept (Compose)](/guides/android/samples/form-click-intercept-compose-kotlin.md)
- [Form Creation](/guides/android/samples/form-creation-kotlin.md)
- [Form Filling](/guides/android/samples/form-filling-kotlin.md)
- [Forms with JavaScript](/guides/android/samples/forms-with-javascript-kotlin.md)
- [Fragment Runtime Configuration](/guides/android/samples/fragment-runtime-configuration-kotlin.md)
- [Generate PDF Report](/guides/android/samples/generate-pdf-report-kotlin.md)
- [Hide and Reveal Areas](/guides/android/samples/hide-and-reveal-areas-kotlin.md)
- [HTML-to-PDF Conversion](/guides/android/samples/html-to-pdf-conversion-kotlin.md)
- [Image Document](/guides/android/samples/image-document-kotlin.md)
- [Inline Multimedia](/guides/android/samples/inline-multimedia-kotlin.md)
- [Inline Search](/guides/android/samples/inline-search-java.md)
- [Instant Document JSON](/guides/android/samples/instant-document-json-kotlin.md)
- [Instant JSON Attachment](/guides/android/samples/instant-json-attachment-kotlin.md)
- [JavaScript Actions](/guides/android/samples/javascript-actions-kotlin.md)
- [JavaScript Calculator](/guides/android/samples/javascript-calculator-kotlin.md)
- [JavaScript Form Filling](/guides/android/samples/javascript-form-filling-kotlin.md)
- [Kiosk Grid](/guides/android/samples/kiosk-grid-kotlin.md)
- [LTV Signature](/guides/android/samples/ltv-signature-kotlin.md)
- [Measurement Tools](/guides/android/samples/measurement-tools-kotlin.md)
- [Merge Documents](/guides/android/samples/merge-documents-kotlin.md)
- [Multimedia Annotations](/guides/android/samples/multimedia-annotations-kotlin.md)
- [Multiple Documents (Compose Pager)](/guides/android/samples/multiple-documents-compose-pager-kotlin.md)
- [OCR](/guides/android/samples/ocr-kotlin.md)
- [Overlay Views](/guides/android/samples/overlay-views-kotlin.md)
- [Overlay Visibility](/guides/android/samples/overlay-visibility-kotlin.md)
- [Password Protected PDF](/guides/android/samples/password-protected-pdf-kotlin.md)
- [PDF from Image](/guides/android/samples/pdf-from-image-kotlin.md)
- [PdfFragment](/guides/android/samples/pdffragment-kotlin.md)
- [PdfUiFragment](/guides/android/samples/pdfuifragment-kotlin.md)
- [Playground](/guides/android/samples/playground-kotlin.md)
- [Popup Toolbar Customization](/guides/android/samples/popup-toolbar-customization-kotlin.md)
- [Programmatic Zoom](/guides/android/samples/programmatic-zoom-kotlin.md)
- [Reader View](/guides/android/samples/reader-view-kotlin.md)
- [Remote URL](/guides/android/samples/remote-url-kotlin.md)
- [Runtime Configuration](/guides/android/samples/runtime-configuration-kotlin.md)
- [Scientific Paper](/guides/android/samples/scientific-paper-kotlin.md)
- [Screen Reader](/guides/android/samples/screen-reader-java.md)
- [Search Indexing](/guides/android/samples/search-indexing-kotlin.md)
- [Selection Customization](/guides/android/samples/selection-customization-java.md)
- [Signature Parcelize](/guides/android/samples/signature-parcelize-kotlin.md)
- [Signature Storage Database](/guides/android/samples/signature-storage-database-kotlin.md)
- [Sound Extraction](/guides/android/samples/sound-extraction-kotlin.md)
- [Split View](/guides/android/samples/split-view-java.md)
- [Tabbed Documents](/guides/android/samples/tabbed-documents-kotlin.md)
- [Tabbed Documents (Persistent)](/guides/android/samples/tabbed-documents-persistent-kotlin.md)
- [Text Field Suggestions](/guides/android/samples/text-field-suggestions-kotlin.md)
- [Thumbnail Bar Modes](/guides/android/samples/thumbnail-bar-modes-kotlin.md)
- [Try Instant](/guides/android/samples/try-instant-kotlin.md)
- [UI View Modes](/guides/android/samples/ui-view-modes-kotlin.md)
- [Vertical Scrollbar](/guides/android/samples/vertical-scrollbar-java.md)
- [Watermarks](/guides/android/samples/watermarks-kotlin.md)
- [XFDF Import/Export](/guides/android/samples/xfdf-import-export-kotlin.md)

