A single, stand-alone Android app that showcases the Camera2 and CameraX APIs through a catalog of small, self-contained, Compose-first samples. Each sample focuses on one feature and reuses a shared camera-scaffolding layer, so adding a new sample means writing the feature — not the boilerplate.
The home catalog in the "Console" theme — a violet accent, monospace metadata, a bordered featured card, and a dense 2-column grid. The filter pills narrow the catalog by category, and samples are grouped CameraX-first, Camera2 below.
- Clone the repository.
- Open it in a recent Android Studio.
- Run the
appconfiguration on a device or emulator. Camera-dependent samples (extensions, slow-motion, ML) behave best on a physical device; emulators with a virtual camera cover the basics.
No Firebase / google-services.json is required.
Filter the catalog by category (Images / Video / ML / Graphics / Extensions / Controls) on the home screen. Within each view, CameraX samples are listed first and Camera2 below.
Samples that depend on optional hardware (extensions, high-speed recording, manual sensor) detect support at runtime and show a friendly "not supported on this device" state instead of crashing.
Every sample is a Gradle library module under samples/{api}-{feature}/
(package com.android.{api}.{feature}) exposing a single {Api}{Feature}Screen() composable. It
follows the layered, unidirectional pattern in android_architecture.md:
{Feature}UiState— asealed interfaceof states (Initial, feature states,Error).{Feature}ViewModel— a@HiltViewModelexposing a singleStateFlow<UiState>; no Android/lifecycle references.{Feature}Controller— a@Stablestate-holder that owns the camera SDK lifecycle, created with aremember{Feature}Controller(...)composable.{Feature}Screen— collects state withcollectAsStateWithLifecycle, wraps everything in the shared scaffold, and renders with awhen(state).
The scaffolding that used to be copy-pasted into every sample lives in three shared modules, so a new sample focuses only on its feature:
:core-theme— the design system: the Material 3 color scheme with the violet "Console" accent, Space Grotesk / Space Mono typography (via Google Fonts), and theAISampleCatalogThemewrapper.:core-camera— camera plumbing:BaseCamera2Controller(background thread, open/close, viewfinder transform, tap-to-focus, session creation),Camera2Preview/CameraXPreview,ImageUtils(Image/ImageProxy→Bitmap),MediaStoreSaver,rememberDisplayRotation(), andCameraPermissions. It re-exports the common CameraX/Camera2 libraries.:core-ui— API-agnostic Compose chrome:CameraSampleScaffold(permission flow + surface), the viewfinder HUD (FocusIndicatorreticle,RuleOfThirdsGrid,ViewfinderTitleChip,TorchChip),ShutterButton/RecordButton/CameraControlsBar/ScrimIconButton,ValueSlider/ZoomControls,CapturedImagePreview/CapturedVideoPreview/VideoPlayer, theSettingsOverlaymenu, and the loading / error / unsupported state views. It re-exports:core-cameraand:core-theme.
A sample is registered in one place — a SampleCatalogItem in
SampleCatalog.kt — and the
app's NavHost is derived from that list automatically.
Use the generator, which scaffolds a working (preview-only) compose-first module and wires it into the build and catalog:
./gradlew createSample \
-PsampleName="camera2-flash" \
-PscreenName="Camera2FlashScreen" \
-Ptitle="Camera2 • Flash" \
-Pdesc="Toggle the flash with Camera2" \
-Ptype="camera2" # camera2 (default) or camerax
./gradlew spotlessApply # format the generated filesThen implement the feature in the generated Controller / Screen and re-sync Gradle.
The project is formatted with Spotless (ktlint + Apache
license headers). Run ./gradlew spotlessApply before committing.


