camera.png

In the world of Android development, CameraX stands as Google’s answer to the complexities of the traditional Camera API, offering a more streamlined and accessible approach for developers. Originally designed with XML in mind, it presented a challenge for those venturing into the modern declarative UI toolkit of Jetpack Compose. However, the solution is at hand with the AndroidView() composable, which acts as a bridge, seamlessly integrating CameraX with Compose. This unlocks new possibilities for camera app development in today’s Android ecosystem.

Our goal is simple: to create a camera preview, the core of any camera app, using Android Compose. We’ll explore how to effectively use CameraX with this modern UI framework, turning our vision into reality. Let’s begin!

Environment

Step 1: Setting the Stage for CameraX in Compose

Before diving into the code, the initial step involves preparing our environment for CameraX integration with Android Compose. This begins with creating an Empty Compose Project. Ensuring your project is ready to handle camera functionalities requires adding specific dependencies to your app’s build.gradle.kt file. Insert the following lines inside the dependencies block.

implementation("androidx.camera:camera-camera2:1.3.1")
implementation("androidx.camera:camera-view:1.3.1")

Since we are just dealing with the preview of the camera (feedback on what the hardware is capturing on the screen), we need only these 2 dependencies.

These dependencies are crucial for our camera preview feature:

Next, update your project’s AndroidManifest.xml to include the necessary permissions and hardware features. This step is critical for ensuring your application has the ability to access the camera hardware and perform its intended functions without security hitches.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="<http://schemas.android.com/apk/res/android>"
    xmlns:tools="<http://schemas.android.com/tools>">

    <uses-permission android:name="android.permission.CAMERA"/>
    <uses-feature android:name="android.hardware.camera" android:required="false" />

    <application
        ...

Step 2: Navigating Permissions with BaseActivity

Create the BaseActivity.kt class to streamline camera permissions management. This base activity consolidates permission handling, providing a unified and reusable approach across your app. For an in-depth guide on leveraging BaseActivity to simplify permissions management and its application in different projects, explore the resource “Android Camera Permission Essentials: Streamlining with BaseActivity.”


// Your package

import android.Manifest
import android.content.pm.PackageManager
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

// Open class allowing extension, so activities like MainActivity can inherit from it
// for common camera permission handling functionality
open class BaseActivity : ComponentActivity() {
    // Key Point: Managing Camera Permission State
    private val _isCameraPermissionGranted = MutableStateFlow(false)
    val isCameraPermissionGranted: StateFlow<Boolean> = _isCameraPermissionGranted

    // Declare a launcher for the camera permission request, handling the permission result
    private val cameraPermissionRequestLauncher: ActivityResultLauncher<String> =
        registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
            if (isGranted) {
                // Permission granted, update the state
                _isCameraPermissionGranted.value = true
            } else {
                // Permission denied: inform the user to enable it through settings
                Toast.makeText(
                    this,
                    "Go to settings and enable camera permission to use this feature",
                    Toast.LENGTH_SHORT
                ).show()
            }
        }

    // Checks camera permission and either starts the camera directly or requests permission
    fun handleCameraPermission() {
        when {
            ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.CAMERA
            ) == PackageManager.PERMISSION_GRANTED -> {
                // Permission is already granted, update the state
                _isCameraPermissionGranted.value = true
            }

            else -> {
                // Permission is not granted: request it
                cameraPermissionRequestLauncher.launch(Manifest.permission.CAMERA)
            }
        }
    }
}