From fa54dcba1c8575161d5addb2753c47b74c9e1b62 Mon Sep 17 00:00:00 2001 From: sathwikcs <85106054+sathwikcs@users.noreply.github.com> Date: Tue, 15 Oct 2024 16:15:20 +0530 Subject: [PATCH] login screen vm , qr detect ml lit implemented ,hilt implemented ,data store implemented ,side navigation implemented --- .idea/deploymentTargetSelector.xml | 3 + app/build.gradle.kts | 28 +++ app/src/main/AndroidManifest.xml | 8 +- .../sminnovations/attendanceapp/AppClass.kt | 13 ++ .../attendanceapp/AppNavigation.kt | 40 ++++ .../attendanceapp/MainActivity.kt | 6 +- .../attendanceapp/data/dataStore/datastore.kt | 2 + .../screens/home/data/EmployeeData.kt | 3 + .../ui/components/BottomComponents.kt | 2 +- .../ui/components/TopComponents.kt | 33 ++- .../home/navigationDrawer/ui/navigation.kt | 74 +++++++ .../viewModel/navigationViewModel.kt | 4 + .../screens/home/ui/componets/QRscreen.kt | 56 +++++ .../screens/home/ui/componets/Qrscanner.kt | 100 +++++++++ .../screens/home/ui/componets/asd.kt | 2 + .../attendanceapp/screens/home/ui/home.kt | 39 ++++ .../screens/home/viewModel/homeVM.kt | 38 ++++ .../screens/login/data/AppModule.kt | 22 ++ .../screens/login/data/LoginDataStore.kt | 54 +++++ .../screens/login/data/loginData.kt | 6 + .../attendanceapp/screens/login/ui/login.kt | 197 +++++++++--------- .../screens/login/viewModel/loginVM.kt | 66 ++++++ .../screens/navigationDrawer/ui/navigation.kt | 61 ------ .../viewModel/navigationViewModel.kt | 4 - .../attendanceapp/ui/theme/Color.kt | 8 +- .../attendanceapp/ui/theme/Type.kt | 2 +- .../main/res/drawable/baseline_menu_24.xml | 5 + build.gradle.kts | 1 + gradle/libs.versions.toml | 4 + 29 files changed, 699 insertions(+), 182 deletions(-) create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/AppClass.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/AppNavigation.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/datastore.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/home/data/EmployeeData.kt rename app/src/main/java/in/sminnovations/attendanceapp/screens/{ => home}/navigationDrawer/ui/components/BottomComponents.kt (93%) rename app/src/main/java/in/sminnovations/attendanceapp/screens/{ => home}/navigationDrawer/ui/components/TopComponents.kt (69%) create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/ui/navigation.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/viewModel/navigationViewModel.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/QRscreen.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/Qrscanner.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/asd.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/home/viewModel/homeVM.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/AppModule.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/LoginDataStore.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/loginData.kt create mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/login/viewModel/loginVM.kt delete mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/ui/navigation.kt delete mode 100644 app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/viewModel/navigationViewModel.kt create mode 100644 app/src/main/res/drawable/baseline_menu_24.xml diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index b268ef3..fe03d1a 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -5,6 +5,9 @@ + + \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1299f51..a0c3469 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,9 @@ plugins { + kotlin("kapt") + id("com.google.dagger.hilt.android") alias(libs.plugins.android.application) alias(libs.plugins.jetbrains.kotlin.android) + } android { @@ -47,6 +50,10 @@ android { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } + // Allow references to generated code + kapt { + correctErrorTypes = true + } } dependencies { @@ -59,6 +66,8 @@ dependencies { implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) + implementation(libs.androidx.datastore.core.android) + implementation(libs.androidx.datastore.preferences.core.jvm) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) @@ -66,4 +75,23 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) + + implementation ("androidx.camera:camera-camera2:1.3.4") + implementation ( "androidx.camera:camera-lifecycle:1.3.4") + implementation ("androidx.camera:camera-view:1.3.4") + implementation ("com.google.mlkit:barcode-scanning:17.3.0") + implementation ("com.google.accompanist:accompanist-permissions:0.31.2-alpha") + + implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6") + + implementation ("androidx.navigation:navigation-compose:2.7.2") + + implementation("androidx.datastore:datastore-preferences:1.1.1") + + implementation("com.google.dagger:hilt-android:2.51.1") + kapt("com.google.dagger:hilt-android-compiler:2.51.1") + implementation("androidx.hilt:hilt-navigation-compose:1.2.0") + + val nav_version = "2.8.2" + implementation("androidx.navigation:navigation-compose:$nav_version") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b061744..c666a81 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,12 @@ - + + + + - diff --git a/app/src/main/java/in/sminnovations/attendanceapp/AppClass.kt b/app/src/main/java/in/sminnovations/attendanceapp/AppClass.kt new file mode 100644 index 0000000..ca74c74 --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/AppClass.kt @@ -0,0 +1,13 @@ +package `in`.sminnovations.attendanceapp + +// File: MyApp.kt + +import android.app.Application +import dagger.hilt.android.HiltAndroidApp + +@HiltAndroidApp +class MyApp : Application(){ + +} + + diff --git a/app/src/main/java/in/sminnovations/attendanceapp/AppNavigation.kt b/app/src/main/java/in/sminnovations/attendanceapp/AppNavigation.kt new file mode 100644 index 0000000..ac11a04 --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/AppNavigation.kt @@ -0,0 +1,40 @@ +package `in`.sminnovations.attendanceapp + +import androidx.compose.runtime.Composable +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.navigation.NavHostController +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import `in`.sminnovations.attendanceapp.screens.login.ui.LoginScreen +import `in`.sminnovations.attendanceapp.screens.home.navigationDrawer.ui.NavigationDrawer + +@Composable +fun AppNavigation() { + val navController = rememberNavController() + NavigationHost(navController = navController) +} + +@Composable +fun NavigationHost(navController: NavHostController) { + NavHost(navController = navController, startDestination = "login") { + // Login Screen Route + composable("login") { + LoginScreen( + loginViewModel = hiltViewModel(), + onLoginSuccess = { + // Navigate to the home screen after login success + navController.navigate("home") { + // Pop the login screen from the back stack + popUpTo("login") { inclusive = true } + } + } + ) + } + + + composable("home") { + NavigationDrawer() + } + } +} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/MainActivity.kt b/app/src/main/java/in/sminnovations/attendanceapp/MainActivity.kt index 184a991..d7df789 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/MainActivity.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/MainActivity.kt @@ -4,16 +4,18 @@ import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import `in`.sminnovations.attendanceapp.screens.navigationDrawer.ui.NavigationDrawer +import dagger.hilt.android.AndroidEntryPoint +import `in`.sminnovations.attendanceapp.screens.home.navigationDrawer.ui.NavigationDrawer import `in`.sminnovations.attendanceapp.ui.theme.SMIAttendanceTheme +@AndroidEntryPoint class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { SMIAttendanceTheme { - NavigationDrawer() + AppNavigation() } } } diff --git a/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/datastore.kt b/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/datastore.kt new file mode 100644 index 0000000..6cbec44 --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/data/dataStore/datastore.kt @@ -0,0 +1,2 @@ +package `in`.sminnovations.attendanceapp.data.dataStore + diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/data/EmployeeData.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/data/EmployeeData.kt new file mode 100644 index 0000000..c550d8f --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/data/EmployeeData.kt @@ -0,0 +1,3 @@ +package `in`.sminnovations.attendanceapp.screens.home.data + +class EmployeeData \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/ui/components/BottomComponents.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/ui/components/BottomComponents.kt similarity index 93% rename from app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/ui/components/BottomComponents.kt rename to app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/ui/components/BottomComponents.kt index 4fb3bb0..08e6830 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/ui/components/BottomComponents.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/ui/components/BottomComponents.kt @@ -1,4 +1,4 @@ -package `in`.sminnovations.attendanceapp.screens.navigationDrawer.ui.components +package `in`.sminnovations.attendanceapp.screens.home.navigationDrawer.ui.components import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/ui/components/TopComponents.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/ui/components/TopComponents.kt similarity index 69% rename from app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/ui/components/TopComponents.kt rename to app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/ui/components/TopComponents.kt index edccda3..1fc566a 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/ui/components/TopComponents.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/ui/components/TopComponents.kt @@ -1,13 +1,17 @@ -package `in`.sminnovations.attendanceapp.screens.navigationDrawer.ui.components +package `in`.sminnovations.attendanceapp.screens.home.navigationDrawer.ui.components import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer 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.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -23,22 +27,37 @@ import `in`.sminnovations.attendanceapp.R @Composable -fun TopComponents() { +fun TopComponents( + onclose : () -> Unit +) { Column( - modifier = Modifier.fillMaxWidth().padding(8.dp), + modifier = Modifier + .fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { - // Image with rounded corners + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Absolute.Right + ) { + IconButton( + onClick = {onclose()}, + modifier = Modifier.padding(10.dp) + ) { + Icon(painter = painterResource(id =R.drawable.baseline_close_24 ), contentDescription ="close" ) + } + + } Image( painter = painterResource(id = R.drawable.smi_logo), // replace with your image resource contentDescription = "Profile Photo", contentScale = ContentScale.Crop, - modifier = Modifier.size(100.dp).clip(RoundedCornerShape(16.dp)) + modifier = Modifier + .size(150.dp) + .clip(RoundedCornerShape(16.dp)) ) Spacer(modifier = Modifier.height(16.dp)) - // Name Text( text = "John Doe", style = MaterialTheme.typography.titleMedium, @@ -49,7 +68,6 @@ fun TopComponents() { Spacer(modifier = Modifier.height(8.dp)) - // ID Text( text = "ID: 123456", style = MaterialTheme.typography.bodyMedium, @@ -59,7 +77,6 @@ fun TopComponents() { Spacer(modifier = Modifier.height(4.dp)) - // Designation Text( text = "Software Engineer", style = MaterialTheme.typography.bodyMedium, diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/ui/navigation.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/ui/navigation.kt new file mode 100644 index 0000000..484a2d3 --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/ui/navigation.kt @@ -0,0 +1,74 @@ +package `in`.sminnovations.attendanceapp.screens.home.navigationDrawer.ui + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.DrawerValue +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.ModalDrawerSheet +import androidx.compose.material3.ModalNavigationDrawer +import androidx.compose.material3.Scaffold +import androidx.compose.material3.rememberDrawerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import `in`.sminnovations.attendanceapp.R +import `in`.sminnovations.attendanceapp.screens.home.ui.Home +import `in`.sminnovations.attendanceapp.screens.home.navigationDrawer.ui.components.BottomComponents +import `in`.sminnovations.attendanceapp.screens.home.navigationDrawer.ui.components.TopComponents +import kotlinx.coroutines.launch + +@Composable +fun NavigationDrawer() { + + val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) + val scope = rememberCoroutineScope() + + fun openClose() { + scope.launch { + drawerState.apply { + if (isClosed) open() else close() + } + } + } + ModalNavigationDrawer( + drawerState = drawerState, + drawerContent = { + ModalDrawerSheet { + Column(modifier = Modifier.clickable { openClose() }) { + TopComponents(onclose = { + scope.launch { + openClose() + } + }) + Spacer(modifier = Modifier.weight(1f)) + BottomComponents(appVersion = "1.0") + } + } + }, + ) { + Scaffold(topBar = { + Row { + IconButton( + onClick = { openClose() }, modifier = Modifier.padding(20.dp) + ) { + Icon( + painter = painterResource(id = R.drawable.baseline_menu_24), + contentDescription = "menu" + ) + } + } + }) { contentPadding -> + Column(modifier = Modifier.padding(contentPadding)) { + Home() + } + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/viewModel/navigationViewModel.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/viewModel/navigationViewModel.kt new file mode 100644 index 0000000..af45e96 --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/navigationDrawer/viewModel/navigationViewModel.kt @@ -0,0 +1,4 @@ +package `in`.sminnovations.attendanceapp.screens.home.navigationDrawer.viewModel + +class navigationViewModel { +} \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/QRscreen.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/QRscreen.kt new file mode 100644 index 0000000..5c683e5 --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/QRscreen.kt @@ -0,0 +1,56 @@ +package `in`.sminnovations.attendanceapp.screens.home.ui.componets + +import android.widget.Toast +import androidx.compose.foundation.layout.* +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.PermissionState +import com.google.accompanist.permissions.isGranted + + +@Composable +fun DefaultScreen( + qrCodeValue: String?, + onScanClick: () -> Unit, + onRetryClick: () -> Unit +) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + qrCodeValue?.let { + Text(text = "Scanned Value: $it", modifier = Modifier.padding(16.dp)) + } + Button(onClick = if (qrCodeValue == null) onScanClick else onRetryClick) { + Text(if (qrCodeValue == null) "Scan" else "Retry") + } + } +} + +@OptIn(ExperimentalPermissionsApi::class) +@Composable +fun RequestPermissions( + cameraPermissionState: PermissionState, + locationPermissionState: PermissionState +) { + val context = LocalContext.current + LaunchedEffect(Unit) { + cameraPermissionState.launchPermissionRequest() + locationPermissionState.launchPermissionRequest() + } + if (!cameraPermissionState.status.isGranted) { + Toast.makeText(context, "Camera permission is required", Toast.LENGTH_LONG).show() + } + if (!locationPermissionState.status.isGranted) { + Toast.makeText(context, "Location permission is required", Toast.LENGTH_LONG).show() + } +} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/Qrscanner.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/Qrscanner.kt new file mode 100644 index 0000000..178582f --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/Qrscanner.kt @@ -0,0 +1,100 @@ +package `in`.sminnovations.attendanceapp.screens.home.ui.componets + +import android.annotation.SuppressLint +import android.util.Log +import androidx.camera.core.* +import androidx.camera.lifecycle.ProcessCameraProvider +import androidx.camera.view.PreviewView +import androidx.compose.foundation.layout.* +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.viewinterop.AndroidView +import androidx.core.content.ContextCompat +import androidx.lifecycle.LifecycleOwner +import com.google.mlkit.vision.barcode.BarcodeScannerOptions +import com.google.mlkit.vision.barcode.BarcodeScanning +import com.google.mlkit.vision.barcode.common.Barcode +import com.google.mlkit.vision.common.InputImage + +@SuppressLint("UnsafeOptInUsageError") +@Composable +fun QRCodeScannerScreen( + onQRCodeScanned: (String) -> Unit, + lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current +) { + val context = LocalContext.current + val previewView = remember { PreviewView(context) } + + LaunchedEffect(Unit) { + val cameraProviderFuture = ProcessCameraProvider.getInstance(context) + val cameraProvider = cameraProviderFuture.get() + + val preview = Preview.Builder().build().apply { + setSurfaceProvider(previewView.surfaceProvider) + } + + val barcodeScanner = BarcodeScanning.getClient( + BarcodeScannerOptions.Builder() + .setBarcodeFormats(Barcode.FORMAT_QR_CODE) + .build() + ) + + val imageAnalyzer = ImageAnalysis.Builder() + .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) + .build() + .also { analyzer -> + analyzer.setAnalyzer(ContextCompat.getMainExecutor(context)) { imageProxy -> + processImageProxy(imageProxy, barcodeScanner) { value -> + onQRCodeScanned(value) + } + } + } + + try { + cameraProvider.unbindAll() + cameraProvider.bindToLifecycle( + lifecycleOwner, + CameraSelector.DEFAULT_BACK_CAMERA, + preview, + imageAnalyzer + ) + } catch (e: Exception) { + Log.e("QRCodeScanner", "Camera binding failed", e) + } + } + + AndroidView( + modifier = Modifier + .fillMaxWidth() + .aspectRatio(1f), + factory = { previewView } + ) +} + +@SuppressLint("UnsafeOptInUsageError") +private fun processImageProxy( + imageProxy: ImageProxy, + scanner: com.google.mlkit.vision.barcode.BarcodeScanner, + onQRCodeScanned: (String) -> Unit +) { + val mediaImage = imageProxy.image + if (mediaImage != null) { + val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) + scanner.process(image) + .addOnSuccessListener { barcodes -> + barcodes.firstOrNull()?.rawValue?.let { qrCodeValue -> + onQRCodeScanned(qrCodeValue) + } + } + .addOnFailureListener { + Log.e("QRCodeScanner", "Barcode processing failed", it) + } + .addOnCompleteListener { + imageProxy.close() + } + } else { + imageProxy.close() + } +} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/asd.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/asd.kt new file mode 100644 index 0000000..77c11d8 --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/componets/asd.kt @@ -0,0 +1,2 @@ +package `in`.sminnovations.attendanceapp.screens.home.ui.componets + diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/home.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/home.kt index 8633d99..d726f2f 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/home.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/ui/home.kt @@ -1,8 +1,47 @@ package `in`.sminnovations.attendanceapp.screens.home.ui +import android.Manifest +import androidx.compose.foundation.layout.Column +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.lifecycle.viewmodel.compose.viewModel +import com.google.accompanist.permissions.ExperimentalPermissionsApi +import com.google.accompanist.permissions.isGranted +import com.google.accompanist.permissions.rememberPermissionState +import `in`.sminnovations.attendanceapp.screens.home.ui.componets.DefaultScreen +import `in`.sminnovations.attendanceapp.screens.home.ui.componets.QRCodeScannerScreen +import `in`.sminnovations.attendanceapp.screens.home.ui.componets.RequestPermissions +import `in`.sminnovations.attendanceapp.screens.home.viewModel.HomeViewModel +@OptIn(ExperimentalPermissionsApi::class) @Composable fun Home(){ + val viewModel: HomeViewModel = viewModel() + val isScanning by viewModel.isScanning.collectAsState() + val qrCodeValue by viewModel.qrCodeValue.collectAsState() + // Request permissions + val cameraPermissionState = rememberPermissionState(Manifest.permission.CAMERA) + val locationPermissionState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION) + + // Function to check if all permissions are granted + val allPermissionsGranted = cameraPermissionState.status.isGranted && + locationPermissionState.status.isGranted + + if (!allPermissionsGranted) { + RequestPermissions( + cameraPermissionState = cameraPermissionState, + locationPermissionState = locationPermissionState + ) + } else if (isScanning) { + QRCodeScannerScreen(viewModel::setQRCodeValue) + } else { + DefaultScreen( + qrCodeValue = qrCodeValue, + onScanClick = { viewModel.startScanning() }, + onRetryClick = { viewModel.startScanning() } + ) + } } \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/home/viewModel/homeVM.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/viewModel/homeVM.kt new file mode 100644 index 0000000..681182e --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/home/viewModel/homeVM.kt @@ -0,0 +1,38 @@ +package `in`.sminnovations.attendanceapp.screens.home.viewModel + + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +class HomeViewModel : ViewModel() { + private val _qrCodeValue = MutableStateFlow(null) + val qrCodeValue: StateFlow = _qrCodeValue.asStateFlow() + + private val _isScanning = MutableStateFlow(false) + val isScanning: StateFlow = _isScanning.asStateFlow() + + fun startScanning() { + _isScanning.value = true + } + + private fun stopScanning() { + _isScanning.value = false + } + + fun setQRCodeValue(value: String) { + viewModelScope.launch { + _qrCodeValue.emit(value) + stopScanning() + } + } + + fun getEmployeeData(){ + + } +} + + diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/AppModule.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/AppModule.kt new file mode 100644 index 0000000..827f0d0 --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/AppModule.kt @@ -0,0 +1,22 @@ +package `in`.sminnovations.attendanceapp.screens.login.data + +// File: AppModule.kt + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object AppModule { + + @Provides + @Singleton + fun provideLoginDataStore(@ApplicationContext context: Context): LoginDataStore { + return LoginDataStore(context) + } +} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/LoginDataStore.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/LoginDataStore.kt new file mode 100644 index 0000000..736ba07 --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/LoginDataStore.kt @@ -0,0 +1,54 @@ +package `in`.sminnovations.attendanceapp.screens.login.data +// File: LoginDataStore.kt + +import android.content.Context +import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.preferencesDataStore +import dagger.hilt.android.qualifiers.ApplicationContext +import javax.inject.Inject +import javax.inject.Singleton +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +private const val DATASTORE_NAME = "user_prefs" +private val Context.dataStore by preferencesDataStore(name = DATASTORE_NAME) + +@Singleton +class LoginDataStore @Inject constructor(@ApplicationContext context: Context) { + + private val dataStore = context.dataStore + + private val LOGIN_ID_KEY = stringPreferencesKey("login_id") + private val PASSWORD_KEY = stringPreferencesKey("password") + private val IS_LOGGED_IN_KEY = booleanPreferencesKey("is_logged_in") + + val loginId: Flow = dataStore.data.map { preferences -> + preferences[LOGIN_ID_KEY] ?: "" + } + + val password: Flow = dataStore.data.map { preferences -> + preferences[PASSWORD_KEY] ?: "" + } + + val isLoggedIn: Flow = dataStore.data.map { preferences -> + preferences[IS_LOGGED_IN_KEY] ?: false + } + + suspend fun saveLoginData(id: String, password: String) { + dataStore.edit { preferences -> + preferences[LOGIN_ID_KEY] = id + preferences[PASSWORD_KEY] = password + preferences[IS_LOGGED_IN_KEY] = true + } + } + + suspend fun clearLoginData() { + dataStore.edit { preferences -> + preferences.remove(LOGIN_ID_KEY) + preferences.remove(PASSWORD_KEY) + preferences[IS_LOGGED_IN_KEY] = false + } + } +} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/loginData.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/loginData.kt new file mode 100644 index 0000000..8fa54fa --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/data/loginData.kt @@ -0,0 +1,6 @@ +package `in`.sminnovations.attendanceapp.screens.login.data + +data class LoginData( + val id : String ="", + val password: String = "" +) diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/login.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/login.kt index 64b04a8..8c705de 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/login.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/ui/login.kt @@ -1,13 +1,14 @@ package `in`.sminnovations.attendanceapp.screens.login.ui -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +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.text.KeyboardOptions -import androidx.compose.material.* -import androidx.compose.material.icons.Icons import androidx.compose.material3.Button import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon @@ -15,125 +16,125 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Text -import androidx.compose.material3.TextField -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.text.input.PasswordVisualTransformation -import androidx.compose.ui.text.input.TextFieldValue -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.hilt.navigation.compose.hiltViewModel import `in`.sminnovations.attendanceapp.R -import kotlinx.coroutines.delay +import `in`.sminnovations.attendanceapp.screens.login.viewModel.LoginViewModel import kotlinx.coroutines.launch - @Composable -fun LoginScreen(onLoginSuccess: () -> Unit) { +fun LoginScreen( + loginViewModel: LoginViewModel = hiltViewModel(), + onLoginSuccess: () -> Unit +) { val coroutineScope = rememberCoroutineScope() + val isLoading by loginViewModel.isLoading.collectAsState() + val isLoggedIn by loginViewModel.isLoggedIn.collectAsState() + var userId by remember { mutableStateOf(TextFieldValue("")) } var password by remember { mutableStateOf(TextFieldValue("")) } - var showLoginButton by remember { mutableStateOf(false) } - var isButtonLoading by remember { mutableStateOf(false) } var passwordVisible by remember { mutableStateOf(false) } - // Animated visibility for screen entrance - LaunchedEffect(Unit) { - delay(300) // Small delay before showing content - showLoginButton = true + // Navigate to the main screen if the user is already logged in + LaunchedEffect(isLoggedIn) { + if (isLoggedIn) onLoginSuccess() } - AnimatedVisibility( - visible = showLoginButton, - enter = fadeIn(), - exit = fadeOut() + Column( + modifier = Modifier + .fillMaxSize() + .padding(26.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { - Column( - modifier = Modifier - .fillMaxSize() - .padding(26.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text(text = "Attendance App ", style = MaterialTheme.typography.headlineLarge) - Spacer(modifier = Modifier.height(50.dp)) - Image(painter = painterResource(id = R.drawable.smi_logo), contentDescription = "Image", alignment = Alignment.TopCenter) - Spacer(modifier = Modifier.height(100.dp)) - Text(text = "Credentials", style = MaterialTheme.typography.headlineMedium) + Text(text = "Attendance App", style = MaterialTheme.typography.headlineLarge) + Spacer(modifier = Modifier.height(50.dp)) - Spacer(modifier = Modifier.height(20.dp)) + // Logo Image + Image( + painter = painterResource(id = R.drawable.smi_logo), + contentDescription = "App Logo", + alignment = Alignment.TopCenter + ) - OutlinedTextField( - value = userId, - onValueChange = { userId = it }, - textStyle = MaterialTheme.typography.bodyLarge, - label = { Text("User ID") }, - modifier = Modifier.fillMaxWidth(), - keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Next) - ) + Spacer(modifier = Modifier.height(100.dp)) + Text(text = "Credentials", style = MaterialTheme.typography.headlineMedium) - Spacer(modifier = Modifier.height(30.dp)) + Spacer(modifier = Modifier.height(20.dp)) - OutlinedTextField( - value = password, - onValueChange = { password = it }, - label = { Text("Password") }, - modifier = Modifier.fillMaxWidth(), - textStyle = MaterialTheme.typography.bodyLarge, - visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done), - trailingIcon = { - val image = if (passwordVisible) painterResource(id = R.drawable.baseline_visibility_24) else painterResource( - id = R.drawable.baseline_visibility_off_24 - ) - IconButton(onClick = { passwordVisible = !passwordVisible }) { - Icon(painter = image, contentDescription = "Toggle Password Visibility") - } - } - ) + // User ID TextField + OutlinedTextField( + value = userId, + onValueChange = { userId = it }, + label = { Text("User ID") }, + modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Next) + ) - Spacer(modifier = Modifier.height(60.dp)) + Spacer(modifier = Modifier.height(30.dp)) - Button( - onClick = { - isButtonLoading = true - coroutineScope.launch { - delay(1000) // Simulate network call - onLoginSuccess() - isButtonLoading = false - } - }, - modifier = Modifier - .fillMaxWidth() - .height(50.dp), - enabled = !isButtonLoading - ) { - if (isButtonLoading) { - CircularProgressIndicator( - color = Color.White, - modifier = Modifier.size(24.dp), - strokeWidth = 2.dp - ) + // Password TextField + OutlinedTextField( + value = password, + onValueChange = { password = it }, + label = { Text("Password") }, + modifier = Modifier.fillMaxWidth(), + visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done), + trailingIcon = { + val icon = if (passwordVisible) { + painterResource(id = R.drawable.baseline_visibility_24) } else { - Text( - text = "Login", - fontSize = 20.sp - - ) + painterResource(id = R.drawable.baseline_visibility_off_24) } + IconButton(onClick = { passwordVisible = !passwordVisible }) { + Icon(painter = icon, contentDescription = "Toggle Password Visibility") + } + } + ) + + Spacer(modifier = Modifier.height(60.dp)) + + // Login Button + Button( + onClick = { + coroutineScope.launch { + loginViewModel.login(userId.text, password.text, onLoginSuccess) + } + }, + modifier = Modifier + .fillMaxWidth() + .height(50.dp), + enabled = !isLoading + ) { + if (isLoading) { + CircularProgressIndicator( + color = Color.White, + modifier = Modifier.size(24.dp), + strokeWidth = 2.dp + ) + } else { + Text( + text = "Login", + fontSize = 20.sp + ) } } } } - -@Preview -@Composable -fun lofpre(){ - LoginScreen { - - } -} \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/login/viewModel/loginVM.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/viewModel/loginVM.kt new file mode 100644 index 0000000..f7eb504 --- /dev/null +++ b/app/src/main/java/in/sminnovations/attendanceapp/screens/login/viewModel/loginVM.kt @@ -0,0 +1,66 @@ +package `in`.sminnovations.attendanceapp.screens.login.viewModel + +// File: LoginViewModel.kt + + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import `in`.sminnovations.attendanceapp.screens.login.data.LoginData +import `in`.sminnovations.attendanceapp.screens.login.data.LoginDataStore +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.delay +import javax.inject.Inject + +@HiltViewModel +class LoginViewModel @Inject constructor( + private val loginDataStore: LoginDataStore +) : ViewModel() { + + private val _isLoading = MutableStateFlow(false) + val isLoading = _isLoading.asStateFlow() + + private val _isLoggedIn = MutableStateFlow(false) + val isLoggedIn = _isLoggedIn.asStateFlow() + + private val _loginData = MutableStateFlow(LoginData("", "")) + val loginData = _loginData.asStateFlow() + + init { + viewModelScope.launch { + loginDataStore.isLoggedIn.collect { loggedIn -> + _isLoggedIn.value = loggedIn + if (loggedIn) { + loginDataStore.loginId.collect { id -> + loginDataStore.password.collect { password -> + _loginData.value = LoginData(id, password) + } + } + } + } + } + } + + fun login(userId: String, password: String, onLoginSuccess: () -> Unit) { + viewModelScope.launch { + _isLoading.value = true + delay(1000) // Simulate network call + + if (userId == "1" && password == "2") { + loginDataStore.saveLoginData(userId, password) + _isLoggedIn.value = true + onLoginSuccess() + } + _isLoading.value = false + } + } + + fun logout() { + viewModelScope.launch { + loginDataStore.clearLoginData() + _isLoggedIn.value = false + } + } +} diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/ui/navigation.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/ui/navigation.kt deleted file mode 100644 index 3a09844..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/ui/navigation.kt +++ /dev/null @@ -1,61 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.navigationDrawer.ui - -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add -import androidx.compose.material3.DrawerValue -import androidx.compose.material3.ExtendedFloatingActionButton -import androidx.compose.material3.Icon -import androidx.compose.material3.ModalDrawerSheet -import androidx.compose.material3.ModalNavigationDrawer -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.material3.rememberDrawerState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Modifier -import `in`.sminnovations.attendanceapp.screens.navigationDrawer.ui.components.BottomComponents -import kotlinx.coroutines.launch -import `in`.sminnovations.attendanceapp.screens.navigationDrawer.ui.components.TopComponents - -@Composable -fun NavigationDrawer(){ - val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) - val scope = rememberCoroutineScope() - ModalNavigationDrawer( - drawerState = drawerState, - drawerContent = { - ModalDrawerSheet { - Column ( - Modifier.fillMaxHeight(), - - ){ - TopComponents() - BottomComponents(appVersion = "") - } - } - }, - ) { - Scaffold( - floatingActionButton = { - ExtendedFloatingActionButton( - text = { Text("Show drawer") }, - icon = { Icon(Icons.Filled.Add, contentDescription = "") }, - onClick = { - scope.launch { - drawerState.apply { - if (isClosed) open() else close() - } - } - } - ) - } - ) { contentPadding -> - Column (modifier = Modifier.padding(contentPadding)){ - `in`.sminnovations.attendanceapp.screens.home.ui.Home() - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/viewModel/navigationViewModel.kt b/app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/viewModel/navigationViewModel.kt deleted file mode 100644 index 2961c42..0000000 --- a/app/src/main/java/in/sminnovations/attendanceapp/screens/navigationDrawer/viewModel/navigationViewModel.kt +++ /dev/null @@ -1,4 +0,0 @@ -package `in`.sminnovations.attendanceapp.screens.navigationDrawer.viewModel - -class navigationViewModel { -} \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/ui/theme/Color.kt b/app/src/main/java/in/sminnovations/attendanceapp/ui/theme/Color.kt index b049187..43ae79b 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/ui/theme/Color.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/ui/theme/Color.kt @@ -2,10 +2,10 @@ package `in`.sminnovations.attendanceapp.ui.theme import androidx.compose.ui.graphics.Color -val Purple80 = Color(0xFFD0BCFF) -val PurpleGrey80 = Color(0xFFCCC2DC) +val Purple80 = Color(0xFFFF9800) +val PurpleGrey80 = Color(0xFFFFCB7E) val Pink80 = Color(0xFFEFB8C8) -val Purple40 = Color(0xFF6650a4) -val PurpleGrey40 = Color(0xFF625b71) +val Purple40 = Color(0xFFB86E02) +val PurpleGrey40 = Color(0xFF865307) val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/in/sminnovations/attendanceapp/ui/theme/Type.kt b/app/src/main/java/in/sminnovations/attendanceapp/ui/theme/Type.kt index 1361ad3..d299a71 100644 --- a/app/src/main/java/in/sminnovations/attendanceapp/ui/theme/Type.kt +++ b/app/src/main/java/in/sminnovations/attendanceapp/ui/theme/Type.kt @@ -11,7 +11,7 @@ val Typography = Typography( bodyLarge = TextStyle( fontFamily = FontFamily.Default, fontWeight = FontWeight.Normal, - fontSize = 16.sp, + fontSize = 18.sp, lineHeight = 24.sp, letterSpacing = 0.5.sp ) diff --git a/app/src/main/res/drawable/baseline_menu_24.xml b/app/src/main/res/drawable/baseline_menu_24.xml new file mode 100644 index 0000000..90a85a6 --- /dev/null +++ b/app/src/main/res/drawable/baseline_menu_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/build.gradle.kts b/build.gradle.kts index f74b04b..5b2b703 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,4 +2,5 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.jetbrains.kotlin.android) apply false + id("com.google.dagger.hilt.android") version "2.51.1" apply false } \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8b34422..1734bbc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,6 +8,8 @@ espressoCore = "3.6.1" lifecycleRuntimeKtx = "2.8.6" activityCompose = "1.9.2" composeBom = "2023.08.00" +datastoreCoreAndroid = "1.1.1" +datastorePreferencesCoreJvm = "1.1.1" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -24,6 +26,8 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } +androidx-datastore-core-android = { group = "androidx.datastore", name = "datastore-core-android", version.ref = "datastoreCoreAndroid" } +androidx-datastore-preferences-core-jvm = { group = "androidx.datastore", name = "datastore-preferences-core-jvm", version.ref = "datastorePreferencesCoreJvm" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }