base functionality implemented for test api
This commit is contained in:
parent
39a60043d2
commit
02385d86d5
@ -70,4 +70,10 @@ dependencies {
|
|||||||
implementation("androidx.datastore:datastore:1.1.2")
|
implementation("androidx.datastore:datastore:1.1.2")
|
||||||
implementation("androidx.compose.runtime:runtime-livedata:1.7.8")
|
implementation("androidx.compose.runtime:runtime-livedata:1.7.8")
|
||||||
implementation("io.coil-kt:coil-compose:2.0.0-rc01")
|
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")
|
||||||
}
|
}
|
@ -3,7 +3,11 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
<uses-feature android:name="android.hardware.camera"/>
|
||||||
|
<uses-feature android:name="android.hardware.camera.autofocus"/>
|
||||||
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
<application
|
<application
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
@ -15,10 +19,13 @@
|
|||||||
android:theme="@style/Theme.Nto_minipigs"
|
android:theme="@style/Theme.Nto_minipigs"
|
||||||
tools:targetApi="31">
|
tools:targetApi="31">
|
||||||
<activity
|
<activity
|
||||||
|
android:screenOrientation="portrait"
|
||||||
|
tools:replace="screenOrientation"
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/Theme.Nto_minipigs">
|
android:theme="@style/Theme.Nto_minipigs"
|
||||||
|
tools:ignore="DiscouragedApi">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package com.example.nto_minipigs
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
@ -11,7 +12,6 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.datastore.preferences.core.edit
|
import androidx.datastore.preferences.core.edit
|
||||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
@ -33,6 +33,9 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.setValue
|
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() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
@ -49,6 +52,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
val navController = rememberNavController()
|
val navController = rememberNavController()
|
||||||
val loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
|
val loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
|
||||||
val mainViewModel = ViewModelProvider(this)[MainViewModel::class.java]
|
val mainViewModel = ViewModelProvider(this)[MainViewModel::class.java]
|
||||||
|
val qrViewModel = ViewModelProvider(this)[QRViewModel::class.java]
|
||||||
|
|
||||||
val token = dataStore.getToken()
|
val token = dataStore.getToken()
|
||||||
|
|
||||||
@ -57,10 +61,11 @@ class MainActivity : ComponentActivity() {
|
|||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = if(token == "") Login else Main
|
startDestination = if(token == "") Login else Main
|
||||||
// startDestination = Login
|
|
||||||
) {
|
) {
|
||||||
composable<Login> { LoginScreen( onNavigateToMain = { navController.popBackStack(); navController.navigate(route = Main) }, viewModel = loginViewModel, dataStore = dataStore ) }
|
composable<Login> { LoginScreen( onNavigateToMain = { navController.popBackStack(); navController.navigate(route = Main) }, viewModel = loginViewModel, dataStore = dataStore ) }
|
||||||
composable<Main> { MainScreen( viewModel = mainViewModel, dataStore = dataStore, navController = navController ) }
|
composable<Main> { MainScreen( viewModel = mainViewModel, dataStore = dataStore, navController = navController ) }
|
||||||
|
composable<QR> { QRScreen( navController = navController, viewModel = qrViewModel) }
|
||||||
|
composable<QRResult> { QRResultScreen( navController = navController, viewModel = qrViewModel ) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,3 +96,9 @@ object Login
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
object Main
|
object Main
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object QR
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
object QRResult
|
||||||
|
@ -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.Auth
|
||||||
import com.example.nto_minipigs.Retrofit.Models.Data
|
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 com.example.nto_minipigs.Retrofit.Models.User
|
||||||
import okhttp3.ResponseBody
|
import okhttp3.ResponseBody
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
@ -19,8 +21,8 @@ public interface ApiService {
|
|||||||
@GET("/api/info")
|
@GET("/api/info")
|
||||||
suspend fun info(@Header("Authorization") token: String): Response<User>
|
suspend fun info(@Header("Authorization") token: String): Response<User>
|
||||||
|
|
||||||
@PATCH("/api/{LOGIN}/open")
|
@PATCH("/api/open")
|
||||||
suspend fun open(@Body data: Data, @Path("LOGIN") login: String): ResponseBody
|
suspend fun open(@Body data: Door): Response<DoorResponse>
|
||||||
|
|
||||||
@POST("/api/login")
|
@POST("/api/login")
|
||||||
suspend fun login(@Body data: Auth): Response<Data>
|
suspend fun login(@Body data: Auth): Response<Data>
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.example.nto_minipigs.Retrofit.Models
|
||||||
|
|
||||||
|
data class Door(
|
||||||
|
val id: String
|
||||||
|
)
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.example.nto_minipigs.Retrofit.Models
|
||||||
|
|
||||||
|
data class DoorResponse(
|
||||||
|
val name: String
|
||||||
|
)
|
@ -58,6 +58,7 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import coil.compose.rememberAsyncImagePainter
|
import coil.compose.rememberAsyncImagePainter
|
||||||
import com.example.nto_minipigs.Login
|
import com.example.nto_minipigs.Login
|
||||||
|
import com.example.nto_minipigs.QR
|
||||||
import com.example.nto_minipigs.UserData
|
import com.example.nto_minipigs.UserData
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -193,7 +194,7 @@ fun MainScreen( viewModel: MainViewModel, dataStore: UserData, navController: Na
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button(
|
Button(
|
||||||
onClick = { },
|
onClick = {navController.navigate(QR) },
|
||||||
shape = CircleShape,
|
shape = CircleShape,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomEnd)
|
.align(Alignment.BottomEnd)
|
||||||
|
@ -36,10 +36,6 @@ class MainViewModel: ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Logout() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class NetworkResponse<out T> {
|
sealed class NetworkResponse<out T> {
|
||||||
|
@ -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 -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
@ -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<NetworkResponse<DoorResponse>>()
|
||||||
|
val data : LiveData<NetworkResponse<DoorResponse>> = _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<out T> {
|
||||||
|
data class Success<out T>(val data: T) : NetworkResponse<T>()
|
||||||
|
data class Error(val message: String) : NetworkResponse<Nothing>()
|
||||||
|
object Loading : NetworkResponse<Nothing>()
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user