android-data-layer
Use when implementing the data layer in Android or KMP — the Repository pattern's layered error-propagation model (repository as…
Use when reading, adding, updating, or removing PDF annotations (highlight, free-text, stamp) or page objects (path/text/image) with Android's platform PDF APIs, or when saving those edits back to disk — the editing surface added in API 36.1 (PdfRenderer.Page) and SDK extension
$ npx -y skills add rcosteira79/android-skills --skill pdf-annotations --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/pdf-annotationsContext preview
The summary Claude sees to decide when to auto-load this skill.
Use when reading, adding, updating, or removing PDF annotations (highlight, free-text, stamp) or page objects (path/text/image) with Android's platform PDF APIs, or when saving those edits back to disk — the editing surface added in API 36.1 (PdfRenderer.Page) and SDK extension
name: pdf-annotations description: > Use when reading, adding, updating, or removing PDF annotations (highlight, free-text, stamp) or page objects (path/text/image) with Android's platform PDF APIs, or when saving those edits back to disk — the editing surface added in API 36.1 (PdfRenderer.Page) and SDK extension 18 (PdfRendererPreV.Page on API 31+), android.graphics.pdf.component. Covers the version gate, the open-edit-write workflow, and the id/render-flag/save contracts. Not for display-only PdfViewerFragment work, PdfDocument creation-from-scratch, form filling, or third-party PDF SDKs (iText, PDFBox, PSPDFKit).
Android can natively edit PDF annotations since API 36.1 — before that the platform story was render-only and the docs still say so: the Jetpack viewer guide's PDF surface is `PdfViewerFragment`, which explicitly does **not** support edit mode and hands annotation off to other apps via an `android.intent.action.ANNOTATE` intent [kb://android/develop/ui/views/layout/pdf/implement-pdf-viewer]. Do not conclude "use a third-party library" for annotation work before applying the version gate below.
Two parallel renderers expose the same editing surface (`android.graphics.pdf.component`):
| Entry point | Editing available | Device requirement | |---|---|---| | `PdfRenderer.Page` | API 36.1+ | platform release | | `PdfRendererPreV.Page` | SDK extension 18 | API 31+ with the PDF module updated (PreV itself needs extension 13) |
Gate at runtime with `SdkExtensions.getExtensionVersion(Build.VERSION_CODES.S)` [kb://android/guide/sdk-extensions/index]. Extension 18 reaches back to API 31 via mainline updates, so `PdfRendererPreV` is the branch that covers most devices.
// The fd must be seekable — pipes/sockets are rejected at construction.
ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY).use { fd ->
PdfRenderer(fd).use { renderer -> // or PdfRendererPreV on the ext-18 branch
renderer.openPage(0).use { page ->
// Only SUPPORTED annotation types are listed. The pairs are
// android.util.Pair — NO Kotlin destructuring (no component1/2);
// read them with .first / .second.
val existing = page.getPageAnnotations()
val staleId: Int? = existing.firstOrNull { it.second is FreeTextAnnotation }?.first
val highlight = HighlightAnnotation(boundsList) // List<RectF> quads
val id = page.addPageAnnotation(highlight)
check(id != -1) { "annotation rejected" } // add returns -1, it does NOT throw
highlight.color = newColor
page.updatePageAnnotation(id, highlight) // by id -> Boolean success
staleId?.let { page.removePageAnnotation(it) } // by id, not list position
}
// Write BEFORE close(), to a separate writable fd; the boolean is
// removePasswordProtection (IOException if true and the doc stays encrypted).
ParcelFileDescriptor.open(
dest,
ParcelFileDescriptor.MODE_READ_WRITE or ParcelFileDescriptor.MODE_CREATE,
).use { out ->
renderer.write(out, false)
}
}
}Contracts:
cannot be added — check the return; only null/`UNKNOWN`-type/already-attached arguments throw** (`IllegalArgumentException`).
"already added in this page or some other page" throws. Construct a fresh component object per add; don't reuse a removed one across pages.
returns only annotation types the API models, so a page with unsupported annotations has gaps — the `Pair.first` id is the stable handle.
("you can have only a single page opened at any given time") still applies; close the page before opening the next.
seekable fd; `IllegalStateException` after close.
destructuring (`for ((id, a) in …)`) does not compile against them; use `.first`/`.second`.
Annotation types render only when `RenderParams.renderFlags` includes them: `FLAG_RENDER_TEXT_ANNOTATIONS` / `FLAG_RENDER_HIGHLIGHT_ANNOTATIONS` (and the 36.1 additions `FLAG_RENDER_FREETEXT_ANNOTATIONS` / `FLAG_RENDER_STAMP_ANNOTATIONS`). An added stamp or free-text that "doesn't show up" in your preview bitmap is usually a missing render flag, not a failed add — re-render with the matching flags set via `RenderParams.Builder.setRenderFlags(...)` and the `render(bitmap, clip, transform, params)` overload. The render-mode constants live on **`RenderParams`** (and legacy `PdfRenderer.Page`) — `PdfRendererPreV.Page` has none, so on the PreV branch write `RenderParams.Builder(RenderParams.RENDER_MODE_FOR_DISPLAY)`; qualifying it via `PdfRendererPreV.Page` does not compile.
settable.
`PdfPageTextObject`, or `PdfPageImageObject` children (`addObject`), per the class contract.
`getPageObjects` / `updatePageObject` / `removePageObject` — same id-based contract as annotations), and carry a transform matrix (`PdfPageObject.setMatrix`/`transform`). `PdfPagePathObject` s
Android & KMP development skills for Claude Code, Codex, and Copilot CLI — architecture, Compose, coroutines, flows, networking, persistence, dependency injection, testing, debugging, and Gradle.
Use when implementing the data layer in Android or KMP — the Repository pattern's layered error-propagation model (repository as…
Use when debugging Android or KMP issues — Android-specific techniques covering Logcat, ADB, ANR traces, R8 stack trace decoding,…
Use this skill as the baseline for ALL Android and Kotlin Multiplatform (KMP) work — whenever the user mentions Android, Kotlin…
Use when setting up or refactoring Android Gradle build logic — convention plugins, composite builds, version catalogs, and…
Use when setting up or working with Retrofit in Android — service interface definitions, coroutines integration, OkHttp…
Use when needing to fetch Android source code — AOSP platform internals (@hide APIs, framework classes, system services) or…