How to capture images using the camera in jetpack compose
In this tutorial, I am going to explain how to capture images in a jetpack compose using a device camera. Also, I have added code to asking runtime permissions for the camera.
To get started with taking photos using the camera, First need to add camera permission on the manifest.xml
file.
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera.any"/>
Get File URI From Pathprovider¶
In the next step, need to get the file URI to store the captured image. So, we need to use the file Provider to get the file URI.
need to create a path_provider.xml
file and define the cache directory.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="my_images" path="/"/>
</paths>
After creating the path provider XML file need to declare that in the manifest.xml
file.
<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/path_provider" />
</provider>
Now, we have access to the external cache directory. Then create the file and get the URL from the file provider.
val context = LocalContext.current
val file = context.createImageFile()
val uri = FileProvider.getUriForFile(
Objects.requireNonNull(context),
BuildConfig.APPLICATION_ID + ".provider", file
)
fun Context.createImageFile(): File {
// Create an image file name
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
val imageFileName = "JPEG_" + timeStamp + "_"
val image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
externalCacheDir /* directory */
)
return image
}
Now the file is ready to store the captured image. But to launch the camera we need camera permission for the application. let's create the permission launcher to get the camera permission.
check my another tutorial to learn runtime permissions on jetpack compose.
https://www.howtodoandroid.com/runtime-permission-on-jetpack-compose/
val permissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) {
if (it) {
Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show()
cameraLauncher.launch(uri)
} else {
Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show()
}
}
Also, create the camera launcher to capture the image. Also, create mutableStateOf()
a variable to store the captured image URL. by default it will be empty. After capture, the image updates the captured image URI.
var capturedImageUri by remember {
mutableStateOf<Uri>(Uri.EMPTY)
}
val cameraLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) {
capturedImageUri = uri
}
Check the camera permission, if permission is granted call the camera launcher or launch the camera launcher.
val permissionCheckResult =
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
if (permissionCheckResult == PackageManager.PERMISSION_GRANTED) {
cameraLauncher.launch(uri)
} else {
// Request a permission
permissionLauncher.launch(Manifest.permission.CAMERA)
}
In the above code in the camera launcher, we are updating the captured image URI, So that the image will be displayed using the URL.
if (capturedImageUri.path?.isNotEmpty() == true) {
Image(
modifier = Modifier
.padding(16.dp, 8.dp),
painter = rememberImagePainter(capturedImageUri),
contentDescription = null
)
}
final code for this example,
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CaptureImageFromCameraJetpackComposeTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
AppContent()
//check permission
}
}
}
}
}
@OptIn(ExperimentalCoilApi::class)
@Composable
fun AppContent() {
val context = LocalContext.current
val file = context.createImageFile()
val uri = FileProvider.getUriForFile(
Objects.requireNonNull(context),
BuildConfig.APPLICATION_ID + ".provider", file
)
var capturedImageUri by remember {
mutableStateOf<Uri>(Uri.EMPTY)
}
val cameraLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) {
capturedImageUri = uri
}
val permissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) {
if (it) {
Toast.makeText(context, "Permission Granted", Toast.LENGTH_SHORT).show()
cameraLauncher.launch(uri)
} else {
Toast.makeText(context, "Permission Denied", Toast.LENGTH_SHORT).show()
}
}
Column(
Modifier
.fillMaxSize()
.padding(10.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(onClick = {
val permissionCheckResult =
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
if (permissionCheckResult == PackageManager.PERMISSION_GRANTED) {
cameraLauncher.launch(uri)
} else {
// Request a permission
permissionLauncher.launch(Manifest.permission.CAMERA)
}
}) {
Text(text = "Capture Image From Camera")
}
}
if (capturedImageUri.path?.isNotEmpty() == true) {
Image(
modifier = Modifier
.padding(16.dp, 8.dp),
painter = rememberImagePainter(capturedImageUri),
contentDescription = null
)
}
}
output
Thanks for reading. let me know your feedback in the comments. Also, you can download is sample source code on Github.