Working with Camera
1. Requesting Camera Permission
| Permission | Description | Required in Manifest | Runtime Check |
|---|---|---|---|
| android.permission.CAMERA | Allows access to camera hardware for taking photos and recording videos | true |
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED |
Example: Check and Request Camera Permission
val CAMERA_PERMISSION = Manifest.permission.CAMERA
val PERMISSION_REQUEST_CODE = 100
if (ContextCompat.checkSelfPermission(this, CAMERA_PERMISSION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(CAMERA_PERMISSION), PERMISSION_REQUEST_CODE)
} else {
// Permission already granted, proceed to open camera
}
Example: Handle Permission Result
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
when (requestCode) {
PERMISSION_REQUEST_CODE -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted, initialize camera
} else {
// Permission denied, show rationale or disable feature
}
}
}
}
2. Using Camera Intent
| Property | Description | Example |
|---|---|---|
| Intent.ACTION_IMAGE_CAPTURE | Standard intent to capture an image using the device camera | Intent(Intent.ACTION_IMAGE_CAPTURE) |
| EXTRA_OUTPUT | Specifies the file URI where the captured image should be saved | Intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri) |
Example: Launch Camera Intent
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
val photoFile = File(externalCacheDir, "photo.jpg")
val imageUri = FileProvider.getUriForFile(this, "com.example.fileprovider", photoFile)
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
startActivityForResult(intent, REQUEST_IMAGE_CAPTURE)
Note: Ensure you have the necessary permissions in your manifest and handle runtime permissions for Android 6.0+.
3. Capturing Photos
| Property | Description | Values |
|---|---|---|
| CameraProvider | Interface to access camera functionality | androidx.camera.core.CameraProvider |
| ImageCapture | Class for capturing images from camera | androidx.camera.core.ImageCapture |
| ImageCaptureMode | Mode for image capture (e.g., JPEG, RAW) | ImageCapture.Mode |
Example: Capturing a Photo
val imageCapture = ImageCapture.Builder()
.setTargetRotation(viewFinder.display.rotation)
.build()
val photoFile = File(context.filesDir, "photo.jpg")
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture.takePicture(
outputFileOptions,
ContextCompat.getMainExecutor(context),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(output: ImageCapture.OutputFileResults) {
val savedUri = output.savedUri ?: return
Log.d("Camera", "Photo saved: $savedUri")
}
override fun onError(exception: ImageCaptureException) {
Log.e("Camera", "Photo capture failed: ${exception.message}")
}
}
)
Note: Ensure camera permissions are requested and granted before capturing photos.
Warning: ImageCapture may fail silently if the camera is not properly initialized or if permissions are missing.
4. Saving Captured Images
| Property | Description | Values |
|---|---|---|
| outputFile | File path where the captured image is saved | File("/sdcard/DCIM/camera_image.jpg") |
| format | Image format to save in | "JPEG", "PNG" |
| quality | Compression quality (0-100) | 90 |
Example: Saving Image to Gallery
val outputFile = File(context.getExternalFilesDir(null), "captured_image.jpg")
val imageCapture = ImageCapture.Builder()
.setTargetRotation(Surface.ROTATION_0)
.setFileOutputFormat(ImageCapture.FORMAT_JPEG)
.build()
val imageCaptureCallback = ImageCapture.OnImageSavedCallback { output ->
val savedUri = output.savedUri
if (savedUri != null) {
// Notify gallery about new image
val intent = Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
intent.data = savedUri
context.sendBroadcast(intent)
}
}
Note: Ensure you have
WRITE_EXTERNAL_STORAGE permission in your manifest to save images to external storage.
Warning: On Android 10+, you must use
MediaStore API for media access.
5. Using CameraX
Example: Basic CameraX Setup
import androidx.camera.core.CameraProvider
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class CameraActivity : AppCompatActivity() {
private lateinit var cameraProvider: ProcessCameraProvider
private lateinit var previewView: PreviewView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
previewView = findViewById(R.id.preview_view)
startCamera()
}
private fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().apply {
setSurfaceProvider(previewView.surfaceProvider)
}
cameraProvider.bindToLifecycle(this, CameraSelector.DEFAULT_BACK_CAMERA, preview)
}, ContextCompat.getMainExecutor(this))
}
}
| Property | Description | Values | Notes |
|---|---|---|---|
| Preview.Builder() | Creates a preview use case | Preview |
Used to display live camera feed |
| CameraSelector.DEFAULT_BACK_CAMERA | Selects the back camera | CameraSelector |
Can also use DEFAULT_FRONT_CAMERA |
| setSurfaceProvider() | Assigns the surface to display preview | SurfaceProvider |
Required for preview to render |
Note: CameraX requires
androidx.camera:camera-core, camera-lifecycle, and camera-view dependencies.Warning: CameraX is not available on all Android versions. Ensure compatibility using
CameraProvider.Key Features of CameraX
- Easy camera integration with lifecycle-aware components
- Supports both front and back cameras
- Handles camera permission requests automatically
- Provides a consistent API across device models
6. Configuring CameraX Use Cases
| Use Case | Description | Configuration | Notes |
|---|---|---|---|
| Preview | Displays live camera feed to the screen | Preview.Builder() |
Must be added to CameraProvider |
| ImageCapture | Captures still images from the camera | ImageCapture.Builder() |
Supports JPEG and RAW formats |
| VideoCapture | Records video from the camera | VideoCapture.Builder() |
Uses MediaRecorder under the hood |
Example: Setting up Preview Use Case
val preview = Preview.Builder().build()
preview.setSurfaceProvider(viewFinder.surfaceProvider)
Example: Setting up ImageCapture Use Case
val imageCapture = ImageCapture.Builder()
.setTargetRotation(viewFinder.display.rotation)
.build()
Example: Setting up VideoCapture Use Case
val videoCapture = VideoCapture.Builder()
.setTargetRotation(viewFinder.display.rotation)
.build()
7. Capturing Images with CameraX
| Property | Description | Values |
|---|---|---|
| cameraProvider | Provides access to camera devices | CameraProvider |
| imageCapture | Handles image capture operations | ImageCapture |
| previewView | Displays live camera preview | PreviewView |
Example: Setting up CameraX for image capture
import androidx.camera.core.CameraProvider
import androidx.camera.core.ImageCapture
import androidx.camera.core.PreviewView
class CameraActivity : AppCompatActivity() {
private lateinit var cameraProvider: CameraProvider
private lateinit var imageCapture: ImageCapture
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
val previewView = findViewById<PreviewView>(R.id.previewView)
imageCapture = ImageCapture.Builder().build()
cameraProvider = ProcessCameraProvider.getInstance(this).get()
}
}
Example: Capturing an image
val outputFile = File(context.filesDir, "image.jpg")
val outputOptions = ImageCapture.OutputFileOptions.Builder(outputFile).build()
imageCapture.takePicture(
outputOptions,
ContextCompat.getMainExecutor(context),
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(savedImage: ImageCapture.OutputFileResults) {
val msg = "Image saved successfully"
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
override fun onError(exception: ImageCaptureException) {
val msg = "Image capture failed: ${exception.message}"
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
}
)
Note: Ensure you have added the necessary permissions in
AndroidManifest.xml for camera access and storage.
Warning: CameraX requires Android 5.0 (API level 21) or higher.
8. Recording Video with CameraX
| Property | Description | Values |
|---|---|---|
| videoEncoder | Specifies the video encoder to use | H264, H265 |
| videoFrameRate | Frame rate for video recording | 30, 60 |
| videoBitRate | Video bit rate in bits per second | 1000000, 5000000 |
| videoSize | Resolution of the recorded video | 1920x1080, 1280x720 |
Example: Start Video Recording
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build()
val videoCapture = VideoCapture.Builder().build()
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build()
val videoCapture = VideoCapture.Builder().build()
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
this,
CameraSelector.DEFAULT_BACK_CAMERA,
preview,
videoCapture
)
val outputFile = File(filesDir, "video.mp4")
val outputOptions = VideoCapture.OutputFileOptions.Builder(outputFile).build()
videoCapture.startRecording(
outputOptions,
ContextCompat.getMainExecutor(this)
)
}, ContextCompat.getMainExecutor(this))
}, ContextCompat.getMainExecutor(this))
Note: Ensure that the app has necessary permissions for camera and storage access.
Warning: Video recording can consume significant battery and storage resources.
9. Setting Camera Parameters
| Parameter | Description | Values | Notes |
|---|---|---|---|
| cameraId | Unique identifier for the camera | 0, 1, etc. |
Use CameraManager to get available cameras |
| previewSize | Resolution of the camera preview | Size(1920, 1080) |
Must be supported by the camera |
| pictureSize | Resolution of captured images | Size(4032, 3024) |
Must be supported by the camera |
| flashMode | Flash mode for capturing images | "auto", "on", "off" |
Not all devices support flash modes |
| focusMode | Focus mode for capturing images | "auto", "macro", "continuous-picture" |
May not be supported on all devices |
Example: Setting Camera Parameters
val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
val cameraId = cameraManager.cameraIdList[0]
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
// Get supported preview sizes
val previewSizes = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
?.getOutputSizes(SurfaceTexture::class.java)
// Set preview size
val previewSize = Size(1920, 1080)
// Set picture size
val pictureSize = Size(4032, 3024)
// Set flash mode
val flashMode = CameraMetadata.FLASH_MODE_AUTO
// Set focus mode
val focusMode = CameraMetadata.LENS_FOCUS_DISTANCE
10. Handling Camera Lifecycle
| State | Description | Key Actions | Notes |
|---|---|---|---|
| INITIALIZED | Camera object created but not opened | Initialize camera parameters, set up preview surface | Can configure camera settings before opening |
| OPENED | Camera is open and ready for use | Start preview, capture frames, set up listeners | Preview must be started before capturing images |
| PREVIEW_STARTED | Preview is active and displaying frames | Adjust preview size, handle frame callbacks | Preview must be running to capture images |
| CAPTURE_STARTED | Image capture in progress | Take photo, record video, handle capture results | May block preview until capture completes |
| CLOSED | Camera is closed and resources released | Release camera resources, remove listeners | Must release all resources to prevent memory leaks |
Example: Camera Lifecycle Management
class CameraLifecycleManager {
private var cameraProvider: CameraProvider? = null
private var camera: Camera? = null
fun initializeCamera() {
// INITIALIZED state
cameraProvider = CameraProvider()
camera = cameraProvider?.createCamera()
}
fun openCamera() {
// OPENED state
camera?.open()
startPreview()
}
fun startPreview() {
// PREVIEW_STARTED state
camera?.startPreview()
}
fun captureImage() {
// CAPTURE_STARTED state
camera?.captureImage()
}
fun closeCamera() {
// CLOSED state
camera?.close()
camera = null
cameraProvider = null
}
}
Note: Always ensure camera resources are released in the
CLOSED state to prevent memory leaks and ensure proper device resource management.Warning: Failing to properly close the camera can lead to device resource exhaustion and app crashes.
11. Implementing Image Analysis
Example: Basic Image Analysis Setup
import android.graphics.Bitmap
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageAnalysisConfig
import androidx.camera.core.ImageProxy
import java.nio.ByteBuffer
class ImageAnalyzer : ImageAnalysis.Analyzer {
override fun analyze(image: ImageProxy) {
val bitmap = image.toBitmap()
// Process bitmap for analysis
val buffer = image.planes[0].buffer
val data = ByteArray(buffer.remaining())
buffer.get(data)
// Analyze image data
}
}
val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(Size(640, 480))
.build()
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(context), ImageAnalyzer())
| Property | Description | Values |
|---|---|---|
| ImageAnalysis.Builder() | Creates a new instance of ImageAnalysis | ImageAnalysis.Builder |
| setTargetResolution() | Sets the target resolution for image analysis | Size(640, 480) |
| setAnalyzer() | Sets the analyzer to process images | ImageAnalysis.Analyzer |
Note: Ensure that the camera permission is granted before using ImageAnalysis.
Warning: Image analysis can be CPU-intensive; consider using background threads for processing.
12. Switching Between Cameras
| Property | Description | Values |
|---|---|---|
| cameraId | Unique identifier for the camera | 0, 1, etc. |
| cameraFacing | Direction the camera is facing | "front", "back" |
Example: Switching Camera
val cameraProvider = CameraProviderManager.getInstance(context)
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val cameraProviderFuture = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
// Switch to front camera
val frontCameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_FRONT)
.build()
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(lifecycleOwner, frontCameraSelector, preview)
Note: Ensure the camera is unbound before switching to avoid conflicts.