Access Camera and Gallery in a Jetpack Compose Android App

Charles Raj Iruthayaraj
3 min readJul 24, 2024

--

Hello Buddy’s

In this blog, we’ll explore how to access images from the gallery and use the system camera to take pictures in an Android application using Kotlin. This tutorial builds upon a previous example where we demonstrated creating a bottom navigation with a FAB (Floating Action Button). We’ll integrate camera and gallery access into this application.

Adding Permissions

First, we need to add the necessary permissions in the AndroidManifest.xml file. These permissions allow the app to access the camera and the gallery. and we’ll add the content provider authorities and file path to store the image in the content provider.

<manifest ...>
<application ...>
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>

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

Create a file_paths.xml file in the res/xml directory:

<paths>
<external-files-path name="external_files" path="." />
</paths>

Creating the Bottom Sheet

Let’s create a new Kotlin file named BottomSheets.kt where we'll define our bottom sheet for selecting camera or gallery options.

package com.example.app

import android.content.Context
import android.net.Uri
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ModalBottomSheetLayout
import androidx.compose.runtime.*
import androidx.compose.ui.res.painterResource
import androidx.core.content.FileProvider
import java.io.File

@Composable
fun CameraBottomSheet(
context: Context,
sheetState: ModalBottomSheetLayout,
onDismiss: () -> Unit,
onAction: (String) -> Unit
) {
var imageUri by remember { mutableStateOf<Uri?>(null) }
val cameraPermission = remember { mutableStateOf(false) }

Column {
Row {
IconButton(onClick = {
if (cameraPermission.value) {
imageUri = createImageFile(context)
onAction(imageUri.toString())
}
}) {
Icon(painter = painterResource(R.drawable.ic_camera), contentDescription = "Camera")
}
IconButton(onClick = {
// Launch gallery picker
}) {
Icon(painter = painterResource(R.drawable.ic_gallery), contentDescription = "Gallery")
}
}
}
}

private fun createImageFile(context: Context): Uri {
val storageDir: File? = context.getExternalFilesDir(null)
val imageFile = File.createTempFile("JPEG_${System.currentTimeMillis()}_", ".jpg", storageDir)
return FileProvider.getUriForFile(context, "${context.packageName}.provider", imageFile)
}

Implementing Camera and Gallery Launchers

In the same BottomSheets.kt file, we'll add functionality to launch the camera and gallery.

import android.content.Intent
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts

@Composable
fun CameraBottomSheet(
context: Context,
sheetState: ModalBottomSheetLayout,
onDismiss: () -> Unit,
onAction: (String) -> Unit
) {
var imageUri by remember { mutableStateOf<Uri?>(null) }
val cameraPermission = remember { mutableStateOf(false) }

val galleryLauncher = rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
uri?.let { onAction(it.toString()) }
}

val cameraLauncher = rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) { success ->
if (success) {
imageUri?.let { onAction(it.toString()) }
}
}

Column {
Row {
IconButton(onClick = {
if (cameraPermission.value) {
imageUri = createImageFile(context)
cameraLauncher.launch(imageUri)
}
}) {
Icon(painter = painterResource(R.drawable.ic_camera), contentDescription = "Camera")
}
IconButton(onClick = {
galleryLauncher.launch("image/*")
}) {
Icon(painter = painterResource(R.drawable.ic_gallery), contentDescription = "Gallery")
}
}
}
}

Adding the Bottom Sheet to the Main Activity

Now, let’s integrate the bottom sheet into the main activity.

package com.example.app

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.ModalBottomSheetLayout
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val context = LocalContext.current
var openBottomSheet by remember { mutableStateOf(false) }
val sheetState = rememberModalBottomSheetState()

if (openBottomSheet) {
ModalBottomSheetLayout(
sheetState = sheetState,
onDismissRequest = { openBottomSheet = false }
) {
CameraBottomSheet(
context = context,
sheetState = sheetState,
onDismiss = { openBottomSheet = false },
onAction = { imagePath ->
// Handle the captured image path
}
)
}
}
}
}
}

Handling Permissions

To handle permissions gracefully, add the following dependencies to your build.gradle file:

dependencies {
implementation "com.google.accompanist:accompanist-permissions:0.24.7-alpha"
}

Then, request camera permissions in your composable function.

import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberPermissionState

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraBottomSheet(
context: Context,
sheetState: ModalBottomSheetLayout,
onDismiss: () -> Unit,
onAction: (String) -> Unit
) {
var imageUri by remember { mutableStateOf<Uri?>(null) }
val cameraPermission = rememberPermissionState(android.Manifest.permission.CAMERA)

if (!cameraPermission.status.isGranted) {
LaunchedEffect(cameraPermission) {
cameraPermission.launchPermissionRequest()
}
}

// Rest of the composable function
}

Conclusion

In this blog, we’ve implemented a bottom sheet in a Kotlin Android app that allows users to access the camera and gallery. We’ve also handled necessary permissions and file storage using a content provider. This approach ensures that the app remains secure and functional across different Android versions.

Feel free to customize the UI and add additional functionality as needed. Happy coding!

--

--

Charles Raj Iruthayaraj

Experienced software developer with hands-on experience in Android Application and iOS development.