diff --git a/feature/homeImpl/build.gradle.kts b/feature/homeImpl/build.gradle.kts
index 37148a3..4314ace 100644
--- a/feature/homeImpl/build.gradle.kts
+++ b/feature/homeImpl/build.gradle.kts
@@ -11,4 +11,7 @@ dependencies {
implementation(projects.feature.featureAApi)
implementation(libs.bundles.exoplayer)
+ implementation(libs.bundles.camerax)
+
+ implementation(libs.glide.compose)
}
diff --git a/feature/homeImpl/src/main/AndroidManifest.xml b/feature/homeImpl/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f9618a5
--- /dev/null
+++ b/feature/homeImpl/src/main/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/HomeGraphEntry.kt b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/HomeGraphEntry.kt
index a966086..62b9464 100644
--- a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/HomeGraphEntry.kt
+++ b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/HomeGraphEntry.kt
@@ -1,10 +1,15 @@
package com.featuremodule.homeImpl
+import android.graphics.Bitmap
+import androidx.compose.runtime.getValue
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import com.featuremodule.core.navigation.HIDE_NAV_BAR
import com.featuremodule.homeApi.HomeDestination
+import com.featuremodule.homeImpl.camera.TakePhotoScreen
import com.featuremodule.homeImpl.exoplayer.ExoplayerScreen
+import com.featuremodule.homeImpl.imageUpload.ImageUploadScreen
import com.featuremodule.homeImpl.ui.HomeScreen
fun NavGraphBuilder.registerHome() {
@@ -15,12 +20,36 @@ fun NavGraphBuilder.registerHome() {
composable(InternalRoutes.ExoplayerDestination.ROUTE) {
ExoplayerScreen()
}
+
+ composable(InternalRoutes.ImageUploadDestination.ROUTE) { backStack ->
+ val bitmap by backStack.savedStateHandle
+ .getStateFlow(InternalRoutes.ImageUploadDestination.BITMAP_POP_ARG, null)
+ .collectAsStateWithLifecycle()
+ ImageUploadScreen(returnedBitmap = bitmap)
+ }
+
+ composable(InternalRoutes.TakePhotoDestination.ROUTE) {
+ TakePhotoScreen()
+ }
}
-internal sealed class InternalRoutes {
+internal class InternalRoutes {
object ExoplayerDestination {
const val ROUTE = HIDE_NAV_BAR + "exoplayer"
fun constructRoute() = ROUTE
}
+
+ object ImageUploadDestination {
+ const val ROUTE = "image_upload"
+ const val BITMAP_POP_ARG = "bitmap"
+
+ fun constructRoute() = ROUTE
+ }
+
+ object TakePhotoDestination {
+ const val ROUTE = HIDE_NAV_BAR + "take_photo"
+
+ fun constructRoute() = ROUTE
+ }
}
diff --git a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/camera/TakePhotoContract.kt b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/camera/TakePhotoContract.kt
new file mode 100644
index 0000000..680b5da
--- /dev/null
+++ b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/camera/TakePhotoContract.kt
@@ -0,0 +1,11 @@
+package com.featuremodule.homeImpl.camera
+
+import android.graphics.Bitmap
+import com.featuremodule.core.ui.UiEvent
+import com.featuremodule.core.ui.UiState
+
+internal class State : UiState
+
+internal sealed interface Event : UiEvent {
+ data class CaptureSuccess(val bitmap: Bitmap, val rotation: Int) : Event
+}
diff --git a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/camera/TakePhotoScreen.kt b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/camera/TakePhotoScreen.kt
new file mode 100644
index 0000000..d9d81f4
--- /dev/null
+++ b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/camera/TakePhotoScreen.kt
@@ -0,0 +1,100 @@
+package com.featuremodule.homeImpl.camera
+
+import androidx.camera.core.CameraSelector
+import androidx.camera.core.ImageCapture.OnImageCapturedCallback
+import androidx.camera.core.ImageProxy
+import androidx.camera.view.CameraController
+import androidx.camera.view.LifecycleCameraController
+import androidx.camera.view.PreviewView
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.windowInsetsPadding
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.content.ContextCompat
+import androidx.hilt.navigation.compose.hiltViewModel
+
+@Composable
+internal fun TakePhotoScreen(viewModel: TakePhotoVM = hiltViewModel()) {
+ val context = LocalContext.current
+ val lifecycleOwner = LocalLifecycleOwner.current
+ val previewView = remember {
+ PreviewView(context).apply {
+ scaleType = PreviewView.ScaleType.FILL_CENTER
+ }
+ }
+ val cameraController = remember {
+ LifecycleCameraController(context).apply {
+ setEnabledUseCases(CameraController.IMAGE_CAPTURE)
+ bindToLifecycle(lifecycleOwner)
+ cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
+ }
+ }
+
+ LaunchedEffect(previewView, cameraController) {
+ previewView.controller = cameraController
+ }
+
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(Color.Black)
+ .windowInsetsPadding(WindowInsets.navigationBars),
+ ) {
+ AndroidView(
+ factory = { previewView },
+ Modifier
+ .align(Alignment.Center)
+ .aspectRatio(1f)
+ .fillMaxSize(),
+ )
+
+ IconButton(
+ onClick = {
+ runCatching {
+ cameraController.takePicture(
+ ContextCompat.getMainExecutor(context),
+ object : OnImageCapturedCallback() {
+ override fun onCaptureSuccess(image: ImageProxy) {
+ viewModel.postEvent(
+ Event.CaptureSuccess(
+ image.toBitmap(),
+ image.imageInfo.rotationDegrees,
+ ),
+ )
+ image.close()
+ }
+ },
+ )
+ }
+ },
+ modifier = Modifier
+ .align(Alignment.BottomCenter)
+ .padding(bottom = 16.dp)
+ .size(50.dp),
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(MaterialTheme.colorScheme.primary, CircleShape),
+ )
+ }
+ }
+}
diff --git a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/camera/TakePhotoVM.kt b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/camera/TakePhotoVM.kt
new file mode 100644
index 0000000..7cfe96c
--- /dev/null
+++ b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/camera/TakePhotoVM.kt
@@ -0,0 +1,46 @@
+package com.featuremodule.homeImpl.camera
+
+import android.graphics.Bitmap
+import android.graphics.Matrix
+import com.featuremodule.core.navigation.NavCommand
+import com.featuremodule.core.navigation.NavManager
+import com.featuremodule.core.ui.BaseVM
+import com.featuremodule.homeImpl.InternalRoutes.ImageUploadDestination
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+
+@HiltViewModel
+internal class TakePhotoVM @Inject constructor(
+ private val navManager: NavManager,
+) : BaseVM() {
+ override fun initialState() = State()
+
+ override fun handleEvent(event: Event) {
+ when (event) {
+ is Event.CaptureSuccess -> launch {
+ val rotatedBitmap = rotateBitmap(event.bitmap, event.rotation)
+ navManager.navigate(
+ NavCommand.PopBackWithArguments(
+ mapOf(ImageUploadDestination.BITMAP_POP_ARG to rotatedBitmap),
+ ),
+ )
+ }
+ }
+ }
+
+ // Because image is not rotated by default, it only has rotation value in EXIF
+ private fun rotateBitmap(bitmap: Bitmap, rotation: Int): Bitmap {
+ val matrix = Matrix().apply {
+ postRotate(rotation.toFloat())
+ }
+ return Bitmap.createBitmap(
+ bitmap,
+ 0,
+ 0,
+ bitmap.width,
+ bitmap.height,
+ matrix,
+ true,
+ )
+ }
+}
diff --git a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/imageUpload/ImageUploadContract.kt b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/imageUpload/ImageUploadContract.kt
new file mode 100644
index 0000000..2d53add
--- /dev/null
+++ b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/imageUpload/ImageUploadContract.kt
@@ -0,0 +1,16 @@
+package com.featuremodule.homeImpl.imageUpload
+
+import android.graphics.Bitmap
+import android.net.Uri
+import com.featuremodule.core.ui.UiEvent
+import com.featuremodule.core.ui.UiState
+
+internal data class State(
+ val image: Any? = null,
+) : UiState
+
+internal sealed interface Event : UiEvent {
+ data class PhotoTaken(val bitmap: Bitmap) : Event
+ data class ImagePicked(val uri: Uri) : Event
+ data object OpenInAppCamera : Event
+}
diff --git a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/imageUpload/ImageUploadScreen.kt b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/imageUpload/ImageUploadScreen.kt
new file mode 100644
index 0000000..93eb93c
--- /dev/null
+++ b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/imageUpload/ImageUploadScreen.kt
@@ -0,0 +1,230 @@
+package com.featuremodule.homeImpl.imageUpload
+
+import android.Manifest
+import android.content.ActivityNotFoundException
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.PickVisualMediaRequest
+import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
+import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
+import androidx.activity.result.contract.ActivityResultContracts.TakePicturePreview
+import androidx.activity.result.launch
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Button
+import androidx.compose.material3.Card
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import androidx.core.content.ContextCompat
+import androidx.hilt.navigation.compose.hiltViewModel
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
+import com.bumptech.glide.integration.compose.GlideImage
+
+@Suppress("LongMethod")
+@Composable
+internal fun ImageUploadScreen(
+ returnedBitmap: Bitmap?,
+ viewModel: ImageUploadVM = hiltViewModel(),
+) {
+ val context = LocalContext.current
+ val state by viewModel.state.collectAsStateWithLifecycle()
+ var permissionNotGrantedVisibility by remember { mutableStateOf(false) }
+ var cameraNotFoundVisibility by remember { mutableStateOf(false) }
+
+ val launchSystemCamera = rememberLauncherForActivityResult(TakePicturePreview()) { bitmap ->
+ bitmap?.let { viewModel.postEvent(Event.PhotoTaken(it)) }
+ }
+ val launchSystemCameraPermissionRequest =
+ rememberLauncherForActivityResult(RequestPermission()) { isGranted ->
+ if (isGranted) {
+ try {
+ launchSystemCamera.launch()
+ } catch (_: ActivityNotFoundException) {
+ cameraNotFoundVisibility = true
+ }
+ } else {
+ permissionNotGrantedVisibility = true
+ }
+ }
+ val launchImagePicker = rememberLauncherForActivityResult(PickVisualMedia()) { uri ->
+ uri?.let { viewModel.postEvent(Event.ImagePicked(it)) }
+ }
+ val launchInAppCameraPermissionRequest =
+ rememberLauncherForActivityResult(RequestPermission()) { isGranted ->
+ if (isGranted) {
+ viewModel.postEvent(Event.OpenInAppCamera)
+ } else {
+ permissionNotGrantedVisibility = true
+ }
+ }
+
+ LaunchedEffect(returnedBitmap) {
+ returnedBitmap?.let {
+ viewModel.postEvent(Event.PhotoTaken(it))
+ }
+ }
+
+ Box {
+ ImageUploadScreen(
+ state = state,
+ launchSystemCamera = {
+ if (ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.CAMERA,
+ ) == PackageManager.PERMISSION_DENIED
+ ) {
+ launchSystemCameraPermissionRequest.launch(Manifest.permission.CAMERA)
+ } else {
+ try {
+ launchSystemCamera.launch()
+ } catch (_: ActivityNotFoundException) {
+ cameraNotFoundVisibility = true
+ }
+ }
+ },
+ launchImagePicker = {
+ launchImagePicker.launch(PickVisualMediaRequest(PickVisualMedia.ImageOnly))
+ },
+ launchCamera = {
+ if (ContextCompat.checkSelfPermission(
+ context,
+ Manifest.permission.CAMERA,
+ ) == PackageManager.PERMISSION_DENIED
+ ) {
+ launchInAppCameraPermissionRequest.launch(Manifest.permission.CAMERA)
+ } else {
+ viewModel.postEvent(Event.OpenInAppCamera)
+ }
+ },
+ )
+
+ PermissionNotGrantedDialog(
+ isVisible = permissionNotGrantedVisibility,
+ onDismiss = { permissionNotGrantedVisibility = false },
+ )
+
+ CameraNotFoundDialog(
+ isVisible = cameraNotFoundVisibility,
+ onDismiss = { cameraNotFoundVisibility = false },
+ )
+ }
+}
+
+@OptIn(ExperimentalGlideComposeApi::class)
+@Composable
+private fun ImageUploadScreen(
+ state: State,
+ launchSystemCamera: () -> Unit,
+ launchImagePicker: () -> Unit,
+ launchCamera: () -> Unit,
+) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .verticalScroll(
+ rememberScrollState(),
+ ),
+ contentAlignment = Alignment.Center,
+ ) {
+ Column(modifier = Modifier.width(200.dp)) {
+ GlideImage(
+ model = state.image,
+ contentDescription = null,
+ modifier = Modifier
+ .padding(vertical = 16.dp)
+ .size(200.dp),
+ contentScale = ContentScale.Crop,
+ )
+
+ @Composable
+ fun GenericButton(text: String, onClick: () -> Unit) {
+ Button(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onClick,
+ ) {
+ Text(text = text)
+ }
+ }
+
+ GenericButton(text = "Open camera app") { launchSystemCamera() }
+ GenericButton(text = "Open image picker") { launchImagePicker() }
+ GenericButton(text = "Open in-app camera") { launchCamera() }
+ }
+ }
+}
+
+@Composable
+private fun PermissionNotGrantedDialog(isVisible: Boolean, onDismiss: () -> Unit) {
+ if (!isVisible) return
+
+ Dialog(onDismissRequest = onDismiss) {
+ Card {
+ Column(
+ modifier = Modifier
+ .padding(16.dp)
+ .width(IntrinsicSize.Max),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Text(
+ text = "Permission was not granted",
+ fontWeight = FontWeight.SemiBold,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Button(onClick = onDismiss, modifier = Modifier.fillMaxWidth()) {
+ Text(text = "Close")
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun CameraNotFoundDialog(isVisible: Boolean, onDismiss: () -> Unit) {
+ if (!isVisible) return
+
+ Dialog(onDismissRequest = onDismiss) {
+ Card {
+ Column(
+ modifier = Modifier
+ .padding(16.dp)
+ .width(IntrinsicSize.Max),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Text(
+ text = "Camera app was not found",
+ fontWeight = FontWeight.SemiBold,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ Spacer(modifier = Modifier.height(16.dp))
+ Button(onClick = onDismiss, modifier = Modifier.fillMaxWidth()) {
+ Text(text = "Close")
+ }
+ }
+ }
+ }
+}
diff --git a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/imageUpload/ImageUploadVM.kt b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/imageUpload/ImageUploadVM.kt
new file mode 100644
index 0000000..8f1fea8
--- /dev/null
+++ b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/imageUpload/ImageUploadVM.kt
@@ -0,0 +1,27 @@
+package com.featuremodule.homeImpl.imageUpload
+
+import com.featuremodule.core.navigation.NavCommand
+import com.featuremodule.core.navigation.NavManager
+import com.featuremodule.core.ui.BaseVM
+import com.featuremodule.homeImpl.InternalRoutes
+import dagger.hilt.android.lifecycle.HiltViewModel
+import javax.inject.Inject
+
+@HiltViewModel
+internal class ImageUploadVM @Inject constructor(
+ private val navManager: NavManager,
+) : BaseVM() {
+ override fun initialState() = State()
+
+ override fun handleEvent(event: Event) {
+ when (event) {
+ is Event.PhotoTaken -> setState { copy(image = event.bitmap) }
+ is Event.ImagePicked -> setState { copy(image = event.uri) }
+ Event.OpenInAppCamera -> launch {
+ navManager.navigate(
+ NavCommand.Forward(InternalRoutes.TakePhotoDestination.constructRoute()),
+ )
+ }
+ }
+ }
+}
diff --git a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeContract.kt b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeContract.kt
index e5bc0c6..3892fac 100644
--- a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeContract.kt
+++ b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeContract.kt
@@ -8,4 +8,5 @@ internal data object State : UiState
internal sealed interface Event : UiEvent {
data object NavigateToFeatureA : Event
data object NavigateToExoplayer : Event
+ data object NavigateToCamera : Event
}
diff --git a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeScreen.kt b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeScreen.kt
index 2b7a3e5..ec64729 100644
--- a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeScreen.kt
+++ b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeScreen.kt
@@ -1,11 +1,10 @@
package com.featuremodule.homeImpl.ui
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
-import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.Text
@@ -25,21 +24,21 @@ internal fun HomeScreen(route: String?, viewModel: HomeVM = hiltViewModel()) {
Column(
modifier = Modifier.width(IntrinsicSize.Max),
+ verticalArrangement = Arrangement.spacedBy(24.dp),
) {
- Button(
- modifier = Modifier.fillMaxWidth(),
- onClick = { viewModel.postEvent(Event.NavigateToFeatureA) },
- ) {
- Text(text = "Pass number")
+ @Composable
+ fun GenericButton(text: String, onClick: () -> Unit) {
+ Button(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = onClick,
+ ) {
+ Text(text = text)
+ }
}
- Spacer(modifier = Modifier.height(24.dp))
- Button(
- modifier = Modifier.fillMaxWidth(),
- onClick = { viewModel.postEvent(Event.NavigateToExoplayer) },
- ) {
- Text(text = "Exoplayer")
- }
+ GenericButton(text = "Pass number") { viewModel.postEvent(Event.NavigateToFeatureA) }
+ GenericButton(text = "Exoplayer") { viewModel.postEvent(Event.NavigateToExoplayer) }
+ GenericButton(text = "Camera") { viewModel.postEvent(Event.NavigateToCamera) }
}
}
}
diff --git a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeVM.kt b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeVM.kt
index e274dd8..afe3d54 100644
--- a/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeVM.kt
+++ b/feature/homeImpl/src/main/java/com/featuremodule/homeImpl/ui/HomeVM.kt
@@ -33,6 +33,12 @@ internal class HomeVM @Inject constructor(
NavCommand.Forward(InternalRoutes.ExoplayerDestination.constructRoute()),
)
}
+
+ Event.NavigateToCamera -> launch {
+ navManager.navigate(
+ NavCommand.Forward(InternalRoutes.ImageUploadDestination.constructRoute()),
+ )
+ }
}
}
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 4eb3191..b240345 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -23,6 +23,7 @@ collections-immutable = "0.3.7"
glide-compose = "1.0.0-beta01"
leakcanary = "2.14"
media3 = "1.4.1"
+camerax = "1.4.0"
# Versions used for android{} setup
sdk-compile = "34"
@@ -72,6 +73,11 @@ glide-compose = { module = "com.github.bumptech.glide:compose", version.ref = "g
media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3" }
media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3" }
+camerax-core = { module = "androidx.camera:camera-core", version.ref = "camerax" }
+camerax-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camerax" }
+camerax-view = { module = "androidx.camera:camera-view", version.ref = "camerax" }
+camerax-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camerax" }
+
# Testing
junit = { module = "junit:junit", version.ref = "junit" }
androidx-junit = { module = "androidx.test.ext:junit", version.ref = "junit-androidx" }
@@ -88,6 +94,7 @@ compose = ["androidx-lifecycle-runtime-compose", "compose-ui", "compose-ui-graph
"compose-ui-tooling-preview", "compose-material3", "compose-runtime"]
network = ["retrofit", "retrofit-converter-moshi", "moshi"]
exoplayer = ["media3-exoplayer", "media3-ui"]
+camerax = ["camerax-core", "camerax-camera2", "camerax-view", "camerax-lifecycle"]
[plugins]
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }