---
title: "Screen Reader"
canonical_url: "https://www.nutrient.io/guides/android/samples/screen-reader-java/"
md_url: "https://www.nutrient.io/guides/android/samples/screen-reader-java.md"
last_updated: "2026-05-15T19:10:04.916Z"
description: "Read document text aloud and highlight each word as it is spoken."
---

# Screen Reader

Read document text aloud and highlight each word as it is spoken.

[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/)

---

```java

/*
 *   Copyright © 2016-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.java;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.os.Bundle;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.appcompat.app.AlertDialog;
import com.pspdfkit.catalog.R;
import com.pspdfkit.catalog.SdkExample;
import com.pspdfkit.configuration.activity.PdfActivityConfiguration;
import com.pspdfkit.datastructures.Range;
import com.pspdfkit.datastructures.TextBlock;
import com.pspdfkit.document.PdfDocument;
import com.pspdfkit.document.providers.AssetDataProvider;
import com.pspdfkit.ui.PdfActivity;
import com.pspdfkit.ui.PdfActivityIntentBuilder;
import com.pspdfkit.ui.PdfFragment;
import com.pspdfkit.ui.drawable.PdfDrawable;
import com.pspdfkit.ui.drawable.PdfDrawableProvider;
import com.pspdfkit.utils.TextBlockHelpersKt;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.BackpressureStrategy;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import java.text.BreakIterator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

/**
 * This example showcases how to build a screen reader. It uses Android's {@link TextToSpeech} class
 * for text synthesis while highlighting spoken text using Nutrient's drawable provider API.
 */
public class ScreenReaderExample extends SdkExample {

    public ScreenReaderExample(@NonNull Context context) {
        super(context, R.string.screenReaderExampleTitle, R.string.screenReaderExampleDescription);
    }

    @Override
    public void launchExample(@NonNull Context context, @NonNull PdfActivityConfiguration.Builder configuration) {
        // Simply loads a document from the assets. The actual screen reading is performed by the
        // activity.
        // Launch the custom example activity using the document and configuration.
        final Intent intent = PdfActivityIntentBuilder.fromDataProvider(context, new AssetDataProvider(WELCOME_DOC)).configuration(configuration.build()).activityClass(ScreenReaderExampleActivity.class).build();

        // Start the ScreenReaderExampleActivity for the extracted document.
        context.startActivity(intent);
    }

    /** Example activity using a {@link ScreenReader} to read and highlight text on a page. */
    public static class ScreenReaderExampleActivity extends PdfActivity {

        /** The screen reader encapsulates text-to-speech synthesis and screen highlighting. */
        private ScreenReader screenReader;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            // Creating the screen reader will require a callback for when initialization failed.
            // In case of a failure (usually because of a missing TTS engine) the example will be
            // stopped.
            screenReader = new ScreenReader(this, new ScreenReader.OnInitListener() {

                @Override
                public void onInitializationSucceeded() {
                    // If the document is ready upon initialization, start reading it.
                    int pageIndex = getPageIndex();
                    if (getDocument()!= null && pageIndex >= 0) {
                        screenReader.readSentencesOnPage(getDocument(), pageIndex);
                    }
                }

                @Override
                public void onInitializationFailed() {
                    new AlertDialog.Builder(ScreenReaderExampleActivity.this).setTitle("Error").setMessage(
                                    "Could not initiate text-to-speech engine. This may happen if your device does not have a local TTS "
                                            + "engine installed and is not connected to the internet.").setCancelable(false).setNeutralButton("Leave example", (dialog, which) -> finish()).show();
                }
            });

            // The screen reader provides a drawable provider that has to be registered on the fragment.
            // It will serve drawables for highlighting text on the document while it is read out loud.
            getPdfFragment().addDrawableProvider(screenReader.getDrawableProvider());
        }

        @Override
        public void onStop() {
            super.onStop();
            // When the activity goes to the background we stop any running TTS process and clean up
            // drawable providers.
            screenReader.stopReading();
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            getPdfFragment().removeDrawableProvider(screenReader.getDrawableProvider());
            screenReader.shutdown();
            screenReader = null;
        }

        @UiThread
        @Override
        public void onDocumentLoaded(@NonNull PdfDocument document) {
            // If the screen reader is initialized upon loading the document start reading on the
            // current page.
            int pageIndex = getPageIndex();
            if (screenReader.isInitialized() && pageIndex >= 0) {
                screenReader.readSentencesOnPage(document, pageIndex);
            }
        }

        /** Every time the page is changed we start reading text on that page. */
        @Override
        public void onPageChanged(@NonNull PdfDocument document, int pageIndex) {
            screenReader.readSentencesOnPage(document, pageIndex);
        }
    }

    /**
     * The {@link ScreenReader} reads sentences on a document, and provides a {@link
     * PdfDrawableProvider} for synchronously highlighting on the fragment. It parses the document and
     * creates {@link Unit} instances, that can be read and highlighted synchronously.
     *
     * <p>Use {@link #readSentencesOnPage(PdfDocument, int)} to start reading the sentences of a single

     * page.
     */
    public static class ScreenReader {

        /**
         * The Android TTS module used for text synthesis. The class will try to initialize this in the
         * constructor, creating an error if TTS initialization failed.
         */
        @NonNull
        private final TextToSpeech textToSpeech;
        /**
         * Visual padding of highlighted units. This contains a pixel value, but is initialized using
         * DP.
         */
        private final int highlightPadding;
        /**
         * A list of units that are currently spoken. The screen reader will subsequently speak out
         * these units, and highlight them synchronously. Theoretically units can be words, sentences,
         * or even letters - for simplicity this example only uses sentences.
         */
        @Nullable
        private List<Unit> spokenUnits;
        /**
         * The drawable provider serving drawables of currently spoken units. This is a very basic
         * example of a drawable provider, that is backed by a list of ready-to-serve drawables (the
         * {@link #spokenUnits}). If a document provider needs to serve drawables for many hundred

         * pages, it may create drawables on demand rather than eagerly.
         */
        @NonNull
        private final PdfDrawableProvider drawableProvider = new ScreenReaderDrawableProvider(this);
        /**
         * Since we're parsing text on a background thread using RxJava, this will keep track of such
         * operations. If {@link #stopReading()} is called before parsing has been finished, we can

         * cancel parsing operations using this.
         */
        @Nullable
        private Disposable parsingDisposable;
        /** Set to true as soon as TTS has been successfully initialized. */
        private boolean initialized = false;
        /**
         * This listener handles highlighting while a TTS is progressing. It is registered on the used
         * {@link TextToSpeech} object when starting to read, and unregistered when stopping (to prevent
         * leaks).
         */
        private UtteranceProgressListener textToSpeechProgressListener = new UtteranceProgressListener() {

            @Override
            public void onStart(String utteranceId) {
                setSpokenUnitHighlighted(utteranceId, true);
            }

            @Override
            public void onDone(String utteranceId) {
                setSpokenUnitHighlighted(utteranceId, false);
            }

            @Override
            public void onError(String utteranceId) {
                setSpokenUnitHighlighted(utteranceId, false);
            }

            @SuppressLint("CheckResult")
            private void setSpokenUnitHighlighted(@NonNull final String utteranceId, final boolean highlighted) {
                findUnitByUid(spokenUnits, utteranceId)
                        // Changing the drawable (and thus invalidating it) is only allowed from
                        // the main thread..observeOn(AndroidSchedulers.mainThread()).subscribe(unit -> unit.setHighlighted(highlighted));
            }
        };

        @Nullable
        List<PdfDrawable> getDrawablesForPage(@IntRange(from = 0) int pageIndex) {
            final List<Unit> availableDrawables = spokenUnits;
            if (availableDrawables == null) return null;
            final List<PdfDrawable> drawablesForPage = new ArrayList<>();
            for (Unit unit : availableDrawables) {
                if (unit.textBlock.pageIndex == pageIndex) {
                    drawablesForPage.add(unit);
                }
            }
            return drawablesForPage;
        }

        /**
         * Creates the {@link ScreenReader} initializing the text-to-speech engine. Once initialization
         * is done the {@link OnInitListener#onInitializationSucceeded()} method of the provided

         * listener is called. If TTS could not be initialized (e.g. because of a missing TTS engine)
         * the {@link OnInitListener#onInitializationFailed()} is called instead.

         *
         * @param context The context is required for TTS initialization.
         * @param onInitListener {@link OnInitListener} that is called on a successful initialization or
         *     in case of a failure.
         */
        public ScreenReader(@NonNull final Context context, @NonNull final OnInitListener onInitListener) {
            this.highlightPadding =
                    context.getResources().getDimensionPixelOffset(R.dimen.screenreaderexample_drawable_padding);
            this.textToSpeech = new TextToSpeech(context.getApplicationContext(), status -> {
                if (status == TextToSpeech.ERROR) {
                    initialized = false;
                    onInitListener.onInitializationFailed();
                } else {
                    initialized = true;
                    onInitListener.onInitializationSucceeded();
                }
            });
        }

        /**
         * Starts reading and highlighting sentences on the requested document page.
         *
         * @param document {@link PdfDocument} that should be read.
         * @param pageIndex Number of the document page that should be read.
         */
        public void readSentencesOnPage(@NonNull final PdfDocument document, @IntRange(from = 0) final int pageIndex) {
            stopReading();

            // Parse the document on a computational thread. Doing this on the main-thread would block
            // the UI.
            parsingDisposable = parseSentences(document, pageIndex).subscribeOn(Schedulers.computation()).toList().subscribe(this::readUnits);
        }

        /**
         * This will stop and currently running screen reading process and will remove all highlights.
         */
        public void stopReading() {
            if (!isReading()) return;

            if (parsingDisposable!= null) {
                parsingDisposable.dispose();
                parsingDisposable = null;
            }

            textToSpeech.setOnUtteranceProgressListener(null);
            textToSpeech.stop();
            spokenUnits = null;
            drawableProvider.notifyDrawablesChanged();
        }

        /**
         * Returns the drawableProvider that highlights text while it is read. Register it on the
         * fragment using {@link PdfFragment#addDrawableProvider(PdfDrawableProvider)}

         *
         * @return A {@link PdfDrawableProvider} that highlights the text, while it is read.
         */
        @NonNull
        public PdfDrawableProvider getDrawableProvider() {
            return drawableProvider;
        }

        /**
         * Returns whether this screen reader has been successfully initialized or not.
         *
         * @return {@code true} if initialized and ready to use, otherwise {@code false}.
         */
        public boolean isInitialized() {
            return initialized;
        }

        /**
         * Releases all resources. After calling this, the screen reader is no longer usable and should
         * be disposed.
         */
        public void shutdown() {
            initialized = false;
            textToSpeech.shutdown();
            spokenUnits = null;
        }

        /**
         * Returns whether a something is currently read and highlighted or not.
         *
         * @return {@code true} if something is currently read, otherwise {@code false}.
         */
        private boolean isReading() {
            return spokenUnits!= null;
        }

        private void readUnits(@NonNull final List<Unit> units) {
            // The progress of currently spoken units is followed by this listener, which will update
            // highlights accordingly.
            this.textToSpeech.setOnUtteranceProgressListener(textToSpeechProgressListener);

            spokenUnits = units;

            // We notify the drawable provider that its backing data has changed.
            drawableProvider.notifyDrawablesChanged();

            // Enqueue all sentences for TTS synthesis. Once they are read the listener registered above
            // will do the actual screen highlighting.
            for (final Unit sentence : units) {
                textToSpeech.speak(sentence.textBlock.text, TextToSpeech.QUEUE_ADD, null, sentence.uid);
            }
        }

        /**
         * Internal helper for parsing sentences on a document page. Since parsing can take time, this
         * uses an RxJava observable which allows putting the work into a computational thread.
         *
         * @param document The document providing the text to parse.
         * @param pageIndex The page number of the document page to parse.
         * @return A {@link Observable} emitting {@link Unit} objects, each containing a sentence of the
         *     parsed page.
         */
        @NonNull
        private Flowable<Unit> parseSentences(
                @NonNull final PdfDocument document, @IntRange(from = 0) final int pageIndex) {
            return Flowable.create(
                    emitter -> {
                        // This example uses a BreakIterator to speak and highlight whole sentences.
                        // It requires the document locale, to correctly find sentences.
                        final BreakIterator iterator = BreakIterator.getSentenceInstance(Locale.US);
                        iterator.setText(document.getPageText(pageIndex));

                        // Split the text into sentences and store each sentence as a readable Unit.
                        int start = iterator.first();
                        for (int end = iterator.next();
                                end!= BreakIterator.DONE &&!emitter.isCancelled();
                                start = end, end = iterator.next()) {
                            emitter.onNext(new Unit(
                                    TextBlockHelpersKt.createTextBlock(
                                            document, pageIndex, new Range(start, end - start)),
                                    highlightPadding));
                        }

                        if (!emitter.isCancelled()) {
                            emitter.onComplete();
                        }
                    },
                    BackpressureStrategy.BUFFER);
        }

        /**
         * Helper for finding a unit in a list. This uses RxJava to allow the search operation and
         * result handling on different threads.
         */
        @NonNull
        private Observable<Unit> findUnitByUid(@Nullable final List<Unit> units, @NonNull final String uid) {
            return Observable.defer(() -> {
                if (units!= null) {
                    for (Unit unit : units) {
                        if (uid.equals(unit.uid)) return Observable.just(unit);
                    }
                }

                return Observable.empty();
            });
        }

        /**
         * Initialization listener, this is used to teardown the example on devices without proper TTS
         * support.
         */
        public interface OnInitListener {
            /** Called by the {@link ScreenReader} as soon as the TTS engine was initialized. */
            void onInitializationSucceeded();

            /**
             * Called by the {@link ScreenReader} if there was an error while initializing the TTS
             * engine.
             */
            void onInitializationFailed();
        }

        /**
         * A screen reader unit is a word or a sentence that can be read via TTS and at the same time
         * get highlighted. Units are created on-demand (e.g. when asking to read a whole page). This
         * class extends the {@link PdfDrawable} which allows drawing content on top of a displayed
         * page.
         */
        private static class Unit extends PdfDrawable {

            @NonNull
            private final Paint paint = new Paint();

            @NonNull
            private final List<RectF> screenRects;

            /**
             * This is the spoken and highlighted text block on the page. It contains the text, as well
             * as the PDF coordinates. Inside {@link #updatePdfToViewTransformation(Matrix)} these

             * coordinates are converted to screen coordinates for drawing.
             */
            @NonNull
            private final TextBlock textBlock;

            /**
             * The uid is used to uniquely identify the spoken unit inside the TTS system. It will be
             * returned by the {@link UtteranceProgressListener} while speaking is performed, and allows
             * to fetch this unit and update its visual representation.
             */
            @NonNull
            private final String uid;

            private final int highlightPadding;
            private int alpha;

            private Unit(@NonNull TextBlock textBlock, final int highlightPadding) {
                this.textBlock = textBlock;
                this.highlightPadding = highlightPadding;
                this.uid = Integer.toHexString(textBlock.hashCode());

                final List<RectF> screenRects = new ArrayList<>(textBlock.pageRects.size());
                for (int i = 0; i < textBlock.pageRects.size(); i++) screenRects.add(new RectF());
                this.screenRects = Collections.unmodifiableList(screenRects);

                // The drawable will paint a light yellow rect via multiplication on top of the
                // highlighted text.
                paint.setColor(Color.parseColor("#FDF4B9"));

                paint.setStyle(Paint.Style.FILL);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    paint.setBlendMode(BlendMode.MULTIPLY);
                } else {
                    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
                }
                alpha = 0;
                paint.setAlpha(0);
            }

            /**
             * This method enables or disables visual highlighting of this unit.
             *
             * @param highlighted {@code true} to enable visual highlighting, or {@code false} to
             *     disable it.
             */
            @UiThread
            private void setHighlighted(final boolean highlighted) {
                setAlpha(highlighted? 255 : 0);
            }

            /**
             * Every time this method is called, Nutrient provides a fresh transformation matrix that
             * holds the current PDF-to-view transformation. Using the matrix the drawable can convert
             * PDF coordinates to view/screen coordinates. This example uses the matrix to calculate
             * screen coordinates of {@link TextBlock} instances that should be highlighted.
             */
            @Override
            public void updatePdfToViewTransformation(@NonNull Matrix matrix) {
                super.updatePdfToViewTransformation(matrix);
                for (int i = 0; i < textBlock.pageRects.size(); i++) {
                    final RectF rect = screenRects.get(i);

                    // This transforms the PDF coordinates of the text block (inside
                    // TextBlock#pageRects) to

                    // screen coordinates and stores them into another RectF.
                    matrix.mapRect(rect, textBlock.pageRects.get(i));

                    // We slightly inflate the highlighted rectangle above the text, just because it
                    // looks better.
                    rect.inset(-highlightPadding, -highlightPadding);

                    // The drawable defines its boundaries (the area where it will draw).
                    // We're rounding "outside" so that content of the drawable is not accidentally
                    // clipped.
                    final int l = (int) rect.left;
                    final int t = (int) rect.top;
                    final int r = (int) Math.ceil(rect.right);
                    final int b = (int) Math.ceil(rect.bottom);
                    final Rect bounds = getBounds();
                    if (i == 0) {
                        bounds.set(l, t, r, b);
                    } else {
                        bounds.union(l, t, r, b);
                    }
                    setBounds(bounds);
                }
            }

            @Override
            public void draw(Canvas canvas) {
                if (alpha == 0) return;

                // This method is called on the UI thread, and should not do any long-running
                // calculations.
                // Since the drawable has pre-calculated the screen coordinates, we can simply draw them
                // without additional computation.
                for (RectF rect : screenRects) {
                    canvas.drawRect(rect, paint);
                }
            }

            @UiThread
            @Override
            public void setAlpha(int alpha) {
                this.alpha = alpha;
                paint.setAlpha(alpha);

                // If the visual representation of a drawable changed, it can call this method to ensure
                // it's updated on-screen.
                // You can also use this self-invalidation for animating a drawable.
                invalidateSelf();
            }

            @Override
            public void setColorFilter(ColorFilter colorFilter) {
                paint.setColorFilter(colorFilter);
                invalidateSelf();
            }

            @Override
            public int getOpacity() {
                // The highlighted text lets the background shine through (it's semi-transparent). If
                // your own drawable is instead opaque,
                // and covers it's complete bounds when drawing, return PixelFormat.OPAQUE instead.
                return PixelFormat.TRANSLUCENT;
            }
        }
    }
}

```

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

---

## Related pages

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

