android-sdd 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +143 -0
- package/package.json +27 -0
- package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
- package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
- package/skills/Android Platform/Configuration/SKILL.md +201 -0
- package/skills/Android Platform/Filesystem/SKILL.md +216 -0
- package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
- package/skills/Android Platform/Manifest/SKILL.md +226 -0
- package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
- package/skills/Android Platform/Resources/SKILL.md +234 -0
- package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
- package/skills/Android Platform/State Restoration/SKILL.md +210 -0
- package/skills/Architecture/Bounded Context/SKILL.md +207 -0
- package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
- package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
- package/skills/Architecture/Entity Design/SKILL.md +243 -0
- package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
- package/skills/Architecture/MVI/SKILL.md +224 -0
- package/skills/Architecture/MVVM/SKILL.md +198 -0
- package/skills/Architecture/Modularization/SKILL.md +194 -0
- package/skills/Architecture/Offline First/SKILL.md +249 -0
- package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
- package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
- package/skills/Architecture/State Management/SKILL.md +229 -0
- package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
- package/skills/Architecture/Use Case Design/SKILL.md +244 -0
- package/skills/Architecture/Value Object/SKILL.md +226 -0
- package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
- package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
- package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
- package/skills/Build System/Build Cache/SKILL.md +233 -0
- package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
- package/skills/Build System/Build Variant/SKILL.md +215 -0
- package/skills/Build System/Convention Plugin/SKILL.md +288 -0
- package/skills/Build System/Dependency Management/SKILL.md +261 -0
- package/skills/Build System/Gradle/SKILL.md +284 -0
- package/skills/Build System/Incremental Build/SKILL.md +199 -0
- package/skills/Build System/KAPT/SKILL.md +198 -0
- package/skills/Build System/KSP/SKILL.md +263 -0
- package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
- package/skills/Build System/Specialized/C++/SKILL.md +308 -0
- package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
- package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
- package/skills/Build System/Version Catalog/SKILL.md +304 -0
- package/skills/Concurrency/Background Processing/SKILL.md +185 -0
- package/skills/Concurrency/Channel/SKILL.md +207 -0
- package/skills/Concurrency/Coroutine/SKILL.md +200 -0
- package/skills/Concurrency/Flow/SKILL.md +179 -0
- package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
- package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
- package/skills/Concurrency/StateFlow/SKILL.md +175 -0
- package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
- package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
- package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
- package/skills/Core Language/DSL/SKILL.md +186 -0
- package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
- package/skills/Core Language/Immutability/SKILL.md +156 -0
- package/skills/Core Language/KMP/SKILL.md +182 -0
- package/skills/Core Language/Kotlin/SKILL.md +187 -0
- package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
- package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
- package/skills/Core Language/Serialization/SKILL.md +191 -0
- package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
- package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
- package/skills/Data Layer/DAO/SKILL.md +225 -0
- package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
- package/skills/Data Layer/DataStore/SKILL.md +264 -0
- package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
- package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
- package/skills/Data Layer/File Storage/SKILL.md +247 -0
- package/skills/Data Layer/Indexing/SKILL.md +184 -0
- package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
- package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
- package/skills/Data Layer/Migration/SKILL.md +243 -0
- package/skills/Data Layer/Paging/SKILL.md +264 -0
- package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
- package/skills/Data Layer/Room/SKILL.md +244 -0
- package/skills/Data Layer/SQLite/SKILL.md +255 -0
- package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
- package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
- package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
- package/skills/Dependency Injection/Koin/SKILL.md +282 -0
- package/skills/Developer Experience/Detekt/SKILL.md +272 -0
- package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
- package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
- package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
- package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
- package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
- package/skills/Media/Audio/SKILL.md +257 -0
- package/skills/Media/Camera/SKILL.md +229 -0
- package/skills/Media/CameraX/SKILL.md +295 -0
- package/skills/Media/ExoPlayer/SKILL.md +258 -0
- package/skills/Media/Video/SKILL.md +228 -0
- package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
- package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
- package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
- package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
- package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
- package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
- package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
- package/skills/Navigation/Navigation/SKILL.md +215 -0
- package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
- package/skills/Networking/API Contract/SKILL.md +220 -0
- package/skills/Networking/Authentication/SKILL.md +210 -0
- package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
- package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
- package/skills/Networking/Ktor/SKILL.md +219 -0
- package/skills/Networking/Multipart Upload/SKILL.md +213 -0
- package/skills/Networking/OkHttp/SKILL.md +193 -0
- package/skills/Networking/REST/SKILL.md +178 -0
- package/skills/Networking/Rate Limiting/SKILL.md +170 -0
- package/skills/Networking/Retrofit/SKILL.md +241 -0
- package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
- package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
- package/skills/Networking/WebSocket/SKILL.md +224 -0
- package/skills/Observability/Crash Reporting/SKILL.md +219 -0
- package/skills/Observability/Logging/SKILL.md +168 -0
- package/skills/Observability/Metrics/SKILL.md +227 -0
- package/skills/Observability/Structured Logging/SKILL.md +234 -0
- package/skills/Performance/ANR Prevention/SKILL.md +192 -0
- package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
- package/skills/Performance/App Startup/SKILL.md +183 -0
- package/skills/Performance/Baseline Profile/SKILL.md +205 -0
- package/skills/Performance/Battery Optimization/SKILL.md +192 -0
- package/skills/Performance/Benchmark/SKILL.md +182 -0
- package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
- package/skills/Performance/Compose Optimization/SKILL.md +187 -0
- package/skills/Performance/Heap Management/SKILL.md +184 -0
- package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
- package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
- package/skills/Performance/Rendering Performance/SKILL.md +205 -0
- package/skills/Performance/Startup Optimization/SKILL.md +219 -0
- package/skills/Security/Biometric/SKILL.md +224 -0
- package/skills/Security/Certificate Transparency/SKILL.md +158 -0
- package/skills/Security/Cryptography/SKILL.md +244 -0
- package/skills/Security/Encrypted Storage/SKILL.md +273 -0
- package/skills/Security/Frida Detection/SKILL.md +230 -0
- package/skills/Security/Hook Detection/SKILL.md +197 -0
- package/skills/Security/Keystore/SKILL.md +272 -0
- package/skills/Security/Network Security Config/SKILL.md +186 -0
- package/skills/Security/Obfuscation/SKILL.md +226 -0
- package/skills/Security/Proguard/SKILL.md +202 -0
- package/skills/Security/R8/SKILL.md +234 -0
- package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
- package/skills/Security/Root Detection/SKILL.md +220 -0
- package/skills/Security/Secure Networking/SKILL.md +220 -0
- package/skills/System Integration/AlarmManager/SKILL.md +182 -0
- package/skills/System Integration/App Widget/SKILL.md +182 -0
- package/skills/System Integration/Deep Link/SKILL.md +187 -0
- package/skills/System Integration/Foreground Service/SKILL.md +212 -0
- package/skills/System Integration/Notification/SKILL.md +237 -0
- package/skills/System Integration/WorkManager/SKILL.md +256 -0
- package/skills/System Integration/clipboard/SKILL.md +155 -0
- package/skills/System Integration/share-intent/SKILL.md +182 -0
- package/skills/Testing/Compose Testing/SKILL.md +296 -0
- package/skills/Testing/Espresso/SKILL.md +292 -0
- package/skills/Testing/Fake Data/SKILL.md +245 -0
- package/skills/Testing/Integration Testing/SKILL.md +288 -0
- package/skills/Testing/Mocking/SKILL.md +229 -0
- package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
- package/skills/Testing/UI Testing/SKILL.md +293 -0
- package/skills/Testing/Unit Testing/SKILL.md +309 -0
- package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
- package/skills/UI System/Compose/SKILL.md +296 -0
- package/skills/UI System/Compose Animation/SKILL.md +281 -0
- package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
- package/skills/UI System/Compose Navigation/SKILL.md +255 -0
- package/skills/UI System/Compose Performance/SKILL.md +274 -0
- package/skills/UI System/Design System/SKILL.md +217 -0
- package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
- package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
- package/skills/UI System/Loading Strategy/SKILL.md +254 -0
- package/skills/UI System/Material 3/SKILL.md +279 -0
- package/skills/UI System/RTL/SKILL.md +179 -0
- package/src/index.ts +182 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: camera
|
|
3
|
+
description: >
|
|
4
|
+
Android Camera2 API usage and patterns.
|
|
5
|
+
Load this skill when accessing the camera directly via Camera2 API,
|
|
6
|
+
building custom camera experiences, or when CameraX doesn't meet requirements.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Camera (Camera2)
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
Camera2 is Android's low-level camera API providing full control over capture pipeline, exposure, focus, and output formats. It's complex but powerful. For most use cases, prefer CameraX — use Camera2 only when fine-grained control is required.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Core Principles
|
|
17
|
+
|
|
18
|
+
- Camera2 is **complex and verbose** — use CameraX unless you need its specific capabilities
|
|
19
|
+
- Camera operations must run on a **background thread** — never on main thread
|
|
20
|
+
- Always **close** `CameraDevice` and `CameraCaptureSession` when done — resource leaks crash other apps
|
|
21
|
+
- Request permissions before accessing camera — `CAMERA` is a runtime permission
|
|
22
|
+
- Handle camera **disconnection and errors** — hardware can become unavailable
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## When to Use Camera2 vs CameraX
|
|
27
|
+
|
|
28
|
+
| Need | Use |
|
|
29
|
+
|------|-----|
|
|
30
|
+
| Simple photo/video capture | CameraX |
|
|
31
|
+
| Preview + capture | CameraX |
|
|
32
|
+
| RAW capture | Camera2 |
|
|
33
|
+
| Manual exposure/ISO/shutter | Camera2 |
|
|
34
|
+
| Custom image processing pipeline | Camera2 |
|
|
35
|
+
| YUV frame access | Camera2 |
|
|
36
|
+
| Multi-camera (logical/physical) | Camera2 |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Permission
|
|
41
|
+
|
|
42
|
+
```kotlin
|
|
43
|
+
// AndroidManifest.xml
|
|
44
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
45
|
+
<uses-feature android:name="android.hardware.camera" android:required="true" />
|
|
46
|
+
|
|
47
|
+
// ✅ Request at runtime
|
|
48
|
+
val requestPermissionLauncher = registerForActivityResult(
|
|
49
|
+
ActivityResultContracts.RequestPermission()
|
|
50
|
+
) { isGranted ->
|
|
51
|
+
if (isGranted) openCamera()
|
|
52
|
+
else showPermissionDeniedMessage()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Camera Manager — Enumerate Cameras
|
|
61
|
+
|
|
62
|
+
```kotlin
|
|
63
|
+
// ✅ List available cameras
|
|
64
|
+
val cameraManager = context.getSystemService(CameraManager::class.java)
|
|
65
|
+
|
|
66
|
+
val cameraIds = cameraManager.cameraIdList
|
|
67
|
+
cameraIds.forEach { id ->
|
|
68
|
+
val characteristics = cameraManager.getCameraCharacteristics(id)
|
|
69
|
+
val facing = characteristics.get(CameraCharacteristics.LENS_FACING)
|
|
70
|
+
when (facing) {
|
|
71
|
+
CameraCharacteristics.LENS_FACING_BACK -> Log.d("Camera", "Back camera: $id")
|
|
72
|
+
CameraCharacteristics.LENS_FACING_FRONT -> Log.d("Camera", "Front camera: $id")
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ✅ Find back camera
|
|
77
|
+
fun findBackCamera(manager: CameraManager): String? {
|
|
78
|
+
return manager.cameraIdList.firstOrNull { id ->
|
|
79
|
+
manager.getCameraCharacteristics(id)
|
|
80
|
+
.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Opening Camera
|
|
88
|
+
|
|
89
|
+
```kotlin
|
|
90
|
+
// ✅ Open camera on background thread
|
|
91
|
+
private lateinit var cameraDevice: CameraDevice
|
|
92
|
+
private val cameraThread = HandlerThread("CameraThread").also { it.start() }
|
|
93
|
+
private val cameraHandler = Handler(cameraThread.looper)
|
|
94
|
+
|
|
95
|
+
@SuppressLint("MissingPermission")
|
|
96
|
+
fun openCamera(cameraId: String) {
|
|
97
|
+
cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {
|
|
98
|
+
override fun onOpened(camera: CameraDevice) {
|
|
99
|
+
cameraDevice = camera
|
|
100
|
+
createCaptureSession()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
override fun onDisconnected(camera: CameraDevice) {
|
|
104
|
+
camera.close()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
override fun onError(camera: CameraDevice, error: Int) {
|
|
108
|
+
camera.close()
|
|
109
|
+
// Handle error — show user message
|
|
110
|
+
}
|
|
111
|
+
}, cameraHandler)
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Capture Session and Preview
|
|
118
|
+
|
|
119
|
+
```kotlin
|
|
120
|
+
// ✅ Create capture session for preview
|
|
121
|
+
private lateinit var captureSession: CameraCaptureSession
|
|
122
|
+
|
|
123
|
+
fun createCaptureSession(surface: Surface) {
|
|
124
|
+
val targets = listOf(surface)
|
|
125
|
+
|
|
126
|
+
cameraDevice.createCaptureSession(
|
|
127
|
+
targets,
|
|
128
|
+
object : CameraCaptureSession.StateCallback() {
|
|
129
|
+
override fun onConfigured(session: CameraCaptureSession) {
|
|
130
|
+
captureSession = session
|
|
131
|
+
startPreview(surface)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
override fun onConfigureFailed(session: CameraCaptureSession) {
|
|
135
|
+
// Handle configuration failure
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
cameraHandler
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ✅ Start repeating preview request
|
|
143
|
+
fun startPreview(surface: Surface) {
|
|
144
|
+
val previewRequest = cameraDevice
|
|
145
|
+
.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
|
|
146
|
+
.apply {
|
|
147
|
+
addTarget(surface)
|
|
148
|
+
set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
|
|
149
|
+
set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON)
|
|
150
|
+
}
|
|
151
|
+
.build()
|
|
152
|
+
|
|
153
|
+
captureSession.setRepeatingRequest(previewRequest, null, cameraHandler)
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Capture Photo
|
|
160
|
+
|
|
161
|
+
```kotlin
|
|
162
|
+
// ✅ Capture still image
|
|
163
|
+
fun capturePhoto(imageReader: ImageReader) {
|
|
164
|
+
val captureRequest = cameraDevice
|
|
165
|
+
.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
|
|
166
|
+
.apply {
|
|
167
|
+
addTarget(imageReader.surface)
|
|
168
|
+
set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
|
|
169
|
+
set(CaptureRequest.JPEG_ORIENTATION, getJpegOrientation())
|
|
170
|
+
}
|
|
171
|
+
.build()
|
|
172
|
+
|
|
173
|
+
captureSession.capture(captureRequest, object : CameraCaptureSession.CaptureCallback() {
|
|
174
|
+
override fun onCaptureCompleted(
|
|
175
|
+
session: CameraCaptureSession,
|
|
176
|
+
request: CaptureRequest,
|
|
177
|
+
result: TotalCaptureResult
|
|
178
|
+
) {
|
|
179
|
+
// Photo captured
|
|
180
|
+
}
|
|
181
|
+
}, cameraHandler)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ✅ Read image from ImageReader
|
|
185
|
+
imageReader.setOnImageAvailableListener({ reader ->
|
|
186
|
+
val image = reader.acquireLatestImage()
|
|
187
|
+
val buffer = image.planes[0].buffer
|
|
188
|
+
val bytes = ByteArray(buffer.remaining())
|
|
189
|
+
buffer.get(bytes)
|
|
190
|
+
image.close() // ✅ Always close image
|
|
191
|
+
savePhoto(bytes)
|
|
192
|
+
}, cameraHandler)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Cleanup
|
|
198
|
+
|
|
199
|
+
```kotlin
|
|
200
|
+
// ✅ Always release camera resources
|
|
201
|
+
fun closeCamera() {
|
|
202
|
+
captureSession.close()
|
|
203
|
+
cameraDevice.close()
|
|
204
|
+
imageReader.close()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ✅ Release in lifecycle
|
|
208
|
+
override fun onPause() {
|
|
209
|
+
super.onPause()
|
|
210
|
+
closeCamera()
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Anti-Patterns
|
|
217
|
+
|
|
218
|
+
- Camera operations on the main thread — causes ANR
|
|
219
|
+
- Not closing CameraDevice on error/disconnect — blocks other apps from using camera
|
|
220
|
+
- Not closing Image from ImageReader — buffer pool exhaustion, new images drop
|
|
221
|
+
- Ignoring orientation — photos appear rotated
|
|
222
|
+
- Not handling permission denial gracefully — crashes without permission
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Related Skills
|
|
227
|
+
- `camerax` — high-level camera API (preferred)
|
|
228
|
+
- `manifest` — camera permission declaration
|
|
229
|
+
- `coroutine` — background thread management
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: camerax
|
|
3
|
+
description: >
|
|
4
|
+
CameraX setup and usage for Android camera features.
|
|
5
|
+
Load this skill when implementing photo capture, video recording,
|
|
6
|
+
camera preview, QR scanning, or image analysis with CameraX.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# CameraX
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
CameraX is Jetpack's high-level camera library built on Camera2. It handles lifecycle management, device compatibility, and camera session complexity automatically. It's the recommended approach for most camera use cases in Android.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Core Principles
|
|
17
|
+
|
|
18
|
+
- CameraX is **lifecycle-aware** — bind to a LifecycleOwner, it handles open/close
|
|
19
|
+
- Use **use cases** to compose camera behavior — Preview, ImageCapture, VideoCapture, ImageAnalysis
|
|
20
|
+
- Maximum **3 use cases** can be active simultaneously — choose wisely
|
|
21
|
+
- CameraX handles **device compatibility** automatically — no per-device workarounds
|
|
22
|
+
- Always run image analysis on a **background executor**
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Setup
|
|
27
|
+
|
|
28
|
+
```toml
|
|
29
|
+
[versions]
|
|
30
|
+
camerax = "1.3.4"
|
|
31
|
+
|
|
32
|
+
[libraries]
|
|
33
|
+
camerax-core = { module = "androidx.camera:camera-core", version.ref = "camerax" }
|
|
34
|
+
camerax-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camerax" }
|
|
35
|
+
camerax-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camerax" }
|
|
36
|
+
camerax-video = { module = "androidx.camera:camera-video", version.ref = "camerax" }
|
|
37
|
+
camerax-view = { module = "androidx.camera:camera-view", version.ref = "camerax" }
|
|
38
|
+
camerax-extensions = { module = "androidx.camera:camera-extensions", version.ref = "camerax" }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```kotlin
|
|
42
|
+
dependencies {
|
|
43
|
+
implementation(libs.camerax.core)
|
|
44
|
+
implementation(libs.camerax.camera2)
|
|
45
|
+
implementation(libs.camerax.lifecycle)
|
|
46
|
+
implementation(libs.camerax.video)
|
|
47
|
+
implementation(libs.camerax.view)
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Preview in Compose
|
|
54
|
+
|
|
55
|
+
```kotlin
|
|
56
|
+
// ✅ CameraX Preview with Compose
|
|
57
|
+
@Composable
|
|
58
|
+
fun CameraPreview(
|
|
59
|
+
modifier: Modifier = Modifier,
|
|
60
|
+
onImageCaptured: (Uri) -> Unit
|
|
61
|
+
) {
|
|
62
|
+
val context = LocalContext.current
|
|
63
|
+
val lifecycleOwner = LocalLifecycleOwner.current
|
|
64
|
+
|
|
65
|
+
val previewView = remember { PreviewView(context) }
|
|
66
|
+
val imageCapture = remember { ImageCapture.Builder().build() }
|
|
67
|
+
|
|
68
|
+
LaunchedEffect(Unit) {
|
|
69
|
+
val cameraProvider = ProcessCameraProvider.getInstance(context).await()
|
|
70
|
+
|
|
71
|
+
val preview = Preview.Builder().build().also {
|
|
72
|
+
it.setSurfaceProvider(previewView.surfaceProvider)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
cameraProvider.unbindAll()
|
|
77
|
+
cameraProvider.bindToLifecycle(
|
|
78
|
+
lifecycleOwner,
|
|
79
|
+
CameraSelector.DEFAULT_BACK_CAMERA,
|
|
80
|
+
preview,
|
|
81
|
+
imageCapture
|
|
82
|
+
)
|
|
83
|
+
} catch (e: Exception) {
|
|
84
|
+
Log.e("CameraX", "Use case binding failed", e)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
Box(modifier = modifier) {
|
|
89
|
+
AndroidView(factory = { previewView }, modifier = Modifier.fillMaxSize())
|
|
90
|
+
|
|
91
|
+
Button(
|
|
92
|
+
onClick = { capturePhoto(context, imageCapture, onImageCaptured) },
|
|
93
|
+
modifier = Modifier.align(Alignment.BottomCenter).padding(16.dp)
|
|
94
|
+
) {
|
|
95
|
+
Text("Capture")
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Photo Capture
|
|
104
|
+
|
|
105
|
+
```kotlin
|
|
106
|
+
// ✅ Capture photo to file
|
|
107
|
+
fun capturePhoto(
|
|
108
|
+
context: Context,
|
|
109
|
+
imageCapture: ImageCapture,
|
|
110
|
+
onImageCaptured: (Uri) -> Unit
|
|
111
|
+
) {
|
|
112
|
+
val photoFile = File(
|
|
113
|
+
context.getExternalFilesDir(Environment.DIRECTORY_PICTURES),
|
|
114
|
+
"photo_${System.currentTimeMillis()}.jpg"
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
|
|
118
|
+
|
|
119
|
+
imageCapture.takePicture(
|
|
120
|
+
outputOptions,
|
|
121
|
+
ContextCompat.getMainExecutor(context),
|
|
122
|
+
object : ImageCapture.OnImageSavedCallback {
|
|
123
|
+
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
|
|
124
|
+
val savedUri = output.savedUri ?: Uri.fromFile(photoFile)
|
|
125
|
+
onImageCaptured(savedUri)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
override fun onError(exception: ImageCaptureException) {
|
|
129
|
+
Log.e("CameraX", "Photo capture failed: ${exception.message}", exception)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ✅ Capture to in-memory buffer (no file)
|
|
136
|
+
imageCapture.takePicture(
|
|
137
|
+
ContextCompat.getMainExecutor(context),
|
|
138
|
+
object : ImageCapture.OnImageCapturedCallback() {
|
|
139
|
+
override fun onCaptureSuccess(image: ImageProxy) {
|
|
140
|
+
val buffer = image.planes[0].buffer
|
|
141
|
+
val bytes = ByteArray(buffer.remaining())
|
|
142
|
+
buffer.get(bytes)
|
|
143
|
+
image.close() // ✅ Always close
|
|
144
|
+
processImage(bytes)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
override fun onError(exception: ImageCaptureException) { }
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Image Analysis (QR / ML)
|
|
155
|
+
|
|
156
|
+
```kotlin
|
|
157
|
+
// ✅ Real-time frame analysis
|
|
158
|
+
val imageAnalysis = ImageAnalysis.Builder()
|
|
159
|
+
.setTargetResolution(Size(1280, 720))
|
|
160
|
+
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
|
161
|
+
.build()
|
|
162
|
+
.also { analysis ->
|
|
163
|
+
analysis.setAnalyzer(
|
|
164
|
+
Executors.newSingleThreadExecutor()
|
|
165
|
+
) { imageProxy ->
|
|
166
|
+
analyzeFrame(imageProxy)
|
|
167
|
+
imageProxy.close() // ✅ Always close
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ✅ QR code scanning with ML Kit
|
|
172
|
+
fun analyzeFrame(imageProxy: ImageProxy) {
|
|
173
|
+
val mediaImage = imageProxy.image ?: return
|
|
174
|
+
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
|
|
175
|
+
|
|
176
|
+
barcodeScanner.process(image)
|
|
177
|
+
.addOnSuccessListener { barcodes ->
|
|
178
|
+
barcodes.firstOrNull()?.rawValue?.let { value ->
|
|
179
|
+
onQrCodeDetected(value)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
.addOnCompleteListener {
|
|
183
|
+
imageProxy.close()
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Video Capture
|
|
191
|
+
|
|
192
|
+
```kotlin
|
|
193
|
+
// ✅ Video recording
|
|
194
|
+
private var recording: Recording? = null
|
|
195
|
+
|
|
196
|
+
fun startRecording(
|
|
197
|
+
context: Context,
|
|
198
|
+
videoCapture: VideoCapture<Recorder>,
|
|
199
|
+
onVideoSaved: (Uri) -> Unit
|
|
200
|
+
) {
|
|
201
|
+
val videoFile = File(
|
|
202
|
+
context.getExternalFilesDir(Environment.DIRECTORY_MOVIES),
|
|
203
|
+
"video_${System.currentTimeMillis()}.mp4"
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
recording = videoCapture.output
|
|
207
|
+
.prepareRecording(context, FileOutputOptions.Builder(videoFile).build())
|
|
208
|
+
.withAudioEnabled()
|
|
209
|
+
.start(ContextCompat.getMainExecutor(context)) { event ->
|
|
210
|
+
when (event) {
|
|
211
|
+
is VideoRecordEvent.Finalize -> {
|
|
212
|
+
if (!event.hasError()) {
|
|
213
|
+
onVideoSaved(event.outputResults.outputUri)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
fun stopRecording() {
|
|
221
|
+
recording?.stop()
|
|
222
|
+
recording = null
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ✅ Setup VideoCapture use case
|
|
226
|
+
val recorder = Recorder.Builder()
|
|
227
|
+
.setQualitySelector(QualitySelector.from(Quality.HD))
|
|
228
|
+
.build()
|
|
229
|
+
val videoCapture = VideoCapture.withOutput(recorder)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Camera Selector
|
|
235
|
+
|
|
236
|
+
```kotlin
|
|
237
|
+
// ✅ Select camera
|
|
238
|
+
CameraSelector.DEFAULT_BACK_CAMERA // main back camera
|
|
239
|
+
CameraSelector.DEFAULT_FRONT_CAMERA // selfie camera
|
|
240
|
+
|
|
241
|
+
// ✅ Custom selector
|
|
242
|
+
val cameraSelector = CameraSelector.Builder()
|
|
243
|
+
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
|
|
244
|
+
.build()
|
|
245
|
+
|
|
246
|
+
// ✅ Switch camera
|
|
247
|
+
fun switchCamera(currentSelector: CameraSelector): CameraSelector =
|
|
248
|
+
if (currentSelector == CameraSelector.DEFAULT_BACK_CAMERA)
|
|
249
|
+
CameraSelector.DEFAULT_FRONT_CAMERA
|
|
250
|
+
else
|
|
251
|
+
CameraSelector.DEFAULT_BACK_CAMERA
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Camera Controls
|
|
257
|
+
|
|
258
|
+
```kotlin
|
|
259
|
+
// ✅ Tap to focus
|
|
260
|
+
fun tapToFocus(x: Float, y: Float, meteringPointFactory: MeteringPointFactory, camera: Camera) {
|
|
261
|
+
val point = meteringPointFactory.createPoint(x, y)
|
|
262
|
+
val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF)
|
|
263
|
+
.setAutoCancelDuration(3, TimeUnit.SECONDS)
|
|
264
|
+
.build()
|
|
265
|
+
camera.cameraControl.startFocusAndMetering(action)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ✅ Zoom
|
|
269
|
+
fun setZoom(camera: Camera, zoomRatio: Float) {
|
|
270
|
+
camera.cameraControl.setZoomRatio(zoomRatio)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ✅ Torch
|
|
274
|
+
fun setTorch(camera: Camera, enabled: Boolean) {
|
|
275
|
+
camera.cameraControl.enableTorch(enabled)
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Anti-Patterns
|
|
282
|
+
|
|
283
|
+
- Not closing `ImageProxy` in analysis — blocks future frames
|
|
284
|
+
- Binding more than 3 use cases simultaneously — exceeds hardware limits
|
|
285
|
+
- Running image analysis on the main executor — blocks UI
|
|
286
|
+
- Not handling `cameraProvider.unbindAll()` before rebinding — stale sessions
|
|
287
|
+
- Ignoring `ImageCapture.Builder.setTargetRotation()` — rotated photos
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## Related Skills
|
|
292
|
+
- `camera` — Camera2 for advanced/custom use cases
|
|
293
|
+
- `lifecycle` — binding CameraX to lifecycle
|
|
294
|
+
- `compose` — CameraX in Compose with AndroidView
|
|
295
|
+
- `manifest` — camera permission setup
|