diff --git a/app/build.gradle.kts b/app/build.gradle.kts index ed52de8..10ef019 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -70,4 +70,10 @@ dependencies { implementation("androidx.datastore:datastore:1.1.2") implementation("androidx.compose.runtime:runtime-livedata:1.7.8") implementation("io.coil-kt:coil-compose:2.0.0-rc01") + implementation("com.google.accompanist:accompanist-permissions:0.34.0") + implementation("androidx.camera:camera-camera2:1.4.1") + implementation("androidx.camera:camera-lifecycle:1.4.1") + implementation("androidx.camera:camera-view:1.4.1") + implementation("com.journeyapps:zxing-android-embedded:4.3.0") + implementation("com.google.zxing:core:3.4.0") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4bffea7..26607c7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,7 +3,11 @@ xmlns:tools="http://schemas.android.com/tools"> + + + + android:theme="@style/Theme.Nto_minipigs" + tools:ignore="DiscouragedApi"> diff --git a/app/src/main/java/com/example/nto_minipigs/MainActivity.kt b/app/src/main/java/com/example/nto_minipigs/MainActivity.kt index c6f7e79..96021b1 100644 --- a/app/src/main/java/com/example/nto_minipigs/MainActivity.kt +++ b/app/src/main/java/com/example/nto_minipigs/MainActivity.kt @@ -3,6 +3,7 @@ package com.example.nto_minipigs import android.content.Context import android.os.Bundle import android.util.Log +import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge @@ -11,7 +12,6 @@ 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.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey @@ -33,6 +33,9 @@ import kotlinx.coroutines.runBlocking import kotlinx.serialization.Serializable import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue +import com.example.nto_minipigs.ui.screens.QR.QRResultScreen +import com.example.nto_minipigs.ui.screens.QR.QRScreen +import com.example.nto_minipigs.ui.screens.QR.QRViewModel class MainActivity : ComponentActivity() { @@ -49,6 +52,7 @@ class MainActivity : ComponentActivity() { val navController = rememberNavController() val loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java] val mainViewModel = ViewModelProvider(this)[MainViewModel::class.java] + val qrViewModel = ViewModelProvider(this)[QRViewModel::class.java] val token = dataStore.getToken() @@ -57,10 +61,11 @@ class MainActivity : ComponentActivity() { NavHost( navController = navController, startDestination = if(token == "") Login else Main -// startDestination = Login ) { composable { LoginScreen( onNavigateToMain = { navController.popBackStack(); navController.navigate(route = Main) }, viewModel = loginViewModel, dataStore = dataStore ) } composable
{ MainScreen( viewModel = mainViewModel, dataStore = dataStore, navController = navController ) } + composable { QRScreen( navController = navController, viewModel = qrViewModel) } + composable { QRResultScreen( navController = navController, viewModel = qrViewModel ) } } } } @@ -91,3 +96,9 @@ object Login @Serializable object Main + +@Serializable +object QR + +@Serializable +object QRResult diff --git a/app/src/main/java/com/example/nto_minipigs/Retrofit/ApiService.kt b/app/src/main/java/com/example/nto_minipigs/Retrofit/ApiService.kt index 43a3601..4d61d77 100644 --- a/app/src/main/java/com/example/nto_minipigs/Retrofit/ApiService.kt +++ b/app/src/main/java/com/example/nto_minipigs/Retrofit/ApiService.kt @@ -2,6 +2,8 @@ package com.example.nto_minipigs.Retrofit import com.example.nto_minipigs.Retrofit.Models.Auth import com.example.nto_minipigs.Retrofit.Models.Data +import com.example.nto_minipigs.Retrofit.Models.Door +import com.example.nto_minipigs.Retrofit.Models.DoorResponse import com.example.nto_minipigs.Retrofit.Models.User import okhttp3.ResponseBody import retrofit2.Response @@ -19,8 +21,8 @@ public interface ApiService { @GET("/api/info") suspend fun info(@Header("Authorization") token: String): Response - @PATCH("/api/{LOGIN}/open") - suspend fun open(@Body data: Data, @Path("LOGIN") login: String): ResponseBody + @PATCH("/api/open") + suspend fun open(@Body data: Door): Response @POST("/api/login") suspend fun login(@Body data: Auth): Response diff --git a/app/src/main/java/com/example/nto_minipigs/Retrofit/Models/Door.kt b/app/src/main/java/com/example/nto_minipigs/Retrofit/Models/Door.kt new file mode 100644 index 0000000..cd8f1d4 --- /dev/null +++ b/app/src/main/java/com/example/nto_minipigs/Retrofit/Models/Door.kt @@ -0,0 +1,5 @@ +package com.example.nto_minipigs.Retrofit.Models + +data class Door( + val id: String +) \ No newline at end of file diff --git a/app/src/main/java/com/example/nto_minipigs/Retrofit/Models/DoorResponse.kt b/app/src/main/java/com/example/nto_minipigs/Retrofit/Models/DoorResponse.kt new file mode 100644 index 0000000..dee6213 --- /dev/null +++ b/app/src/main/java/com/example/nto_minipigs/Retrofit/Models/DoorResponse.kt @@ -0,0 +1,5 @@ +package com.example.nto_minipigs.Retrofit.Models + +data class DoorResponse( + val name: String +) \ No newline at end of file diff --git a/app/src/main/java/com/example/nto_minipigs/ui/screens/Main/MainScreen.kt b/app/src/main/java/com/example/nto_minipigs/ui/screens/Main/MainScreen.kt index 1c7f7f4..a904c76 100644 --- a/app/src/main/java/com/example/nto_minipigs/ui/screens/Main/MainScreen.kt +++ b/app/src/main/java/com/example/nto_minipigs/ui/screens/Main/MainScreen.kt @@ -58,6 +58,7 @@ import androidx.compose.ui.unit.dp import androidx.navigation.NavController import coil.compose.rememberAsyncImagePainter import com.example.nto_minipigs.Login +import com.example.nto_minipigs.QR import com.example.nto_minipigs.UserData import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch @@ -193,7 +194,7 @@ fun MainScreen( viewModel: MainViewModel, dataStore: UserData, navController: Na } } Button( - onClick = { }, + onClick = {navController.navigate(QR) }, shape = CircleShape, modifier = Modifier .align(Alignment.BottomEnd) diff --git a/app/src/main/java/com/example/nto_minipigs/ui/screens/Main/MainViewModel.kt b/app/src/main/java/com/example/nto_minipigs/ui/screens/Main/MainViewModel.kt index 43ef478..7a4b7ac 100644 --- a/app/src/main/java/com/example/nto_minipigs/ui/screens/Main/MainViewModel.kt +++ b/app/src/main/java/com/example/nto_minipigs/ui/screens/Main/MainViewModel.kt @@ -36,10 +36,6 @@ class MainViewModel: ViewModel() { } } } - - fun Logout() { - - } } sealed class NetworkResponse { diff --git a/app/src/main/java/com/example/nto_minipigs/ui/screens/QR/QRResultScreen.kt b/app/src/main/java/com/example/nto_minipigs/ui/screens/QR/QRResultScreen.kt new file mode 100644 index 0000000..1d6b305 --- /dev/null +++ b/app/src/main/java/com/example/nto_minipigs/ui/screens/QR/QRResultScreen.kt @@ -0,0 +1,70 @@ +package com.example.nto_minipigs.ui.screens.QR + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Cancel +import androidx.compose.material.icons.outlined.Check +import androidx.compose.material.icons.outlined.CheckCircle +import androidx.compose.material.icons.outlined.Close +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.navigation.NavController +import com.example.nto_minipigs.QRResult +import com.example.nto_minipigs.ui.screens.Main.NetworkResponse +import com.example.nto_minipigs.ui.screens.QR.QRViewModel + +@Composable +fun QRResultScreen(navController: NavController, viewModel: QRViewModel) { + val data = viewModel.data.observeAsState() + Surface { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + when(val result = data.value){ + is com.example.nto_minipigs.ui.screens.QR.NetworkResponse.Error -> { + Icon( + Icons.Outlined.Cancel, + contentDescription = null, + tint = MaterialTheme.colorScheme.error, + modifier = Modifier + .size(100.dp) + ) + } + com.example.nto_minipigs.ui.screens.QR.NetworkResponse.Loading -> { + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally, + ) { + CircularProgressIndicator() + } + } + is com.example.nto_minipigs.ui.screens.QR.NetworkResponse.Success -> { + Icon( + Icons.Outlined.CheckCircle, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier + .size(100.dp) + ) + } + + null -> {} + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/nto_minipigs/ui/screens/QR/QRScreen.kt b/app/src/main/java/com/example/nto_minipigs/ui/screens/QR/QRScreen.kt new file mode 100644 index 0000000..5119bbb --- /dev/null +++ b/app/src/main/java/com/example/nto_minipigs/ui/screens/QR/QRScreen.kt @@ -0,0 +1,54 @@ +package com.example.nto_minipigs.ui.screens.QR + +import android.app.Activity +import android.util.Log +import androidx.activity.result.ActivityResultLauncher +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import com.journeyapps.barcodescanner.CaptureManager +import com.journeyapps.barcodescanner.CompoundBarcodeView +import androidx.compose.runtime.getValue +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import androidx.navigation.NavController +import com.example.nto_minipigs.QRResult + +@Composable +fun QRScreen(navController: NavController, viewModel: QRViewModel) { + var scanFlag by remember { + mutableStateOf(false) + } + + AndroidView( + factory = { context -> + CompoundBarcodeView(context).apply { + val capture = CaptureManager(context as Activity, this) + capture.initializeFromIntent(context.intent, null) + this.setStatusText("") + capture.decode() + this.decodeContinuous { result -> + if (scanFlag) { + return@decodeContinuous + } + println("scanFlag true") + scanFlag = true + result.text?.let { barCodeOrQr -> + Log.d("penis", barCodeOrQr) + scanFlag = true + this.pause() + viewModel.Fetch(barCodeOrQr) + navController.navigate(QRResult) + } + //If you don't put this scanFlag = false, it will never work again. + //you can put a delay over 2 seconds and then scanFlag = false to prevent multiple scanning + } + this.resume() + } + }, + modifier = Modifier + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/nto_minipigs/ui/screens/QR/QRViewModel.kt b/app/src/main/java/com/example/nto_minipigs/ui/screens/QR/QRViewModel.kt new file mode 100644 index 0000000..7078df3 --- /dev/null +++ b/app/src/main/java/com/example/nto_minipigs/ui/screens/QR/QRViewModel.kt @@ -0,0 +1,46 @@ +package com.example.nto_minipigs.ui.screens.QR + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.nto_minipigs.Retrofit.Models.Data +import com.example.nto_minipigs.Retrofit.Models.Door +import com.example.nto_minipigs.Retrofit.Models.DoorResponse +import com.example.nto_minipigs.Retrofit.Models.User +import kotlinx.coroutines.launch + +class QRViewModel: ViewModel() { + private val serviceApi = RetrofitClient.apiService + + private val _data = MutableLiveData>() + val data : LiveData> = _data + + fun Fetch(id: String) { + _data.value = NetworkResponse.Loading + + viewModelScope.launch { + if(id != null) { + try { + val request = Door(id) + val response = serviceApi.open(request) + if(response.isSuccessful) { + response.body()?.let { + _data.value = NetworkResponse.Success(it) + } + } else { + _data.value = NetworkResponse.Error(response.message()) + } + } catch(e: Exception) { + _data.value = NetworkResponse.Error(e.toString()) + } + } + } + } +} + +sealed class NetworkResponse { + data class Success(val data: T) : NetworkResponse() + data class Error(val message: String) : NetworkResponse() + object Loading : NetworkResponse() +} \ No newline at end of file