Open-source project
android/camera-samples avatar
android/camera-samples

camera-samples: twenty-two small Android camera apps sharing one scaffold

Multiple samples showing the best practices in camera APIs on Android.

5,460 stars2,413 forksKotlinLicense varies

At a glance

What is it?
The Android team's camera sample app rebuilt as a Compose catalog where each feature is a Gradle library module, with the camera plumbing factored out into shared modules.
Who is it for?
The distinguishing thing about this repository is that it stopped being a pile of standalone sample projects. Because the camera plumbing, theme and common UI live in three shared modules and each feature is its own Gradle library, a reader can open one directory and see exactly what a feature adds on top of a working preview.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 57 days ago.
What is it written in?
Mainly Kotlin, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One app, a catalog, and a filter on the home screen

This is no longer a repository of separate sample projects. It is a single stand-alone Android app that showcases both the CameraX and Camera2 APIs through a catalog of small, self-contained, Compose-first samples, and each one focuses on a single feature while reusing a shared camera scaffolding layer.

The framing sentence in the README is the whole pitch: adding a new sample means writing the feature, not the boilerplate. The catalog screen groups CameraX samples first and Camera2 below, and filter pills narrow the list by category, with Images, Video, ML, Graphics, Extensions and Controls named explicitly.

The catalog table is where the value shows. Twenty-two rows cover the practical surface of the camera on a modern phone: take a photo with tap-to-focus, record video to `DCIM/Camera`, pause and resume a capture, slow motion on Camera2, 10-bit HDR video through `DynamicRange` and `DynamicRangeProfiles`, video stabilization, flipping cameras mid recording, a QR scanner and image labeling through ML Kit, a luminance meter read off the Y plane, a green screen built on concurrent cameras, Ultra HDR gain maps, live color effects via `ImageAnalysis` and `ColorMatrix`, RAW capture to DNG through `DngCreator`, zoom and torch, exposure compensation, low light boost through `CameraControl`, feature group combinations, and manual ISO, shutter and focus distance.

The tree backs that up. Under `samples/` there are directories such as `camerax-greenscreen/`, `camerax-lowlightboost/`, `camera2-rawcapture/` and `camera2-manualcontrols/`, one per feature rather than one per API.

The four layers every sample follows

Each sample is a Gradle library module under `samples/{api}-{feature}/`, with the package name `com.android.{api}.{feature}`, and it exposes exactly one composable, `{Api}{Feature}Screen()`. The layering is documented in `android_architecture.md` and it is a unidirectional pattern with four named pieces.

`{Feature}UiState` is a sealed interface of states, covering Initial, the feature states and Error. `{Feature}ViewModel` is a `@HiltViewModel` exposing a single `StateFlow<UiState>` and holding no Android or lifecycle references, which is what makes it testable. `{Feature}Controller` is a `@Stable` state holder that owns the camera SDK lifecycle and is created with a `remember{Feature}Controller(...)` composable. `{Feature}Screen` collects state with `collectAsStateWithLifecycle`, wraps itself in the shared scaffold and renders with a `when(state)`.

The controller owning the SDK lifecycle is the interesting decision. Camera2 in particular has an open, configure and close dance with a background thread that has to be right, and putting it behind a state holder rather than in the composable means the screen is a pure function of state. For anyone who has fought a camera teardown in a Compose app, that separation is the part of this repo worth stealing.

Each sample also detects optional hardware at runtime, showing a friendly not-supported state rather than crashing, which matters because several rows in the catalog depend on hardware most emulators do not have.

Three shared modules holding what used to be copy-pasted

The scaffolding lives in three modules at the repository root, and reading their descriptions is the fastest way to understand the codebase.

`:core-theme` is the design system: a Material 3 color scheme with a violet Console accent, Space Grotesk and Space Mono typography pulled from Google Fonts, and an `AISampleCatalogTheme` wrapper. `:core-camera` is the plumbing, containing `BaseCamera2Controller` with its background thread, open and close, viewfinder transform, tap-to-focus and session creation, plus `Camera2Preview` and `CameraXPreview`, `ImageUtils` for converting `Image` and `ImageProxy` to `Bitmap`, `MediaStoreSaver`, `rememberDisplayRotation()` and `CameraPermissions`.

`:core-ui` is the API-agnostic Compose chrome: `CameraSampleScaffold` handling the permission flow and surface, a viewfinder HUD with `FocusIndicator`, `RuleOfThirdsGrid`, `ViewfinderTitleChip` and `TorchChip`, the button set (`ShutterButton`, `RecordButton`, `CameraControlsBar`, `ScrimIconButton`), `ValueSlider` and `ZoomControls`, the preview and player components, a `SettingsOverlay` menu, and the loading, error and unsupported views. It re-exports both `:core-camera` and `:core-theme`, so a sample module depends on one thing.

That re-export chain is a small design choice with a real payoff: a sample that imports `:core-ui` gets the camera libraries, the theme and the widgets without listing five dependencies.

Registration is one entry, and the NavHost follows

Adding a sample to the app happens in exactly one place: a `SampleCatalogItem` in `app/src/main/java/com/android/camera/catalog/domain/SampleCatalog.kt`. The app's `NavHost` is derived from that list automatically, so there is no separate navigation file to keep in sync.

The sample count in the table and the directory count in the tree are close but not identical, which is the kind of small drift worth noticing when reading any repository. Among the sample directories are `camerax-media3effects/`, `camerax-slowmotion/`, `camera2-hdrviewfinder/` and `camerax-imagelabeling/`, none of which has its own row in the table shown in the README.

For running it, the instructions are three steps: clone the repository, open it in a recent Android Studio, and run the `app` configuration on a device or emulator. The README is candid that camera-dependent samples such as extensions, slow motion and ML behave best on physical hardware, with emulators and their virtual camera covering the basics. One practical note in its favour: no Firebase and no `google-services.json` are required, which removes the most common reason a sample app will not build on a fresh clone.

The generator writes the module, then you write the feature

There is a Gradle task that scaffolds a working, preview-only module and wires it into both the build and the catalog.

bash
./gradlew createSample \
  -PsampleName="camera2-flash" \
  -PscreenName="Camera2FlashScreen" \
  -Ptitle="Camera2 • Flash" \
  -Pdesc="Toggle the flash with Camera2" \
  -Ptype="camera2"          # camera2 (default) or camerax

Five properties, and the last one picks the API, defaulting to camera2. After running it you implement the feature in the generated `Controller` and `Screen` and re-sync Gradle.

Formatting is handled by Spotless, configured with ktlint and Apache license headers, in a `spotless/` directory at the root.

bash
./gradlew spotlessApply      # format the generated files

The license header requirement is worth flagging, since it means contributed code must carry an Apache header and running Spotless is not optional for a pull request to come out clean. An `AGENTS.md` and a `CODEOWNERS` file at the root suggest the project is maintained with agent-assisted workflows in mind, which is a reasonable signal about how actively this gets worked on. The last push to `main` was on 2026-08-10, and there are 113 open issues against it, most of which are presumably feature requests for samples that do not exist yet.

How this compares with the older camera sample repos

Google has shipped several generations of Android camera samples. The older Camera2 samples were a set of minimal activities, each self-contained and each reimplementing preview setup, focus handling and capture. They were excellent for copying a single snippet and poor as an architecture reference, because there was no shared layer and no consistent structure between them.

This repository is a deliberate consolidation. The same feature, take a photo, exists once under `camerax-takeaphoto/` and once under `camera2-takeaphoto/`, side by side in the catalog, which turns the choice of API into something you can compare rather than something you infer from which sample you happened to find. Sample rows that list CameraX and Camera2 together, such as Extensions, Zoom and Torch, and the HDR video pair, exist because the table is tracking API coverage, not just features.

The CameraX-first ordering is the opinionated part, and it is the right one for new work: CameraX is where the compatibility handling lives, while Camera2 is what you drop to when you need something CameraX does not expose yet. Having both in one app, behind the same scaffold and the same UI, is a more honest statement of that relationship than two separate repositories.

The trade-off is weight. A catalog of twenty-plus samples with three shared modules is a bigger thing to download and build than a folder of one-file activities, and the shared scaffolding means you cannot read a single sample top to bottom without knowing what `:core-camera` does. For a reference repository that is a fair price.

Editorial conclusion

The distinguishing thing about this repository is that it stopped being a pile of standalone sample projects. Because the camera plumbing, theme and common UI live in three shared modules and each feature is its own Gradle library, a reader can open one directory and see exactly what a feature adds on top of a working preview. That makes it a better reference than the older Camera2 samples it replaces, and better still for anyone building their own camera app, since the controller pattern it demonstrates is the part worth copying. Two things to know before browsing: the catalog is CameraX first with Camera2 below it, so the older API is a deliberate comparison rather than the default, and samples depending on real hardware detect support at runtime and show an unsupported state instead of failing. The generator task is the fastest way in.

Frequently asked questions

Does the android camera-samples app need Firebase to build?

No. The README states that no Firebase and no `google-services.json` are required, which removes the most common reason a sample app fails on a fresh clone. You need a recent Android Studio, then run the `app` configuration on a device or emulator.

Can I run the camera samples on an emulator?

For the basics, yes. The README says emulators with a virtual camera cover the fundamentals, while camera-dependent samples such as extensions, slow motion and the ML ones behave best on a physical device. Samples that depend on optional hardware detect support at runtime and show a not-supported state rather than crashing.

Should a new camera app use CameraX or Camera2?

Start with CameraX. This catalog lists CameraX samples first and Camera2 below for each feature, and includes a feature group row that queries and applies combinations such as HDR, 60fps, stabilization and Ultra HDR. Camera2 is where you go when you need an API CameraX does not expose yet, which is why both are kept in the same app behind a shared scaffold.

How do I add a new sample to the camera-samples project?

Run the `./gradlew createSample` task with a sample name, screen name, title, description and a type of camera2 or camerax. It scaffolds a working preview-only module and registers it in the build and catalog. You then fill in the feature in the generated Controller and Screen, run `./gradlew spotlessApply`, and re-sync Gradle.

What does the Controller layer in the camera samples actually own?

The `{Feature}Controller` is a `@Stable` state holder that owns the camera SDK lifecycle and is created with a `remember{Feature}Controller(...)` composable, while the ViewModel holds a plain `StateFlow<UiState>` with no Android references. Shared Camera2 work such as background threads, open and close, viewfinder transform, tap-to-focus and session creation lives in `BaseCamera2Controller` inside the `:core-camera` module.

Official sources

  1. android/camera-samples on GitHub
  2. Issues
  3. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/android-camera-samples.svg)](https://hysenlabs.com/projects/android-camera-samples)