Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
a3e2a923b8
@ -1,3 +1,5 @@
|
|||||||
|
import com.android.sdklib.AndroidVersion.VersionCodes.UPSIDE_DOWN_CAKE
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
androidApplication
|
androidApplication
|
||||||
jetbrainsKotlinSerialization version Version.Kotlin.language
|
jetbrainsKotlinSerialization version Version.Kotlin.language
|
||||||
@ -9,12 +11,12 @@ plugins {
|
|||||||
val packageName = "ru.myitschool.work"
|
val packageName = "ru.myitschool.work"
|
||||||
android {
|
android {
|
||||||
namespace = packageName
|
namespace = packageName
|
||||||
compileSdk = 35
|
compileSdk = UPSIDE_DOWN_CAKE
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = packageName
|
applicationId = packageName
|
||||||
minSdk = 31
|
minSdk = 31
|
||||||
targetSdk = 35
|
targetSdk = UPSIDE_DOWN_CAKE
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
|
||||||
@ -38,12 +40,11 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.androidx.appcompat)
|
implementation(libs.androidx.appcompat)
|
||||||
implementation(libs.material)
|
implementation(libs.material)
|
||||||
implementation(libs.androidx.activity.ktx)
|
|
||||||
implementation(libs.androidx.constraintlayout)
|
implementation(libs.androidx.constraintlayout)
|
||||||
implementation(libs.androidx.annotation)
|
implementation(libs.androidx.annotation)
|
||||||
implementation(libs.androidx.lifecycle.livedata.ktx)
|
implementation(libs.androidx.lifecycle.livedata.ktx)
|
||||||
implementation(libs.androidx.lifecycle.viewmodel.ktx)
|
implementation(libs.androidx.lifecycle.viewmodel.ktx)
|
||||||
defaultLibrary()
|
|
||||||
|
|
||||||
implementation(Dependencies.AndroidX.activity)
|
implementation(Dependencies.AndroidX.activity)
|
||||||
implementation(Dependencies.AndroidX.fragment)
|
implementation(Dependencies.AndroidX.fragment)
|
||||||
@ -65,10 +66,10 @@ dependencies {
|
|||||||
implementation(libs.androidx.camera.lifecycle)
|
implementation(libs.androidx.camera.lifecycle)
|
||||||
implementation(libs.androidx.camera.view)
|
implementation(libs.androidx.camera.view)
|
||||||
implementation(libs.androidx.camera.mlkit.vision)
|
implementation(libs.androidx.camera.mlkit.vision)
|
||||||
|
implementation(libs.androidx.paging.runtime.ktx)
|
||||||
val hilt = "2.51.1"
|
defaultLibrary()
|
||||||
implementation(libs.hilt.android)
|
implementation(libs.hilt.android)
|
||||||
kapt("com.google.dagger:hilt-android-compiler:$hilt")
|
kapt(libs.hilt.android.compiler)
|
||||||
}
|
}
|
||||||
|
|
||||||
kapt {
|
kapt {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
package ru.myitschool.work.core
|
package ru.myitschool.work.core
|
||||||
// БЕРИТЕ И ИЗМЕНЯЙТЕ ХОСТ ТОЛЬКО ЗДЕСЬ И НЕ БЕРИТЕ ИЗ ДРУГИХ МЕСТ. ФАЙЛ ПЕРЕМЕЩАТЬ НЕЛЬЗЯ
|
// БЕРИТЕ И ИЗМЕНЯЙТЕ ХОСТ ТОЛЬКО ЗДЕСЬ И НЕ БЕРИТЕ ИЗ ДРУГИХ МЕСТ. ФАЙЛ ПЕРЕМЕЩАТЬ НЕЛЬЗЯ
|
||||||
object Constants {
|
object Constants {
|
||||||
const val SERVER_ADDRESS = "http://10.0.2.2:8080"
|
const val SERVER_ADDRESS = "http://192.168.1.145:8080"
|
||||||
}
|
}
|
@ -12,6 +12,8 @@ class UserDataStoreManager(private val context: Context) {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val USERNAME_KEY = stringPreferencesKey("username")
|
private val USERNAME_KEY = stringPreferencesKey("username")
|
||||||
|
private val ROLE_KEY = stringPreferencesKey("role")
|
||||||
|
private val PASSWORD_KEY = stringPreferencesKey("password")
|
||||||
|
|
||||||
fun getInstance(context: Context): UserDataStoreManager {
|
fun getInstance(context: Context): UserDataStoreManager {
|
||||||
return UserDataStoreManager(context.applicationContext)
|
return UserDataStoreManager(context.applicationContext)
|
||||||
@ -21,13 +23,24 @@ class UserDataStoreManager(private val context: Context) {
|
|||||||
val usernameFlow: Flow<String> = context.applicationContext.dataStore.data.map { prefs ->
|
val usernameFlow: Flow<String> = context.applicationContext.dataStore.data.map { prefs ->
|
||||||
prefs[USERNAME_KEY] ?: ""
|
prefs[USERNAME_KEY] ?: ""
|
||||||
}
|
}
|
||||||
suspend fun saveUsername(username: String) {
|
val passwordFlow: Flow<String> = context.applicationContext.dataStore.data.map { prefs ->
|
||||||
|
prefs[PASSWORD_KEY] ?: ""
|
||||||
|
}
|
||||||
|
val roleFlow: Flow<String> = context.applicationContext.dataStore.data.map{ prefs ->
|
||||||
|
prefs[ROLE_KEY] ?: ""
|
||||||
|
}
|
||||||
|
suspend fun saveCredentials(username: String, password: String) {
|
||||||
context.dataStore.edit { prefs ->
|
context.dataStore.edit { prefs ->
|
||||||
prefs[USERNAME_KEY] = username
|
prefs[USERNAME_KEY] = username
|
||||||
|
prefs[PASSWORD_KEY] = password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
suspend fun saveRole(role: String){
|
||||||
suspend fun clearUsername() {
|
context.dataStore.edit { prefs ->
|
||||||
|
prefs[ROLE_KEY] = role
|
||||||
|
}
|
||||||
|
}
|
||||||
|
suspend fun clearCredentials() {
|
||||||
context.applicationContext.dataStore.edit { it.clear() }
|
context.applicationContext.dataStore.edit { it.clear() }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,18 +2,16 @@ package ru.myitschool.work.data.door
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import io.ktor.client.call.body
|
import io.ktor.client.call.body
|
||||||
|
import io.ktor.client.request.basicAuth
|
||||||
import io.ktor.client.request.patch
|
import io.ktor.client.request.patch
|
||||||
import io.ktor.client.request.setBody
|
|
||||||
import io.ktor.client.statement.bodyAsText
|
import io.ktor.client.statement.bodyAsText
|
||||||
import io.ktor.http.ContentType
|
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
import io.ktor.http.contentType
|
import io.ktor.http.headers
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import ru.myitschool.work.core.Constants
|
import ru.myitschool.work.core.Constants
|
||||||
import ru.myitschool.work.data.UserDataStoreManager
|
import ru.myitschool.work.data.UserDataStoreManager
|
||||||
import ru.myitschool.work.data.dto.OpenRequestDTO
|
|
||||||
import ru.myitschool.work.utils.NetworkModule
|
import ru.myitschool.work.utils.NetworkModule
|
||||||
|
|
||||||
class DoorNetworkDataSource(
|
class DoorNetworkDataSource(
|
||||||
@ -21,12 +19,14 @@ class DoorNetworkDataSource(
|
|||||||
) {
|
) {
|
||||||
private val client = NetworkModule.httpClient
|
private val client = NetworkModule.httpClient
|
||||||
private val userDataStoreManager = UserDataStoreManager.getInstance(context)
|
private val userDataStoreManager = UserDataStoreManager.getInstance(context)
|
||||||
suspend fun openDoor(openRequestDTO: OpenRequestDTO): Result<Unit> = withContext(Dispatchers.IO){
|
suspend fun openDoor(code : String): Result<Unit> = withContext(Dispatchers.IO){
|
||||||
runCatching {
|
runCatching {
|
||||||
val username = userDataStoreManager.usernameFlow.first()
|
val username = userDataStoreManager.usernameFlow.first()
|
||||||
val result = client.patch("${Constants.SERVER_ADDRESS}/api/$username/open"){
|
val password = userDataStoreManager.passwordFlow.first()
|
||||||
contentType(ContentType.Application.Json)
|
val result = client.patch("${Constants.SERVER_ADDRESS}/api/employee/open?code=$code"){
|
||||||
setBody(openRequestDTO)
|
headers{
|
||||||
|
basicAuth(username, password)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (result.status != HttpStatusCode.OK) {
|
if (result.status != HttpStatusCode.OK) {
|
||||||
error("Status ${result.status}")
|
error("Status ${result.status}")
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
package ru.myitschool.work.data.door
|
package ru.myitschool.work.data.door
|
||||||
|
|
||||||
import ru.myitschool.work.domain.door.DoorRepo
|
import ru.myitschool.work.domain.door.DoorRepo
|
||||||
import ru.myitschool.work.domain.entities.OpenEntity
|
|
||||||
|
|
||||||
class DoorRepoImpl(
|
class DoorRepoImpl(
|
||||||
private val networkDataSource: DoorNetworkDataSource
|
private val networkDataSource: DoorNetworkDataSource
|
||||||
) : DoorRepo {
|
) : DoorRepo {
|
||||||
override suspend fun openDoor(openEntity: OpenEntity): Result<Unit> {
|
override suspend fun openDoor(code: String): Result<Unit> {
|
||||||
return networkDataSource.openDoor(openEntity.toDto())
|
return networkDataSource.openDoor(code)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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?
|
||||||
|
)
|
@ -4,6 +4,6 @@ import kotlinx.serialization.SerialName
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class OpenRequestDTO(
|
data class EmployeeEntranceListPagingDTO(
|
||||||
@SerialName("value") val value: Long
|
@SerialName("content") val content : List<EmployeeEntranceDTO>?
|
||||||
)
|
)
|
@ -5,11 +5,11 @@ import kotlinx.serialization.Serializable
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class UserDTO(
|
data class UserDTO(
|
||||||
@SerialName("id") val id: Int,
|
@SerialName("id") val id: Long?,
|
||||||
@SerialName("login") val login: String,
|
@SerialName("login") val login: String?,
|
||||||
@SerialName("name") val name: String,
|
@SerialName("name") val name: String?,
|
||||||
@SerialName("photo") val photo: String,
|
@SerialName("authority") val authority : String?,
|
||||||
@SerialName("position") val position: String,
|
@SerialName("photoUrl") val photoUrl: String?,
|
||||||
@SerialName("lastVisit") val lastVisit: String
|
@SerialName("position") val position: String?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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<EmployeeEntranceListPagingDTO> = 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<List<EmployeeEntranceEntity>> {
|
||||||
|
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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<EmployeeEntranceListPagingDTO> = 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<List<EmployeeEntranceEntity>> {
|
||||||
|
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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,11 @@ package ru.myitschool.work.data.info
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import io.ktor.client.call.body
|
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.client.statement.bodyAsText
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.http.headers
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -22,8 +24,12 @@ class InfoNetworkDataSource(
|
|||||||
suspend fun getInfo():Result<UserDTO> = withContext(Dispatchers.IO){
|
suspend fun getInfo():Result<UserDTO> = withContext(Dispatchers.IO){
|
||||||
runCatching {
|
runCatching {
|
||||||
val username = userDataStoreManager.usernameFlow.first()
|
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) {
|
if (result.status != HttpStatusCode.OK) {
|
||||||
error("Status ${result.status}")
|
error("Status ${result.status}")
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,16 @@ class InfoRepoImpl(
|
|||||||
private val networkDataSource: InfoNetworkDataSource
|
private val networkDataSource: InfoNetworkDataSource
|
||||||
): InfoRepo {
|
): InfoRepo {
|
||||||
override suspend fun getInfo(): Result<UserEntity> {
|
override suspend fun getInfo(): Result<UserEntity> {
|
||||||
return networkDataSource.getInfo().map { dto ->
|
return networkDataSource.getInfo().map { dto->
|
||||||
UserEntity(
|
UserEntity(
|
||||||
name = dto.name,
|
id = dto.id ?: 0,
|
||||||
position = dto.position,
|
login = dto.login ?: "",
|
||||||
lastVisit = dto.lastVisit,
|
name = dto.name ?: "",
|
||||||
photo = dto.photo
|
authority = dto.authority ?: "",
|
||||||
|
photoUrl = dto.photoUrl,
|
||||||
|
position = dto.position ?: ""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +1,11 @@
|
|||||||
package ru.myitschool.work.data.login
|
package ru.myitschool.work.data.login
|
||||||
|
|
||||||
import io.ktor.client.call.body
|
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.client.statement.bodyAsText
|
||||||
import io.ktor.http.HttpStatusCode
|
import io.ktor.http.HttpStatusCode
|
||||||
|
import io.ktor.http.headers
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import ru.myitschool.work.core.Constants
|
import ru.myitschool.work.core.Constants
|
||||||
@ -11,10 +13,14 @@ import ru.myitschool.work.utils.NetworkModule
|
|||||||
|
|
||||||
class LoginNetworkDataSource {
|
class LoginNetworkDataSource {
|
||||||
private val client = NetworkModule.httpClient
|
private val client = NetworkModule.httpClient
|
||||||
suspend fun login(username: String):Result<Unit> = withContext(Dispatchers.IO){
|
suspend fun login(username: String, password: String):Result<Unit> = withContext(Dispatchers.IO){
|
||||||
runCatching {
|
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) {
|
if (result.status != HttpStatusCode.OK) {
|
||||||
error("Status ${result.status}")
|
error("Status ${result.status}")
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import ru.myitschool.work.domain.login.LoginRepo
|
|||||||
class LoginRepoImpl(
|
class LoginRepoImpl(
|
||||||
private val networkDataSource: LoginNetworkDataSource
|
private val networkDataSource: LoginNetworkDataSource
|
||||||
) : LoginRepo {
|
) : LoginRepo {
|
||||||
override suspend fun login(username: String): Result<Unit> {
|
override suspend fun login(username: String, password: String): Result<Unit> {
|
||||||
return networkDataSource.login(username)
|
return networkDataSource.login(username, password)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,5 @@
|
|||||||
package ru.myitschool.work.domain.door
|
package ru.myitschool.work.domain.door
|
||||||
|
|
||||||
import ru.myitschool.work.domain.entities.OpenEntity
|
|
||||||
|
|
||||||
interface DoorRepo {
|
interface DoorRepo {
|
||||||
suspend fun openDoor(openEntity: OpenEntity) : Result<Unit>
|
suspend fun openDoor(code: String) : Result<Unit>
|
||||||
}
|
}
|
@ -1,11 +1,7 @@
|
|||||||
package ru.myitschool.work.domain.door
|
package ru.myitschool.work.domain.door
|
||||||
|
|
||||||
import ru.myitschool.work.domain.entities.OpenEntity
|
|
||||||
|
|
||||||
class OpenDoorUseCase(
|
class OpenDoorUseCase(
|
||||||
private val repo: DoorRepo
|
private val repo: DoorRepo
|
||||||
) {
|
) {
|
||||||
suspend operator fun invoke(openEntity: OpenEntity) = repo.openDoor(
|
suspend operator fun invoke(code: String) = repo.openDoor(code)
|
||||||
openEntity = openEntity
|
|
||||||
)
|
|
||||||
}
|
}
|
@ -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<List<EmployeeEntranceEntity>>
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
@ -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<List<EmployeeEntranceEntity>>
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
@ -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?
|
||||||
|
)
|
@ -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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,10 @@
|
|||||||
package ru.myitschool.work.domain.entities
|
package ru.myitschool.work.domain.entities
|
||||||
|
|
||||||
data class UserEntity (
|
data class UserEntity(
|
||||||
|
val id: Long,
|
||||||
|
val login: String,
|
||||||
val name: String,
|
val name: String,
|
||||||
val photo: String,
|
val authority: String,
|
||||||
val position: String,
|
val photoUrl: String?,
|
||||||
val lastVisit: String
|
val position: String
|
||||||
)
|
)
|
@ -1,5 +1,5 @@
|
|||||||
package ru.myitschool.work.domain.login
|
package ru.myitschool.work.domain.login
|
||||||
|
|
||||||
interface LoginRepo {
|
interface LoginRepo {
|
||||||
suspend fun login(username: String): Result<Unit>
|
suspend fun login(username: String, password: String): Result<Unit>
|
||||||
}
|
}
|
@ -3,5 +3,5 @@ package ru.myitschool.work.domain.login
|
|||||||
class LoginUseCase(
|
class LoginUseCase(
|
||||||
private val repo: LoginRepo
|
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)
|
||||||
}
|
}
|
@ -29,25 +29,44 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
|
|||||||
|
|
||||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
val username = binding.username.text
|
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.username.addTextChangedListener(textWatcher)
|
||||||
|
binding.password.addTextChangedListener(textWatcher)
|
||||||
|
|
||||||
binding.loginBtn.isEnabled = false
|
binding.loginBtn.isEnabled = false
|
||||||
binding.loginBtn.setOnClickListener{
|
binding.loginBtn.setOnClickListener{
|
||||||
viewModel.login(binding.username.text.toString())
|
viewModel.login(binding.username.text.toString(), binding.password.text.toString())
|
||||||
|
|
||||||
}
|
}
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
viewModel.state.collect { state ->
|
viewModel.state.collect { state ->
|
||||||
with(binding) {
|
with(binding) {
|
||||||
error.visibility = if (state is LoginViewModel.State.Error) View.VISIBLE else View.GONE
|
when(state){
|
||||||
username.isEnabled = state !is LoginViewModel.State.Loading
|
is LoginViewModel.State.Error -> {
|
||||||
|
error.visibility = View.VISIBLE
|
||||||
if (state is LoginViewModel.State.Success) {
|
loading.visibility = View.GONE
|
||||||
findNavController().navigate(R.id.mainFragment)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,26 +27,27 @@ class LoginViewModel(
|
|||||||
init {
|
init {
|
||||||
viewModelScope.launch{
|
viewModelScope.launch{
|
||||||
val username = dataStoreManager.usernameFlow.first()
|
val username = dataStoreManager.usernameFlow.first()
|
||||||
if(username != "")
|
val password = dataStoreManager.passwordFlow.first()
|
||||||
login(username)
|
if(username != "" && password != "")
|
||||||
|
login(username, password)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class State {
|
sealed class State {
|
||||||
object Idle : State()
|
data object Idle : State()
|
||||||
object Loading : State()
|
data object Loading : State()
|
||||||
object Success : State()
|
data object Success : State()
|
||||||
data class Error(val message: String?) : State()
|
data class Error(val message: String?) : State()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun login(username: String) {
|
fun login(username: String, password: String) {
|
||||||
_state.value = State.Loading
|
_state.value = State.Loading
|
||||||
viewModelScope.launch{
|
viewModelScope.launch{
|
||||||
useCase.invoke(username).fold(
|
useCase.invoke(username, password).fold(
|
||||||
onSuccess = { data ->
|
onSuccess = { _ ->
|
||||||
dataStoreManager.saveUsername(username)
|
dataStoreManager.saveCredentials(username, password)
|
||||||
_state.value = State.Success
|
_state.value = State.Success
|
||||||
},
|
},
|
||||||
onFailure = {e->
|
onFailure = {e->
|
||||||
|
@ -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<EmployeeEntranceEntity, EmployeeEntranceListAdapter.ViewHolder>(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<EmployeeEntranceEntity>() {
|
||||||
|
override fun areItemsTheSame(oldItem: EmployeeEntranceEntity, newItem: EmployeeEntranceEntity): Boolean {
|
||||||
|
return oldItem.scanTime == newItem.scanTime
|
||||||
|
}
|
||||||
|
override fun areContentsTheSame(oldItem: EmployeeEntranceEntity, newItem: EmployeeEntranceEntity): Boolean {
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<List<EmployeeEntranceEntity>>
|
||||||
|
) : PagingSource<Int, EmployeeEntranceEntity>() {
|
||||||
|
override fun getRefreshKey(state: PagingState<Int, EmployeeEntranceEntity>): Int? {
|
||||||
|
return state.anchorPosition?.let{
|
||||||
|
state.closestPageToPosition(it)?.prevKey?.plus(1) ?:
|
||||||
|
state.closestPageToPosition(it)?.nextKey?.minus(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, EmployeeEntranceEntity> {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,8 @@ import androidx.fragment.app.setFragmentResultListener
|
|||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import androidx.paging.LoadState
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
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.ui.qr.scan.QrScanDestination
|
||||||
import ru.myitschool.work.utils.UserState
|
import ru.myitschool.work.utils.UserState
|
||||||
import ru.myitschool.work.utils.collectWhenStarted
|
import ru.myitschool.work.utils.collectWhenStarted
|
||||||
|
import ru.myitschool.work.utils.collectWithLifecycle
|
||||||
|
|
||||||
class MainFragment : Fragment(R.layout.fragment_main) {
|
class MainFragment : Fragment(R.layout.fragment_main) {
|
||||||
|
|
||||||
@ -31,7 +34,7 @@ class MainFragment : Fragment(R.layout.fragment_main) {
|
|||||||
_binding = FragmentMainBinding.bind(view)
|
_binding = FragmentMainBinding.bind(view)
|
||||||
|
|
||||||
viewModel.getUserData()
|
viewModel.getUserData()
|
||||||
binding.refresh.setOnClickListener { viewModel.getUserData() }
|
|
||||||
binding.logout.setOnClickListener { logout() }
|
binding.logout.setOnClickListener { logout() }
|
||||||
binding.scan.setOnClickListener { onScanClick() }
|
binding.scan.setOnClickListener { onScanClick() }
|
||||||
viewModel.userState.collectWhenStarted(this) { state ->
|
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 ->
|
setFragmentResultListener(QrScanDestination.REQUEST_KEY) { _, bundle ->
|
||||||
val qrData = QrScanDestination.getDataIfExist(bundle)
|
val qrData = QrScanDestination.getDataIfExist(bundle)
|
||||||
println(qrData)
|
println(qrData)
|
||||||
@ -81,9 +105,9 @@ class MainFragment : Fragment(R.layout.fragment_main) {
|
|||||||
private fun showUserData(userEntity: UserEntity) {
|
private fun showUserData(userEntity: UserEntity) {
|
||||||
binding.apply {
|
binding.apply {
|
||||||
fullname.text = userEntity.name
|
fullname.text = userEntity.name
|
||||||
|
println(userEntity.name)
|
||||||
position.text = userEntity.position
|
position.text = userEntity.position
|
||||||
lastEntry.text = viewModel.formatDate(userEntity.lastVisit)
|
Picasso.get().load(userEntity.photoUrl).into(photo)
|
||||||
Picasso.get().load(userEntity.photo).into(photo)
|
|
||||||
|
|
||||||
error.visibility = View.GONE
|
error.visibility = View.GONE
|
||||||
setViewsVisibility(View.VISIBLE)
|
setViewsVisibility(View.VISIBLE)
|
||||||
@ -98,7 +122,6 @@ class MainFragment : Fragment(R.layout.fragment_main) {
|
|||||||
private fun setViewsVisibility(visibility: Int) {
|
private fun setViewsVisibility(visibility: Int) {
|
||||||
binding.fullname.visibility = visibility
|
binding.fullname.visibility = visibility
|
||||||
binding.position.visibility = visibility
|
binding.position.visibility = visibility
|
||||||
binding.lastEntry.visibility = visibility
|
|
||||||
binding.photo.visibility = visibility
|
binding.photo.visibility = visibility
|
||||||
binding.logout.visibility = visibility
|
binding.logout.visibility = visibility
|
||||||
binding.scan.visibility = visibility
|
binding.scan.visibility = visibility
|
||||||
|
@ -6,6 +6,9 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.lifecycle.viewmodel.CreationExtras
|
import androidx.lifecycle.viewmodel.CreationExtras
|
||||||
|
import androidx.paging.Pager
|
||||||
|
import androidx.paging.PagingConfig
|
||||||
|
import androidx.paging.cachedIn
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
@ -14,40 +17,52 @@ import kotlinx.coroutines.withContext
|
|||||||
import ru.myitschool.work.data.UserDataStoreManager
|
import ru.myitschool.work.data.UserDataStoreManager
|
||||||
import ru.myitschool.work.data.info.InfoNetworkDataSource
|
import ru.myitschool.work.data.info.InfoNetworkDataSource
|
||||||
import ru.myitschool.work.data.info.InfoRepoImpl
|
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.info.GetInfoUseCase
|
||||||
|
import ru.myitschool.work.domain.employeeEntrance.employeeEntrances.GetEmployeeEntranceListUseCase
|
||||||
import ru.myitschool.work.utils.UserState
|
import ru.myitschool.work.utils.UserState
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.Locale
|
|
||||||
|
|
||||||
class MainViewModel(
|
class MainViewModel(
|
||||||
private val useCase: GetInfoUseCase,
|
private val infoUseCase: GetInfoUseCase,
|
||||||
|
private val listUseCase: GetEmployeeEntranceListUseCase,
|
||||||
application: Application
|
application: Application
|
||||||
) : AndroidViewModel(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>(UserState.Loading)
|
private val _userState = MutableStateFlow<UserState>(UserState.Loading)
|
||||||
val userState: StateFlow<UserState> get() = _userState
|
val userState: StateFlow<UserState> get() = _userState
|
||||||
|
|
||||||
private val dataStoreManager = UserDataStoreManager(application)
|
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() {
|
fun getUserData() {
|
||||||
_userState.value = UserState.Loading
|
_userState.value = UserState.Loading
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
useCase.invoke().fold(
|
infoUseCase.invoke().fold(
|
||||||
onSuccess = { data -> _userState.value = UserState.Success(data) },
|
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() {
|
fun clearUsername() {
|
||||||
viewModelScope.launch{
|
viewModelScope.launch{
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
dataStoreManager.clearUsername()
|
dataStoreManager.clearCredentials()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,16 +79,22 @@ class MainViewModel(
|
|||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
|
val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
|
||||||
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
|
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
|
||||||
val repoImpl = InfoRepoImpl(
|
val infoRepoImpl = InfoRepoImpl(
|
||||||
networkDataSource = InfoNetworkDataSource(
|
networkDataSource = InfoNetworkDataSource(
|
||||||
context = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
|
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(
|
return MainViewModel(
|
||||||
useCase, extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
|
infoUseCase, listUseCase, extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
|
||||||
) as T
|
) as T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import androidx.fragment.app.viewModels
|
|||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import ru.myitschool.work.R
|
import ru.myitschool.work.R
|
||||||
import ru.myitschool.work.databinding.FragmentQrResultBinding
|
import ru.myitschool.work.databinding.FragmentQrResultBinding
|
||||||
import ru.myitschool.work.domain.entities.OpenEntity
|
|
||||||
import ru.myitschool.work.utils.collectWhenStarted
|
import ru.myitschool.work.utils.collectWhenStarted
|
||||||
|
|
||||||
class QrResultFragment : Fragment(R.layout.fragment_qr_result) {
|
class QrResultFragment : Fragment(R.layout.fragment_qr_result) {
|
||||||
@ -19,7 +18,7 @@ class QrResultFragment : Fragment(R.layout.fragment_qr_result) {
|
|||||||
_binding = FragmentQrResultBinding.bind(view)
|
_binding = FragmentQrResultBinding.bind(view)
|
||||||
var qrData = arguments?.getString("qr_data")
|
var qrData = arguments?.getString("qr_data")
|
||||||
if (qrData != null) {
|
if (qrData != null) {
|
||||||
viewModel.openDoor(OpenEntity(qrData.toLong()))
|
viewModel.openDoor(qrData)
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
binding.result.text = getString(R.string.result_null_text)
|
binding.result.text = getString(R.string.result_null_text)
|
||||||
|
@ -13,7 +13,6 @@ import kotlinx.coroutines.launch
|
|||||||
import ru.myitschool.work.data.door.DoorNetworkDataSource
|
import ru.myitschool.work.data.door.DoorNetworkDataSource
|
||||||
import ru.myitschool.work.data.door.DoorRepoImpl
|
import ru.myitschool.work.data.door.DoorRepoImpl
|
||||||
import ru.myitschool.work.domain.door.OpenDoorUseCase
|
import ru.myitschool.work.domain.door.OpenDoorUseCase
|
||||||
import ru.myitschool.work.domain.entities.OpenEntity
|
|
||||||
|
|
||||||
class QrResultViewModel(
|
class QrResultViewModel(
|
||||||
private val useCase: OpenDoorUseCase,
|
private val useCase: OpenDoorUseCase,
|
||||||
@ -23,15 +22,15 @@ class QrResultViewModel(
|
|||||||
val state: StateFlow<State> = _state.asStateFlow()
|
val state: StateFlow<State> = _state.asStateFlow()
|
||||||
|
|
||||||
sealed class State{
|
sealed class State{
|
||||||
object Success : State()
|
data object Success : State()
|
||||||
object Loading : State()
|
data object Loading : State()
|
||||||
object Error : State()
|
data object Error : State()
|
||||||
}
|
}
|
||||||
fun openDoor(openEntity: OpenEntity){
|
fun openDoor(code: String){
|
||||||
_state.value = State.Loading
|
_state.value = State.Loading
|
||||||
viewModelScope.launch{
|
viewModelScope.launch{
|
||||||
useCase.invoke(openEntity).fold(
|
useCase.invoke(code).fold(
|
||||||
onSuccess = { data->
|
onSuccess = { _ ->
|
||||||
_state.value = State.Success
|
_state.value = State.Success
|
||||||
},
|
},
|
||||||
onFailure = { _ ->
|
onFailure = { _ ->
|
||||||
|
15
app/src/main/java/ru/myitschool/work/utils/DateConverter.kt
Normal file
15
app/src/main/java/ru/myitschool/work/utils/DateConverter.kt
Normal file
@ -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 ""
|
||||||
|
}
|
25
app/src/main/java/ru/myitschool/work/utils/DateSerializer.kt
Normal file
25
app/src/main/java/ru/myitschool/work/utils/DateSerializer.kt
Normal file
@ -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<Date> {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
package ru.myitschool.work.utils
|
package ru.myitschool.work.utils
|
||||||
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.flowWithLifecycle
|
import androidx.lifecycle.flowWithLifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@ -15,4 +17,14 @@ inline fun <T> Flow<T>.collectWhenStarted(
|
|||||||
collector.invoke(value)
|
collector.invoke(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
fun <T> Flow<T>.collectWithLifecycle(
|
||||||
|
fragment: Fragment,
|
||||||
|
function: suspend (T) -> Unit
|
||||||
|
){
|
||||||
|
fragment.viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
fragment.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
collect { function.invoke(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Binary file not shown.
@ -17,6 +17,7 @@ lifecycleLivedataKtx = "2.8.7"
|
|||||||
lifecycleViewmodelKtx = "2.8.7"
|
lifecycleViewmodelKtx = "2.8.7"
|
||||||
navigationFragmentKtx = "2.8.7"
|
navigationFragmentKtx = "2.8.7"
|
||||||
navigationUiKtx = "2.8.7"
|
navigationUiKtx = "2.8.7"
|
||||||
|
pagingRuntimeKtx = "3.3.6"
|
||||||
picasso = "2.8"
|
picasso = "2.8"
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" }
|
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-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-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-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" }
|
barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "barcodeScanning" }
|
||||||
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
|
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" }
|
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-core = { module = "io.ktor:ktor-client-core", version.ref = "ktorClientContentNegotiation" }
|
||||||
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktorClientContentNegotiation" }
|
ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktorClientContentNegotiation" }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user