added gate and visit services, moved acs methods and DTOs to their repositories

This commit is contained in:
Izlydov 2025-02-20 10:43:02 +03:00
parent ba9f60b50c
commit a9e9bdf257
15 changed files with 203 additions and 118 deletions

View File

@ -1,11 +1,15 @@
package com.displaynone package com.displaynone
import android.app.Application import android.app.Application
import com.displaynone.acss.components.acs.models.gate.GateServiceST
import com.displaynone.acss.components.acs.models.visit.VisitServiceST
import com.displaynone.acss.components.auth.models.user.UserServiceST import com.displaynone.acss.components.auth.models.user.UserServiceST
class ACSSApplication : Application() { class ACSSApplication : Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
UserServiceST.createInstance(this) UserServiceST.createInstance(this)
GateServiceST.createInstance()
VisitServiceST.createInstance()
} }
} }

View File

@ -1,4 +1,28 @@
package com.displaynone.acss.components.acs.models.gate package com.displaynone.acss.components.acs.models.gate
import android.content.Context
import android.util.Log
import com.displaynone.acss.components.acs.models.gate.repository.GateRepository
import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager
import com.displaynone.acss.components.auth.internal_utils.UserManager
import com.displaynone.acss.components.auth.models.user.UserServiceST
class GateServiceST { class GateServiceST {
companion object {
private var instance: GateServiceST? = null
fun createInstance() {
if (instance == null) {
instance = GateServiceST()
}
}
fun getInstance(): GateServiceST {
return instance ?: throw RuntimeException("null instance")
}
}
private val gateRepository: GateRepository = GateRepository()
suspend fun openDoor(code: String): Result<Int> {
Log.d("1234", UserServiceST.getInstance().getTokenPair().accessToken)
return gateRepository.openDoor(UserServiceST.getInstance().getTokenPair().accessToken, code = code)
}
} }

View File

@ -0,0 +1,37 @@
package com.displaynone.acss.components.acs.models.gate.repository
import android.util.Log
import com.displaynone.acss.config.Constants.serverUrl
import com.displaynone.acss.config.Network
import io.ktor.client.call.body
import io.ktor.client.request.headers
import io.ktor.client.request.patch
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class GateRepository {
suspend fun openDoor(token: String, code: String): Result<Int> = withContext(Dispatchers.IO){
runCatching{
val result = Network.client.post("$serverUrl/api/acs/open") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
contentType(ContentType.Application.Json)
setBody("""{ "code": $code }""")
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
result.status.value
}
}
}

View File

@ -1,13 +1,11 @@
package com.displaynone.acss.components.auth.models.user.repository package com.displaynone.acss.components.acs.models.visit
import android.graphics.Color
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.paging.PagingDataAdapter import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.displaynone.acss.components.acs.models.visit.repository.dto.VisitDto
import com.displaynone.acss.components.auth.models.user.repository.dto.VisitDto
import com.displaynone.acss.databinding.ItemScannerViewBinding import com.displaynone.acss.databinding.ItemScannerViewBinding
class VisitAdapter: PagingDataAdapter<VisitDto, VisitAdapter.ViewHolder>(VisitDiff) { class VisitAdapter: PagingDataAdapter<VisitDto, VisitAdapter.ViewHolder>(VisitDiff) {
@ -29,7 +27,8 @@ class VisitAdapter: PagingDataAdapter<VisitDto, VisitAdapter.ViewHolder>(VisitDi
userId = -1, userId = -1,
gateId = -1, gateId = -1,
createdAt = "Loading...", createdAt = "Loading...",
)) )
)
} }

View File

@ -1,8 +1,8 @@
package com.displaynone.acss.components.auth.models.user.repository package com.displaynone.acss.components.acs.models.visit
import androidx.paging.PagingSource import androidx.paging.PagingSource
import androidx.paging.PagingState import androidx.paging.PagingState
import com.displaynone.acss.components.auth.models.user.repository.dto.VisitDto import com.displaynone.acss.components.acs.models.visit.repository.dto.VisitDto
class VisitListPagingSource( class VisitListPagingSource(
private val request: suspend (pageNum: Int, pageSize: Int) -> Result<List<VisitDto>> private val request: suspend (pageNum: Int, pageSize: Int) -> Result<List<VisitDto>>

View File

@ -1,4 +1,45 @@
package com.displaynone.acss.components.acs.models.visit package com.displaynone.acss.components.acs.models.visit
import android.content.Context
import com.displaynone.acss.components.acs.models.visit.repository.VisitRepository
import com.displaynone.acss.components.acs.models.visit.repository.dto.VisitDto
import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager
import com.displaynone.acss.components.auth.internal_utils.UserManager
import com.displaynone.acss.components.auth.models.user.UserServiceST
class VisitServiceST { class VisitServiceST {
companion object {
private var instance: VisitServiceST? = null
fun createInstance() {
if (instance == null) {
instance = VisitServiceST()
}
}
fun getInstance(): VisitServiceST {
return instance ?: throw RuntimeException("null instance")
}
}
private val visitRepository: VisitRepository = VisitRepository()
suspend fun getLastVisitsByLogin(pageNum: Int,
pageSize: Int,
login: String): Result<List<VisitDto>> {
if (!UserServiceST.getInstance().hasTokens()) {
throw RuntimeException("access token is null")
}
return visitRepository.getLastVisitsByLogin(pageNum, pageSize, UserServiceST.getInstance().getTokenPair().accessToken, login).map { pagingDto -> pagingDto.content }
}
suspend fun getMyLastVisits(pageNum: Int,
pageSize: Int): Result<List<VisitDto>> {
if (!UserServiceST.getInstance().hasTokens()) {
throw RuntimeException("access token is null")
}
return visitRepository.getMyLastVisits(
pageNum = pageNum,
pageSize = pageSize,
token = UserServiceST.getInstance().getTokenPair().accessToken
).map { pagingDto -> pagingDto.content }
}
} }

View File

@ -0,0 +1,57 @@
package com.displaynone.acss.components.acs.models.visit.repository
import android.util.Log
import com.displaynone.acss.components.acs.models.visit.repository.dto.LastVisitsDto
import com.displaynone.acss.config.Constants.serverUrl
import com.displaynone.acss.config.Network
import io.ktor.client.call.body
import io.ktor.client.request.get
import io.ktor.client.request.headers
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpStatusCode
import io.ktor.http.encodeURLPath
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class VisitRepository {
suspend fun getMyLastVisits(pageNum: Int,
pageSize: Int,
token: String): Result<LastVisitsDto> = withContext(Dispatchers.IO){
runCatching {
val result = Network.client.get("$serverUrl/api/acs/visits/me?page=$pageNum&size=$pageSize") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
}
Log.d("VisitRepository", result.bodyAsText())
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
result.body()
}
}
suspend fun getLastVisitsByLogin(pageNum: Int,
pageSize: Int,
token: String, login: String): Result<LastVisitsDto> = withContext(
Dispatchers.IO){
runCatching {
Log.d("VisitRepository", login)
val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/acs/visits/login/${encodedLogin}?page=$pageNum&size=$pageSize") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
setBody("""{ "login": "$encodedLogin" }""")
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("VisitRepository", result.bodyAsText())
result.body()
}
}
}

View File

@ -1,4 +1,4 @@
package com.displaynone.acss.components.auth.models.user.repository.dto package com.displaynone.acss.components.acs.models.visit.repository.dto
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package com.displaynone.acss.components.auth.models.user.repository.dto package com.displaynone.acss.components.acs.models.visit.repository.dto
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable

View File

@ -1,14 +1,12 @@
package com.displaynone.acss.components.auth.models.user package com.displaynone.acss.components.auth.models.user
import android.content.Context import android.content.Context
import android.util.Log
import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager import com.displaynone.acss.components.auth.internal_utils.AuthTokenManager
import com.displaynone.acss.components.auth.internal_utils.UserManager import com.displaynone.acss.components.auth.internal_utils.UserManager
import com.displaynone.acss.components.auth.models.AuthTokenPair
import com.displaynone.acss.components.auth.models.user.repository.UserRepository import com.displaynone.acss.components.auth.models.user.repository.UserRepository
import com.displaynone.acss.components.auth.models.user.repository.dto.LastVisitsDto
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.components.auth.models.user.repository.dto.VisitDto import com.displaynone.acss.components.acs.models.visit.repository.dto.VisitDto
import java.util.Optional
class UserServiceST( class UserServiceST(
@ -43,29 +41,32 @@ class UserServiceST(
} }
} }
} }
fun getTokenPair(): AuthTokenPair {
return tokenManager.authTokenPair!!
}
fun hasTokens(): Boolean { fun hasTokens(): Boolean {
return tokenManager.hasTokens() return tokenManager.hasTokens()
} }
suspend fun getMyLastVisits(pageNum: Int, // suspend fun getMyLastVisits(pageNum: Int,
pageSize: Int): Result<List<VisitDto>> { // pageSize: Int): Result<List<VisitDto>> {
if (!tokenManager.hasTokens()) { // if (!tokenManager.hasTokens()) {
throw RuntimeException("access token is null") // throw RuntimeException("access token is null")
} // }
//
return userRepository.getMyLastVisits( // return userRepository.getMyLastVisits(
pageNum = pageNum, // pageNum = pageNum,
pageSize = pageSize, // pageSize = pageSize,
token = tokenManager.authTokenPair!!.accessToken // token = tokenManager.authTokenPair!!.accessToken
).map { pagingDto -> pagingDto.content } // ).map { pagingDto -> pagingDto.content }
} // }
suspend fun getLastVisitsByLogin(pageNum: Int, // suspend fun getLastVisitsByLogin(pageNum: Int,
pageSize: Int, // pageSize: Int,
login: String): Result<List<VisitDto>> { // login: String): Result<List<VisitDto>> {
if (!tokenManager.hasTokens()) { // if (!tokenManager.hasTokens()) {
throw RuntimeException("access token is null") // throw RuntimeException("access token is null")
} // }
return userRepository.getLastVisitsByLogin(pageNum, pageSize, tokenManager.authTokenPair!!.accessToken, login).map { pagingDto -> pagingDto.content } // return userRepository.getLastVisitsByLogin(pageNum, pageSize, tokenManager.authTokenPair!!.accessToken, login).map { pagingDto -> pagingDto.content }
} // }
fun logout(){ fun logout(){
tokenManager.clear() tokenManager.clear()
} }

View File

@ -2,7 +2,6 @@ package com.displaynone.acss.components.auth.models.user.repository
import android.util.Log import android.util.Log
import com.displaynone.acss.components.auth.models.AuthTokenPair import com.displaynone.acss.components.auth.models.AuthTokenPair
import com.displaynone.acss.components.auth.models.user.repository.dto.LastVisitsDto
import com.displaynone.acss.config.Constants.serverUrl import com.displaynone.acss.config.Constants.serverUrl
import com.displaynone.acss.config.Network import com.displaynone.acss.config.Network
import com.displaynone.acss.components.auth.models.user.repository.dto.RegisterUserDto import com.displaynone.acss.components.auth.models.user.repository.dto.RegisterUserDto
@ -11,7 +10,6 @@ import com.displaynone.acss.components.auth.models.user.repository.dto.UserLogin
import io.ktor.client.call.body import io.ktor.client.call.body
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.client.request.headers import io.ktor.client.request.headers
import io.ktor.client.request.patch
import io.ktor.client.request.post import io.ktor.client.request.post
import io.ktor.client.request.setBody import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText import io.ktor.client.statement.bodyAsText
@ -24,7 +22,6 @@ import io.ktor.http.encodeURLPath
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlin.math.log
class UserRepository( class UserRepository(
@ -104,72 +101,6 @@ class UserRepository(
result.body() result.body()
} }
} }
suspend fun getMyLastVisits(pageNum: Int,
pageSize: Int,
token: String): Result<LastVisitsDto> = withContext(Dispatchers.IO){
runCatching {
val result = Network.client.get("$serverUrl/api/acs/visits/me?page=$pageNum&size=$pageSize") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
}
Log.d("UserRepository", result.bodyAsText())
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
result.body()
}
}
suspend fun getLastVisitsByLogin(pageNum: Int,
pageSize: Int,
token: String, login: String): Result<LastVisitsDto> = withContext(Dispatchers.IO){
runCatching {
Log.d("UserRepository", login)
val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/acs/visits/login/${encodedLogin}?page=$pageNum&size=$pageSize") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
setBody("""{ "login": "$encodedLogin" }""")
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
result.body()
}
}
suspend fun getAllLastVisitsAsAdmin(token: String, login: String): Result<UserDTO> = withContext(Dispatchers.IO){
runCatching {
val encodedLogin = login.encodeURLPath()
val result = Network.client.get("$serverUrl/api/users/login/$encodedLogin") {
headers {
append(HttpHeaders.Authorization, "Bearer $token")
}
setBody("""{ "code": "$encodedLogin" }""")
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Log.d("UserRepository", result.bodyAsText())
result.body()
}
}
suspend fun openDoor(token: String, code: Long): Result<Unit> = withContext(Dispatchers.IO) {
runCatching {
val result = Network.client.patch("$serverUrl/api/open") {
headers {
append(HttpHeaders.Authorization, token)
}
setBody("""{"value":$code}""")
}
if (result.status != HttpStatusCode.OK) {
error("Status ${result.status}: ${result.body<String>()}")
}
Unit
}
}
suspend fun register(login: String, password: String): Result<Unit> = suspend fun register(login: String, password: String): Result<Unit> =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {

View File

@ -12,7 +12,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.displaynone.acss.R import com.displaynone.acss.R
import com.displaynone.acss.components.auth.models.user.UserServiceST import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.auth.models.user.repository.VisitAdapter import com.displaynone.acss.components.acs.models.visit.VisitAdapter
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import com.displaynone.acss.databinding.FragmentProfileBinding import com.displaynone.acss.databinding.FragmentProfileBinding
import com.displaynone.acss.ui.profile.ProfileViewModel.Action import com.displaynone.acss.ui.profile.ProfileViewModel.Action

View File

@ -6,9 +6,9 @@ import androidx.lifecycle.viewModelScope
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.cachedIn import androidx.paging.cachedIn
import androidx.paging.log
import com.displaynone.acss.components.auth.models.user.UserServiceST import com.displaynone.acss.components.auth.models.user.UserServiceST
import com.displaynone.acss.components.auth.models.user.repository.VisitListPagingSource import com.displaynone.acss.components.acs.models.visit.VisitListPagingSource
import com.displaynone.acss.components.acs.models.visit.VisitServiceST
import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO import com.displaynone.acss.components.auth.models.user.repository.dto.UserDTO
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
@ -36,7 +36,7 @@ class ProfileViewModel(): ViewModel() {
maxSize = 100 maxSize = 100
) )
) { ) {
VisitListPagingSource(UserServiceST.getInstance()::getMyLastVisits) VisitListPagingSource(VisitServiceST.getInstance()::getMyLastVisits)
}.flow }.flow
.cachedIn(viewModelScope) .cachedIn(viewModelScope)
@ -44,7 +44,7 @@ class ProfileViewModel(): ViewModel() {
config = PagingConfig(pageSize = 20, enablePlaceholders = false, maxSize = 100) config = PagingConfig(pageSize = 20, enablePlaceholders = false, maxSize = 100)
) { ) {
VisitListPagingSource { pageNum, pageSize -> VisitListPagingSource { pageNum, pageSize ->
UserServiceST.getInstance().getLastVisitsByLogin(pageNum,pageSize, login) VisitServiceST.getInstance().getLastVisitsByLogin(pageNum,pageSize, login)
} }
}.flow.cachedIn(viewModelScope) }.flow.cachedIn(viewModelScope)

View File

@ -6,6 +6,7 @@ import android.app.Application
import android.net.http.HttpException import android.net.http.HttpException
import android.util.Log import android.util.Log
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.displaynone.acss.components.acs.models.gate.GateServiceST
import com.displaynone.acss.components.auth.models.user.UserServiceST import com.displaynone.acss.components.auth.models.user.UserServiceST
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.io.IOException import kotlinx.io.IOException
@ -18,7 +19,7 @@ class QrResultViewModel(application: Application) : AndroidViewModel(application
viewModelScope.launch { viewModelScope.launch {
try { try {
val stringCode = code.toString() val stringCode = code.toString()
UserServiceST.getInstance().openDoor(stringCode).fold( GateServiceST.getInstance().openDoor(stringCode).fold(
onSuccess = { response -> onSuccess = { response ->
Log.d("QrResultViewModel", "Door opened") Log.d("QrResultViewModel", "Door opened")
_state.emit(State.Result( _state.emit(State.Result(

View File

@ -209,16 +209,6 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigation"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:menu="@menu/bottom_nav_menu"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>