diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4bdcaaa..6aab706 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,5 @@ +import com.android.sdklib.AndroidVersion.VersionCodes.UPSIDE_DOWN_CAKE + plugins { androidApplication jetbrainsKotlinSerialization version Version.Kotlin.language @@ -9,12 +11,12 @@ plugins { val packageName = "ru.myitschool.work" android { namespace = packageName - compileSdk = 35 + compileSdk = UPSIDE_DOWN_CAKE defaultConfig { applicationId = packageName minSdk = 31 - targetSdk = 35 + targetSdk = UPSIDE_DOWN_CAKE versionCode = 1 versionName = "1.0" @@ -38,12 +40,11 @@ android { dependencies { implementation(libs.androidx.appcompat) implementation(libs.material) - implementation(libs.androidx.activity.ktx) implementation(libs.androidx.constraintlayout) implementation(libs.androidx.annotation) implementation(libs.androidx.lifecycle.livedata.ktx) implementation(libs.androidx.lifecycle.viewmodel.ktx) - defaultLibrary() + implementation(Dependencies.AndroidX.activity) implementation(Dependencies.AndroidX.fragment) @@ -65,10 +66,10 @@ dependencies { implementation(libs.androidx.camera.lifecycle) implementation(libs.androidx.camera.view) implementation(libs.androidx.camera.mlkit.vision) - - val hilt = "2.51.1" + implementation(libs.androidx.paging.runtime.ktx) + defaultLibrary() implementation(libs.hilt.android) - kapt("com.google.dagger:hilt-android-compiler:$hilt") + kapt(libs.hilt.android.compiler) } kapt { diff --git a/app/src/main/java/ru/myitschool/work/core/Constants.kt b/app/src/main/java/ru/myitschool/work/core/Constants.kt index ffb63c1..e3bd895 100644 --- a/app/src/main/java/ru/myitschool/work/core/Constants.kt +++ b/app/src/main/java/ru/myitschool/work/core/Constants.kt @@ -1,5 +1,5 @@ package ru.myitschool.work.core // БЕРИТЕ И ИЗМЕНЯЙТЕ ХОСТ ТОЛЬКО ЗДЕСЬ И НЕ БЕРИТЕ ИЗ ДРУГИХ МЕСТ. ФАЙЛ ПЕРЕМЕЩАТЬ НЕЛЬЗЯ object Constants { - const val SERVER_ADDRESS = "http://10.0.2.2:8080" + const val SERVER_ADDRESS = "http://192.168.1.145:8080" } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/UserDataStoreManager.kt b/app/src/main/java/ru/myitschool/work/data/UserDataStoreManager.kt index f24174a..ba5b650 100644 --- a/app/src/main/java/ru/myitschool/work/data/UserDataStoreManager.kt +++ b/app/src/main/java/ru/myitschool/work/data/UserDataStoreManager.kt @@ -12,6 +12,8 @@ class UserDataStoreManager(private val context: Context) { companion object { private val USERNAME_KEY = stringPreferencesKey("username") + private val ROLE_KEY = stringPreferencesKey("role") + private val PASSWORD_KEY = stringPreferencesKey("password") fun getInstance(context: Context): UserDataStoreManager { return UserDataStoreManager(context.applicationContext) @@ -21,13 +23,24 @@ class UserDataStoreManager(private val context: Context) { val usernameFlow: Flow = context.applicationContext.dataStore.data.map { prefs -> prefs[USERNAME_KEY] ?: "" } - suspend fun saveUsername(username: String) { + val passwordFlow: Flow = context.applicationContext.dataStore.data.map { prefs -> + prefs[PASSWORD_KEY] ?: "" + } + val roleFlow: Flow = context.applicationContext.dataStore.data.map{ prefs -> + prefs[ROLE_KEY] ?: "" + } + suspend fun saveCredentials(username: String, password: String) { context.dataStore.edit { prefs -> prefs[USERNAME_KEY] = username + prefs[PASSWORD_KEY] = password } } - - suspend fun clearUsername() { + suspend fun saveRole(role: String){ + context.dataStore.edit { prefs -> + prefs[ROLE_KEY] = role + } + } + suspend fun clearCredentials() { context.applicationContext.dataStore.edit { it.clear() } } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/door/DoorNetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/door/DoorNetworkDataSource.kt index 4589597..4384151 100644 --- a/app/src/main/java/ru/myitschool/work/data/door/DoorNetworkDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/door/DoorNetworkDataSource.kt @@ -2,18 +2,16 @@ package ru.myitschool.work.data.door import android.content.Context import io.ktor.client.call.body +import io.ktor.client.request.basicAuth import io.ktor.client.request.patch -import io.ktor.client.request.setBody import io.ktor.client.statement.bodyAsText -import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode -import io.ktor.http.contentType +import io.ktor.http.headers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext import ru.myitschool.work.core.Constants import ru.myitschool.work.data.UserDataStoreManager -import ru.myitschool.work.data.dto.OpenRequestDTO import ru.myitschool.work.utils.NetworkModule class DoorNetworkDataSource( @@ -21,12 +19,14 @@ class DoorNetworkDataSource( ) { private val client = NetworkModule.httpClient private val userDataStoreManager = UserDataStoreManager.getInstance(context) - suspend fun openDoor(openRequestDTO: OpenRequestDTO): Result = withContext(Dispatchers.IO){ + suspend fun openDoor(code : String): Result = withContext(Dispatchers.IO){ runCatching { val username = userDataStoreManager.usernameFlow.first() - val result = client.patch("${Constants.SERVER_ADDRESS}/api/$username/open"){ - contentType(ContentType.Application.Json) - setBody(openRequestDTO) + val password = userDataStoreManager.passwordFlow.first() + val result = client.patch("${Constants.SERVER_ADDRESS}/api/employee/open?code=$code"){ + headers{ + basicAuth(username, password) + } } if (result.status != HttpStatusCode.OK) { error("Status ${result.status}") diff --git a/app/src/main/java/ru/myitschool/work/data/door/DoorRepoImpl.kt b/app/src/main/java/ru/myitschool/work/data/door/DoorRepoImpl.kt index 510c241..9b627df 100644 --- a/app/src/main/java/ru/myitschool/work/data/door/DoorRepoImpl.kt +++ b/app/src/main/java/ru/myitschool/work/data/door/DoorRepoImpl.kt @@ -1,12 +1,11 @@ package ru.myitschool.work.data.door import ru.myitschool.work.domain.door.DoorRepo -import ru.myitschool.work.domain.entities.OpenEntity class DoorRepoImpl( private val networkDataSource: DoorNetworkDataSource ) : DoorRepo { - override suspend fun openDoor(openEntity: OpenEntity): Result { - return networkDataSource.openDoor(openEntity.toDto()) + override suspend fun openDoor(code: String): Result { + return networkDataSource.openDoor(code) } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/dto/EmployeeEntranceDTO.kt b/app/src/main/java/ru/myitschool/work/data/dto/EmployeeEntranceDTO.kt new file mode 100644 index 0000000..c0bcfc1 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/dto/EmployeeEntranceDTO.kt @@ -0,0 +1,14 @@ +package ru.myitschool.work.data.dto + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import ru.myitschool.work.utils.DateSerializer +import java.util.Date + +@Serializable +data class EmployeeEntranceDTO( + @SerialName("id") val id : Int?, + @SerialName("entryTime") @Serializable(with = DateSerializer::class) val scanTime : Date?, + @SerialName("readerName") val readerName: String?, + @SerialName("type") val type: String? +) diff --git a/app/src/main/java/ru/myitschool/work/data/dto/OpenRequestDTO.kt b/app/src/main/java/ru/myitschool/work/data/dto/EmployeeEntranceListPagingDTO.kt similarity index 54% rename from app/src/main/java/ru/myitschool/work/data/dto/OpenRequestDTO.kt rename to app/src/main/java/ru/myitschool/work/data/dto/EmployeeEntranceListPagingDTO.kt index 159c5a0..26edbc1 100644 --- a/app/src/main/java/ru/myitschool/work/data/dto/OpenRequestDTO.kt +++ b/app/src/main/java/ru/myitschool/work/data/dto/EmployeeEntranceListPagingDTO.kt @@ -4,6 +4,6 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -data class OpenRequestDTO( - @SerialName("value") val value: Long -) \ No newline at end of file +data class EmployeeEntranceListPagingDTO( + @SerialName("content") val content : List? +) diff --git a/app/src/main/java/ru/myitschool/work/data/dto/UserDTO.kt b/app/src/main/java/ru/myitschool/work/data/dto/UserDTO.kt index 3e0e4be..844d6f1 100644 --- a/app/src/main/java/ru/myitschool/work/data/dto/UserDTO.kt +++ b/app/src/main/java/ru/myitschool/work/data/dto/UserDTO.kt @@ -5,11 +5,11 @@ import kotlinx.serialization.Serializable @Serializable data class UserDTO( - @SerialName("id") val id: Int, - @SerialName("login") val login: String, - @SerialName("name") val name: String, - @SerialName("photo") val photo: String, - @SerialName("position") val position: String, - @SerialName("lastVisit") val lastVisit: String + @SerialName("id") val id: Long?, + @SerialName("login") val login: String?, + @SerialName("name") val name: String?, + @SerialName("authority") val authority : String?, + @SerialName("photoUrl") val photoUrl: String?, + @SerialName("position") val position: String? ) diff --git a/app/src/main/java/ru/myitschool/work/data/entrance/allEntrances/AllEntranceListNetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/entrance/allEntrances/AllEntranceListNetworkDataSource.kt new file mode 100644 index 0000000..84469f7 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/entrance/allEntrances/AllEntranceListNetworkDataSource.kt @@ -0,0 +1,39 @@ +package ru.myitschool.work.data.entrance.allEntrances + +import android.content.Context +import io.ktor.client.call.body +import io.ktor.client.request.basicAuth +import io.ktor.client.request.get +import io.ktor.http.HttpStatusCode +import io.ktor.http.headers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.withContext +import ru.myitschool.work.core.Constants +import ru.myitschool.work.data.UserDataStoreManager +import ru.myitschool.work.data.dto.EmployeeEntranceListPagingDTO +import ru.myitschool.work.utils.NetworkModule + +class AllEntranceListNetworkDataSource( + context: Context +) { + private val client = NetworkModule.httpClient + private val userDataStoreManager = UserDataStoreManager.getInstance(context) + suspend fun getList(pageNum: Int, pageSize: Int):Result = withContext( + Dispatchers.IO){ + runCatching { + val username = userDataStoreManager.usernameFlow.first() + val password = userDataStoreManager.passwordFlow.first() + val result = client.get("${Constants.SERVER_ADDRESS}/api/entrance/all?page=$pageNum&size=$pageSize"){ + headers{ + basicAuth(username, password) + } + } + + if (result.status != HttpStatusCode.OK) { + error("Status ${result.status}") + } + result.body() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/entrance/allEntrances/AllEntranceListRepoImpl.kt b/app/src/main/java/ru/myitschool/work/data/entrance/allEntrances/AllEntranceListRepoImpl.kt new file mode 100644 index 0000000..1a7db28 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/entrance/allEntrances/AllEntranceListRepoImpl.kt @@ -0,0 +1,24 @@ +package ru.myitschool.work.data.entrance.allEntrances + +import ru.myitschool.work.domain.employeeEntrance.allEntrances.AllEntranceListRepo +import ru.myitschool.work.domain.entities.EmployeeEntranceEntity + +class AllEntranceListRepoImpl( + private val networkDataSource: AllEntranceListNetworkDataSource +) : AllEntranceListRepo { + override suspend fun getList( + pageNum: Int, + pageSize: Int + ): Result> { + return networkDataSource.getList(pageNum, pageSize).map { pagingDTO -> + pagingDTO.content?.mapNotNull { dto -> + EmployeeEntranceEntity( + id = dto.id ?: return@mapNotNull null, + scanTime = dto.scanTime ?: return@mapNotNull null, + readerName = dto.readerName ?: return@mapNotNull null, + type = dto.type ?: return@mapNotNull null + ) + }?: return Result.failure(IllegalStateException("List parse error")) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/entrance/employeeEntrances/EmployeeEntranceListNetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/entrance/employeeEntrances/EmployeeEntranceListNetworkDataSource.kt new file mode 100644 index 0000000..1ce9725 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/entrance/employeeEntrances/EmployeeEntranceListNetworkDataSource.kt @@ -0,0 +1,40 @@ +package ru.myitschool.work.data.entrance.employeeEntrances + +import android.content.Context +import io.ktor.client.call.body +import io.ktor.client.request.basicAuth +import io.ktor.client.request.get +import io.ktor.client.statement.bodyAsText +import io.ktor.http.HttpStatusCode +import io.ktor.http.headers +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.withContext +import ru.myitschool.work.core.Constants +import ru.myitschool.work.data.UserDataStoreManager +import ru.myitschool.work.data.dto.EmployeeEntranceListPagingDTO +import ru.myitschool.work.utils.NetworkModule + +class EmployeeEntranceListNetworkDataSource( + context: Context +){ + private val client = NetworkModule.httpClient + private val userDataStoreManager = UserDataStoreManager.getInstance(context) + suspend fun getList(pageNum: Int, pageSize: Int):Result = withContext(Dispatchers.IO){ + runCatching { + val username = userDataStoreManager.usernameFlow.first() + val password = userDataStoreManager.passwordFlow.first() + val result = client.get("${Constants.SERVER_ADDRESS}/api/entrance?page=$pageNum&size=$pageSize"){ + headers{ + basicAuth(username, password) + } + } + + if (result.status != HttpStatusCode.OK) { + error("Status ${result.status}") + } + println(result.bodyAsText()) + result.body() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/entrance/employeeEntrances/EmployeeEntranceListRepoImpl.kt b/app/src/main/java/ru/myitschool/work/data/entrance/employeeEntrances/EmployeeEntranceListRepoImpl.kt new file mode 100644 index 0000000..4761046 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/data/entrance/employeeEntrances/EmployeeEntranceListRepoImpl.kt @@ -0,0 +1,21 @@ +package ru.myitschool.work.data.entrance.employeeEntrances + +import ru.myitschool.work.domain.entities.EmployeeEntranceEntity +import ru.myitschool.work.domain.employeeEntrance.employeeEntrances.EmployeeEntranceListRepo + +class EmployeeEntranceListRepoImpl( + private val networkDataSource: EmployeeEntranceListNetworkDataSource +) : EmployeeEntranceListRepo { + override suspend fun getList(pageNum: Int, pageSize: Int): Result> { + return networkDataSource.getList(pageNum, pageSize).map { pagingDTO -> + pagingDTO.content?.mapNotNull { dto-> + EmployeeEntranceEntity( + id = dto.id ?: return@mapNotNull null, + scanTime = dto.scanTime ?: return@mapNotNull null, + readerName = dto.readerName ?: return@mapNotNull null, + type = dto.type ?: return@mapNotNull null, + ) + }?: return Result.failure(IllegalStateException("List parse error")) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/info/InfoNetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/info/InfoNetworkDataSource.kt index 1b38515..d8ffa2d 100644 --- a/app/src/main/java/ru/myitschool/work/data/info/InfoNetworkDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/info/InfoNetworkDataSource.kt @@ -2,9 +2,11 @@ package ru.myitschool.work.data.info import android.content.Context import io.ktor.client.call.body -import io.ktor.client.request.get +import io.ktor.client.request.basicAuth +import io.ktor.client.request.post import io.ktor.client.statement.bodyAsText import io.ktor.http.HttpStatusCode +import io.ktor.http.headers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.withContext @@ -22,8 +24,12 @@ class InfoNetworkDataSource( suspend fun getInfo():Result = withContext(Dispatchers.IO){ runCatching { val username = userDataStoreManager.usernameFlow.first() - val result = client.get("${Constants.SERVER_ADDRESS}/api/$username/info") - + val password = userDataStoreManager.passwordFlow.first() + val result = client.post("${Constants.SERVER_ADDRESS}/api/employee/profile"){ + headers{ + basicAuth(username, password) + } + } if (result.status != HttpStatusCode.OK) { error("Status ${result.status}") } diff --git a/app/src/main/java/ru/myitschool/work/data/info/InfoRepoImpl.kt b/app/src/main/java/ru/myitschool/work/data/info/InfoRepoImpl.kt index 9c82918..d6e3f94 100644 --- a/app/src/main/java/ru/myitschool/work/data/info/InfoRepoImpl.kt +++ b/app/src/main/java/ru/myitschool/work/data/info/InfoRepoImpl.kt @@ -7,13 +7,16 @@ class InfoRepoImpl( private val networkDataSource: InfoNetworkDataSource ): InfoRepo { override suspend fun getInfo(): Result { - return networkDataSource.getInfo().map { dto -> + return networkDataSource.getInfo().map { dto-> UserEntity( - name = dto.name, - position = dto.position, - lastVisit = dto.lastVisit, - photo = dto.photo + id = dto.id ?: 0, + login = dto.login ?: "", + name = dto.name ?: "", + authority = dto.authority ?: "", + photoUrl = dto.photoUrl, + position = dto.position ?: "" ) } + } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/data/login/LoginNetworkDataSource.kt b/app/src/main/java/ru/myitschool/work/data/login/LoginNetworkDataSource.kt index a2b5063..a2b92f2 100644 --- a/app/src/main/java/ru/myitschool/work/data/login/LoginNetworkDataSource.kt +++ b/app/src/main/java/ru/myitschool/work/data/login/LoginNetworkDataSource.kt @@ -1,9 +1,11 @@ package ru.myitschool.work.data.login import io.ktor.client.call.body -import io.ktor.client.request.get +import io.ktor.client.request.basicAuth +import io.ktor.client.request.post import io.ktor.client.statement.bodyAsText import io.ktor.http.HttpStatusCode +import io.ktor.http.headers import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import ru.myitschool.work.core.Constants @@ -11,10 +13,14 @@ import ru.myitschool.work.utils.NetworkModule class LoginNetworkDataSource { private val client = NetworkModule.httpClient - suspend fun login(username: String):Result = withContext(Dispatchers.IO){ + suspend fun login(username: String, password: String):Result = withContext(Dispatchers.IO){ runCatching { - val result = client.get("${Constants.SERVER_ADDRESS}/api/$username/auth") - + println("$username $password") + val result = client.post("${Constants.SERVER_ADDRESS}/api/employee/login"){ + headers{ + basicAuth(username, password) + } + } if (result.status != HttpStatusCode.OK) { error("Status ${result.status}") } diff --git a/app/src/main/java/ru/myitschool/work/data/login/LoginRepoImpl.kt b/app/src/main/java/ru/myitschool/work/data/login/LoginRepoImpl.kt index 890931b..ff6d56a 100644 --- a/app/src/main/java/ru/myitschool/work/data/login/LoginRepoImpl.kt +++ b/app/src/main/java/ru/myitschool/work/data/login/LoginRepoImpl.kt @@ -5,7 +5,7 @@ import ru.myitschool.work.domain.login.LoginRepo class LoginRepoImpl( private val networkDataSource: LoginNetworkDataSource ) : LoginRepo { - override suspend fun login(username: String): Result { - return networkDataSource.login(username) + override suspend fun login(username: String, password: String): Result { + return networkDataSource.login(username, password) } } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/door/DoorRepo.kt b/app/src/main/java/ru/myitschool/work/domain/door/DoorRepo.kt index bd96676..8b5223e 100644 --- a/app/src/main/java/ru/myitschool/work/domain/door/DoorRepo.kt +++ b/app/src/main/java/ru/myitschool/work/domain/door/DoorRepo.kt @@ -1,7 +1,5 @@ package ru.myitschool.work.domain.door -import ru.myitschool.work.domain.entities.OpenEntity - interface DoorRepo { - suspend fun openDoor(openEntity: OpenEntity) : Result + suspend fun openDoor(code: String) : Result } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/door/OpenDoorUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/door/OpenDoorUseCase.kt index f968d4a..98c39c0 100644 --- a/app/src/main/java/ru/myitschool/work/domain/door/OpenDoorUseCase.kt +++ b/app/src/main/java/ru/myitschool/work/domain/door/OpenDoorUseCase.kt @@ -1,11 +1,7 @@ package ru.myitschool.work.domain.door -import ru.myitschool.work.domain.entities.OpenEntity - class OpenDoorUseCase( private val repo: DoorRepo ) { - suspend operator fun invoke(openEntity: OpenEntity) = repo.openDoor( - openEntity = openEntity - ) + suspend operator fun invoke(code: String) = repo.openDoor(code) } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/allEntrances/AllEntranceListRepo.kt b/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/allEntrances/AllEntranceListRepo.kt new file mode 100644 index 0000000..8192537 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/allEntrances/AllEntranceListRepo.kt @@ -0,0 +1,7 @@ +package ru.myitschool.work.domain.employeeEntrance.allEntrances + +import ru.myitschool.work.domain.entities.EmployeeEntranceEntity + +interface AllEntranceListRepo { + suspend fun getList(pageNum : Int, pageSize: Int) : Result> +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/allEntrances/GetAllEmployeesEntranceList.kt b/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/allEntrances/GetAllEmployeesEntranceList.kt new file mode 100644 index 0000000..48351d2 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/allEntrances/GetAllEmployeesEntranceList.kt @@ -0,0 +1,7 @@ +package ru.myitschool.work.domain.employeeEntrance.allEntrances + +class GetAllEmployeesEntranceList( + private val repo: AllEntranceListRepo +) { + suspend operator fun invoke(pageNum : Int, pageSize: Int) = repo.getList(pageNum, pageSize) +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/employeeEntrances/EmployeeEntranceListRepo.kt b/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/employeeEntrances/EmployeeEntranceListRepo.kt new file mode 100644 index 0000000..5e33695 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/employeeEntrances/EmployeeEntranceListRepo.kt @@ -0,0 +1,7 @@ +package ru.myitschool.work.domain.employeeEntrance.employeeEntrances + +import ru.myitschool.work.domain.entities.EmployeeEntranceEntity + +interface EmployeeEntranceListRepo { + suspend fun getList(pageNum : Int, pageSize: Int) : Result> +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/employeeEntrances/GetEmployeeEntranceListUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/employeeEntrances/GetEmployeeEntranceListUseCase.kt new file mode 100644 index 0000000..f3720f3 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/employeeEntrance/employeeEntrances/GetEmployeeEntranceListUseCase.kt @@ -0,0 +1,7 @@ +package ru.myitschool.work.domain.employeeEntrance.employeeEntrances + +class GetEmployeeEntranceListUseCase( + private val repo: EmployeeEntranceListRepo +) { + suspend operator fun invoke(pageNum : Int, pageSize: Int) = repo.getList(pageNum, pageSize) +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/entities/EmployeeEntranceEntity.kt b/app/src/main/java/ru/myitschool/work/domain/entities/EmployeeEntranceEntity.kt new file mode 100644 index 0000000..c131462 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/domain/entities/EmployeeEntranceEntity.kt @@ -0,0 +1,9 @@ +package ru.myitschool.work.domain.entities +import java.util.Date + +data class EmployeeEntranceEntity( + val id : Int?, + val scanTime : Date?, + val readerName: String?, + val type: String? +) diff --git a/app/src/main/java/ru/myitschool/work/domain/entities/OpenEntity.kt b/app/src/main/java/ru/myitschool/work/domain/entities/OpenEntity.kt deleted file mode 100644 index 288b068..0000000 --- a/app/src/main/java/ru/myitschool/work/domain/entities/OpenEntity.kt +++ /dev/null @@ -1,13 +0,0 @@ -package ru.myitschool.work.domain.entities - -import ru.myitschool.work.data.dto.OpenRequestDTO - -data class OpenEntity( - val value: Long -){ - fun toDto() : OpenRequestDTO{ - return OpenRequestDTO( - value = value - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/entities/UserEntity.kt b/app/src/main/java/ru/myitschool/work/domain/entities/UserEntity.kt index 4500cb7..1e99a45 100644 --- a/app/src/main/java/ru/myitschool/work/domain/entities/UserEntity.kt +++ b/app/src/main/java/ru/myitschool/work/domain/entities/UserEntity.kt @@ -1,8 +1,10 @@ package ru.myitschool.work.domain.entities -data class UserEntity ( +data class UserEntity( + val id: Long, + val login: String, val name: String, - val photo: String, - val position: String, - val lastVisit: String + val authority: String, + val photoUrl: String?, + val position: String ) \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/login/LoginRepo.kt b/app/src/main/java/ru/myitschool/work/domain/login/LoginRepo.kt index c63ac08..e3392f4 100644 --- a/app/src/main/java/ru/myitschool/work/domain/login/LoginRepo.kt +++ b/app/src/main/java/ru/myitschool/work/domain/login/LoginRepo.kt @@ -1,5 +1,5 @@ package ru.myitschool.work.domain.login interface LoginRepo { - suspend fun login(username: String): Result + suspend fun login(username: String, password: String): Result } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/domain/login/LoginUseCase.kt b/app/src/main/java/ru/myitschool/work/domain/login/LoginUseCase.kt index 5798888..a977b27 100644 --- a/app/src/main/java/ru/myitschool/work/domain/login/LoginUseCase.kt +++ b/app/src/main/java/ru/myitschool/work/domain/login/LoginUseCase.kt @@ -3,5 +3,5 @@ package ru.myitschool.work.domain.login class LoginUseCase( private val repo: LoginRepo ) { - suspend operator fun invoke(username : String) = repo.login(username) + suspend operator fun invoke(username: String, password: String) = repo.login(username, password) } \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt index 4056cc9..89b273a 100644 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginFragment.kt @@ -29,25 +29,44 @@ class LoginFragment : Fragment(R.layout.fragment_login) { override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val username = binding.username.text - binding.loginBtn.isEnabled = username.length >= 3 && !username[0].isDigit() && username.matches(Regex("^[a-zA-Z0-9]*$")) + val password = binding.password.text + binding.loginBtn.isEnabled = username.length >= 3 && !username[0].isDigit() && username.matches(Regex("^[a-zA-Z0-9]*$")) && + password.length >= 6 } } binding.username.addTextChangedListener(textWatcher) + binding.password.addTextChangedListener(textWatcher) + binding.loginBtn.isEnabled = false binding.loginBtn.setOnClickListener{ - viewModel.login(binding.username.text.toString()) + viewModel.login(binding.username.text.toString(), binding.password.text.toString()) } lifecycleScope.launch { viewModel.state.collect { state -> with(binding) { - error.visibility = if (state is LoginViewModel.State.Error) View.VISIBLE else View.GONE - username.isEnabled = state !is LoginViewModel.State.Loading - - if (state is LoginViewModel.State.Success) { - findNavController().navigate(R.id.mainFragment) + when(state){ + is LoginViewModel.State.Error -> { + error.visibility = View.VISIBLE + loading.visibility = View.GONE + username.isEnabled = true + } + is LoginViewModel.State.Idle -> { + loading.visibility = View.GONE + error.visibility = View.GONE + username.isEnabled = true + } + is LoginViewModel.State.Loading -> { + loading.visibility = View.VISIBLE + error.visibility = View.GONE + username.isEnabled = false + } + is LoginViewModel.State.Success -> { + findNavController().navigate(R.id.mainFragment) + } } + } } } diff --git a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt index e6f1502..75a34cc 100644 --- a/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/login/LoginViewModel.kt @@ -27,26 +27,27 @@ class LoginViewModel( init { viewModelScope.launch{ val username = dataStoreManager.usernameFlow.first() - if(username != "") - login(username) + val password = dataStoreManager.passwordFlow.first() + if(username != "" && password != "") + login(username, password) } } sealed class State { - object Idle : State() - object Loading : State() - object Success : State() + data object Idle : State() + data object Loading : State() + data object Success : State() data class Error(val message: String?) : State() } - fun login(username: String) { + fun login(username: String, password: String) { _state.value = State.Loading viewModelScope.launch{ - useCase.invoke(username).fold( - onSuccess = { data -> - dataStoreManager.saveUsername(username) + useCase.invoke(username, password).fold( + onSuccess = { _ -> + dataStoreManager.saveCredentials(username, password) _state.value = State.Success }, onFailure = {e-> diff --git a/app/src/main/java/ru/myitschool/work/ui/main/EmployeeEntranceListAdapter.kt b/app/src/main/java/ru/myitschool/work/ui/main/EmployeeEntranceListAdapter.kt new file mode 100644 index 0000000..8d0cbf3 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/main/EmployeeEntranceListAdapter.kt @@ -0,0 +1,43 @@ +package ru.myitschool.work.ui.main + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.paging.PagingDataAdapter +import androidx.recyclerview.widget.RecyclerView +import ru.myitschool.work.databinding.ItemVisitBinding +import ru.myitschool.work.domain.entities.EmployeeEntranceEntity +import ru.myitschool.work.utils.dateConverter + +class EmployeeEntranceListAdapter : PagingDataAdapter(DiffUtil) { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : ViewHolder{ + return ViewHolder( + ItemVisitBinding.inflate( + LayoutInflater.from(parent.context), parent, false + ) + ) + } + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = getItem(position) + if (item != null) { + holder.bind(item) + } + + } + inner class ViewHolder( + private val binding: ItemVisitBinding, + ) : RecyclerView.ViewHolder(binding.root) { + fun bind(item: EmployeeEntranceEntity) { + binding.readerName.text = item.readerName + binding.timeVisit.text = dateConverter(item.scanTime) + } + + } + object DiffUtil : androidx.recyclerview.widget.DiffUtil.ItemCallback() { + override fun areItemsTheSame(oldItem: EmployeeEntranceEntity, newItem: EmployeeEntranceEntity): Boolean { + return oldItem.scanTime == newItem.scanTime + } + override fun areContentsTheSame(oldItem: EmployeeEntranceEntity, newItem: EmployeeEntranceEntity): Boolean { + return oldItem == newItem + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/main/EmployeeEntranceListPagingSource.kt b/app/src/main/java/ru/myitschool/work/ui/main/EmployeeEntranceListPagingSource.kt new file mode 100644 index 0000000..feaa7cd --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/ui/main/EmployeeEntranceListPagingSource.kt @@ -0,0 +1,36 @@ +package ru.myitschool.work.ui.main + +import androidx.paging.PagingSource +import androidx.paging.PagingState +import ru.myitschool.work.domain.entities.EmployeeEntranceEntity + +class EmployeeEntranceListPagingSource( + private val request: suspend(pageNum: Int, pageSize: Int) ->Result> +) : PagingSource() { + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let{ + state.closestPageToPosition(it)?.prevKey?.plus(1) ?: + state.closestPageToPosition(it)?.nextKey?.minus(1) + } + } + + override suspend fun load(params: LoadParams): LoadResult { + val pageNum = params.key ?: 0 + return request.invoke( + pageNum, params.loadSize + ).fold( + onSuccess = { value -> + LoadResult.Page( + data = value, + prevKey = (pageNum - 1).takeIf { it >= 0 }, + nextKey = (pageNum + 1).takeIf { value.size == params.loadSize } + ) + + }, + onFailure = { e-> + println(e) + LoadResult.Error(e) + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt b/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt index 69c4b06..204e074 100644 --- a/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/main/MainFragment.kt @@ -8,6 +8,8 @@ import androidx.fragment.app.setFragmentResultListener import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController +import androidx.paging.LoadState +import androidx.recyclerview.widget.LinearLayoutManager import com.squareup.picasso.Picasso import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -17,6 +19,7 @@ import ru.myitschool.work.domain.entities.UserEntity import ru.myitschool.work.ui.qr.scan.QrScanDestination import ru.myitschool.work.utils.UserState import ru.myitschool.work.utils.collectWhenStarted +import ru.myitschool.work.utils.collectWithLifecycle class MainFragment : Fragment(R.layout.fragment_main) { @@ -31,7 +34,7 @@ class MainFragment : Fragment(R.layout.fragment_main) { _binding = FragmentMainBinding.bind(view) viewModel.getUserData() - binding.refresh.setOnClickListener { viewModel.getUserData() } + binding.logout.setOnClickListener { logout() } binding.scan.setOnClickListener { onScanClick() } viewModel.userState.collectWhenStarted(this) { state -> @@ -57,6 +60,27 @@ class MainFragment : Fragment(R.layout.fragment_main) { } } } + binding.content.layoutManager = LinearLayoutManager(requireContext()) + val adapter = EmployeeEntranceListAdapter() + binding.refresh.setOnClickListener { + viewModel.getUserData() + adapter.refresh() + } + binding.content.adapter = adapter + viewModel.listState.collectWithLifecycle(this) { data -> + adapter.submitData(data) + } + adapter.loadStateFlow.collectWithLifecycle(this) { loadState -> + val state = loadState.refresh + binding.error.visibility = if (state is LoadState.Error) View.VISIBLE else View.GONE + binding.loading.visibility = if (state is LoadState.Loading) View.VISIBLE else View.GONE + + if (state is LoadState.Error) { + binding.error.text = state.error.message.toString() + } + } + + setFragmentResultListener(QrScanDestination.REQUEST_KEY) { _, bundle -> val qrData = QrScanDestination.getDataIfExist(bundle) println(qrData) @@ -81,9 +105,9 @@ class MainFragment : Fragment(R.layout.fragment_main) { private fun showUserData(userEntity: UserEntity) { binding.apply { fullname.text = userEntity.name + println(userEntity.name) position.text = userEntity.position - lastEntry.text = viewModel.formatDate(userEntity.lastVisit) - Picasso.get().load(userEntity.photo).into(photo) + Picasso.get().load(userEntity.photoUrl).into(photo) error.visibility = View.GONE setViewsVisibility(View.VISIBLE) @@ -98,7 +122,6 @@ class MainFragment : Fragment(R.layout.fragment_main) { private fun setViewsVisibility(visibility: Int) { binding.fullname.visibility = visibility binding.position.visibility = visibility - binding.lastEntry.visibility = visibility binding.photo.visibility = visibility binding.logout.visibility = visibility binding.scan.visibility = visibility diff --git a/app/src/main/java/ru/myitschool/work/ui/main/MainViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/main/MainViewModel.kt index 641f149..7cd1e3e 100644 --- a/app/src/main/java/ru/myitschool/work/ui/main/MainViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/main/MainViewModel.kt @@ -6,6 +6,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.CreationExtras +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.cachedIn import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -14,40 +17,52 @@ import kotlinx.coroutines.withContext import ru.myitschool.work.data.UserDataStoreManager import ru.myitschool.work.data.info.InfoNetworkDataSource import ru.myitschool.work.data.info.InfoRepoImpl +import ru.myitschool.work.data.entrance.employeeEntrances.EmployeeEntranceListNetworkDataSource +import ru.myitschool.work.data.entrance.employeeEntrances.EmployeeEntranceListRepoImpl import ru.myitschool.work.domain.info.GetInfoUseCase +import ru.myitschool.work.domain.employeeEntrance.employeeEntrances.GetEmployeeEntranceListUseCase import ru.myitschool.work.utils.UserState -import java.text.SimpleDateFormat -import java.util.Locale class MainViewModel( - private val useCase: GetInfoUseCase, + private val infoUseCase: GetInfoUseCase, + private val listUseCase: GetEmployeeEntranceListUseCase, application: Application ) : AndroidViewModel(application) { + val listState = Pager( + config = PagingConfig( + pageSize = 10, + enablePlaceholders = false, + maxSize = 30 + ) + ) { + println("Creating PagingSource") + EmployeeEntranceListPagingSource(listUseCase::invoke) + }.flow.cachedIn(viewModelScope) + init { + viewModelScope.launch { + listState.collect { pagingData -> + if (pagingData.toString().isEmpty()) { + println("No data in paging data.") + } else { + println("Data received: $pagingData") + } + } + } + } + private val _userState = MutableStateFlow(UserState.Loading) val userState: StateFlow get() = _userState private val dataStoreManager = UserDataStoreManager(application) - fun formatDate(date: String): String { - val inputFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.getDefault()) - val outputFormat = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()) - return try { - val formattedDate = inputFormat.parse(date) - if (formattedDate != null) { - outputFormat.format(formattedDate) - } else{} - } catch (_: Exception) { - "Invalid Date" - }.toString() - } - fun getUserData() { _userState.value = UserState.Loading viewModelScope.launch { - useCase.invoke().fold( + infoUseCase.invoke().fold( onSuccess = { data -> _userState.value = UserState.Success(data) }, - onFailure = { _userState.value = UserState.Error } + onFailure = { e -> _userState.value = UserState.Error + println(e)} ) } } @@ -55,7 +70,7 @@ class MainViewModel( fun clearUsername() { viewModelScope.launch{ withContext(Dispatchers.IO) { - dataStoreManager.clearUsername() + dataStoreManager.clearCredentials() } } @@ -64,16 +79,22 @@ class MainViewModel( @Suppress("UNCHECKED_CAST") val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory { override fun create(modelClass: Class, extras: CreationExtras): T { - val repoImpl = InfoRepoImpl( + val infoRepoImpl = InfoRepoImpl( networkDataSource = InfoNetworkDataSource( context = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application ) ) + val listInfoImpl = EmployeeEntranceListRepoImpl( + networkDataSource = EmployeeEntranceListNetworkDataSource( + context = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application + ) + ) - val useCase = GetInfoUseCase(repoImpl) + val infoUseCase = GetInfoUseCase(infoRepoImpl) + val listUseCase = GetEmployeeEntranceListUseCase(listInfoImpl) return MainViewModel( - useCase, extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application + infoUseCase, listUseCase, extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application ) as T } } diff --git a/app/src/main/java/ru/myitschool/work/ui/qr/result/QrResultFragment.kt b/app/src/main/java/ru/myitschool/work/ui/qr/result/QrResultFragment.kt index 2ac5b52..a4e0933 100644 --- a/app/src/main/java/ru/myitschool/work/ui/qr/result/QrResultFragment.kt +++ b/app/src/main/java/ru/myitschool/work/ui/qr/result/QrResultFragment.kt @@ -7,7 +7,6 @@ import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import ru.myitschool.work.R import ru.myitschool.work.databinding.FragmentQrResultBinding -import ru.myitschool.work.domain.entities.OpenEntity import ru.myitschool.work.utils.collectWhenStarted class QrResultFragment : Fragment(R.layout.fragment_qr_result) { @@ -19,7 +18,7 @@ class QrResultFragment : Fragment(R.layout.fragment_qr_result) { _binding = FragmentQrResultBinding.bind(view) var qrData = arguments?.getString("qr_data") if (qrData != null) { - viewModel.openDoor(OpenEntity(qrData.toLong())) + viewModel.openDoor(qrData) } else{ binding.result.text = getString(R.string.result_null_text) diff --git a/app/src/main/java/ru/myitschool/work/ui/qr/result/QrResultViewModel.kt b/app/src/main/java/ru/myitschool/work/ui/qr/result/QrResultViewModel.kt index d9620e2..a21a426 100644 --- a/app/src/main/java/ru/myitschool/work/ui/qr/result/QrResultViewModel.kt +++ b/app/src/main/java/ru/myitschool/work/ui/qr/result/QrResultViewModel.kt @@ -13,7 +13,6 @@ import kotlinx.coroutines.launch import ru.myitschool.work.data.door.DoorNetworkDataSource import ru.myitschool.work.data.door.DoorRepoImpl import ru.myitschool.work.domain.door.OpenDoorUseCase -import ru.myitschool.work.domain.entities.OpenEntity class QrResultViewModel( private val useCase: OpenDoorUseCase, @@ -23,15 +22,15 @@ class QrResultViewModel( val state: StateFlow = _state.asStateFlow() sealed class State{ - object Success : State() - object Loading : State() - object Error : State() + data object Success : State() + data object Loading : State() + data object Error : State() } - fun openDoor(openEntity: OpenEntity){ + fun openDoor(code: String){ _state.value = State.Loading viewModelScope.launch{ - useCase.invoke(openEntity).fold( - onSuccess = { data-> + useCase.invoke(code).fold( + onSuccess = { _ -> _state.value = State.Success }, onFailure = { _ -> diff --git a/app/src/main/java/ru/myitschool/work/utils/DateConverter.kt b/app/src/main/java/ru/myitschool/work/utils/DateConverter.kt new file mode 100644 index 0000000..91d7210 --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/utils/DateConverter.kt @@ -0,0 +1,15 @@ +package ru.myitschool.work.utils + +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +fun dateConverter(date: Date?) : String { + if (date != null) { + val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) + println(dateFormat.format(date).toString()) + return dateFormat.format(date).toString() + + } + return "" +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/utils/DateSerializer.kt b/app/src/main/java/ru/myitschool/work/utils/DateSerializer.kt new file mode 100644 index 0000000..dfe858d --- /dev/null +++ b/app/src/main/java/ru/myitschool/work/utils/DateSerializer.kt @@ -0,0 +1,25 @@ +package ru.myitschool.work.utils + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +object DateSerializer : KSerializer { + private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US) + override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING) + + + override fun deserialize(decoder: Decoder): Date { + return dateFormat.parse(decoder.decodeString())!! + } + + override fun serialize(encoder: Encoder, value: Date) { + encoder.encodeString(dateFormat.format(value)) + } +} \ No newline at end of file diff --git a/app/src/main/java/ru/myitschool/work/utils/FragmentExtesions.kt b/app/src/main/java/ru/myitschool/work/utils/FragmentExtesions.kt index 8c99ef3..5cc563a 100644 --- a/app/src/main/java/ru/myitschool/work/utils/FragmentExtesions.kt +++ b/app/src/main/java/ru/myitschool/work/utils/FragmentExtesions.kt @@ -1,8 +1,10 @@ package ru.myitschool.work.utils import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch @@ -15,4 +17,14 @@ inline fun Flow.collectWhenStarted( collector.invoke(value) } } +} +fun Flow.collectWithLifecycle( + fragment: Fragment, + function: suspend (T) -> Unit +){ + fragment.viewLifecycleOwner.lifecycleScope.launch { + fragment.repeatOnLifecycle(Lifecycle.State.STARTED) { + collect { function.invoke(it) } + } + } } \ No newline at end of file diff --git a/app/src/main/res/font/montserrat_bold.ttf b/app/src/main/res/font/montserrat_bold.ttf deleted file mode 100644 index 4df50da..0000000 Binary files a/app/src/main/res/font/montserrat_bold.ttf and /dev/null differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c9cc1bc..6a01b6e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,6 +17,7 @@ lifecycleLivedataKtx = "2.8.7" lifecycleViewmodelKtx = "2.8.7" navigationFragmentKtx = "2.8.7" navigationUiKtx = "2.8.7" +pagingRuntimeKtx = "3.3.6" picasso = "2.8" [libraries] androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" } @@ -29,8 +30,10 @@ androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = " androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } androidx-navigation-ui-ktx = { module = "androidx.navigation:navigation-ui-ktx", version.ref = "navigationUiKtx" } androidx-navigation-fragment-ktx = { module = "androidx.navigation:navigation-fragment-ktx", version.ref = "navigationFragmentKtx" } +androidx-paging-runtime-ktx = { module = "androidx.paging:paging-runtime-ktx", version.ref = "pagingRuntimeKtx" } barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "barcodeScanning" } hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } +hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" } ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientContentNegotiation" } ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktorClientContentNegotiation" }