fix: data и domain слои получения списка посещений + начало создания пагинации
Some checks failed
Merge core/template-android-project to this repo / merge-if-needed (push) Has been cancelled
Some checks failed
Merge core/template-android-project to this repo / merge-if-needed (push) Has been cancelled
This commit is contained in:
parent
c3c88798a9
commit
5a490f37f4
@ -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,7 @@ 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")
|
||||||
|
|
||||||
fun getInstance(context: Context): UserDataStoreManager {
|
fun getInstance(context: Context): UserDataStoreManager {
|
||||||
return UserDataStoreManager(context.applicationContext)
|
return UserDataStoreManager(context.applicationContext)
|
||||||
@ -21,12 +22,19 @@ 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] ?: ""
|
||||||
}
|
}
|
||||||
|
val roleFlow: Flow<String> = context.applicationContext.dataStore.data.map{ prefs ->
|
||||||
|
prefs[ROLE_KEY] ?: ""
|
||||||
|
}
|
||||||
suspend fun saveUsername(username: String) {
|
suspend fun saveUsername(username: String) {
|
||||||
context.dataStore.edit { prefs ->
|
context.dataStore.edit { prefs ->
|
||||||
prefs[USERNAME_KEY] = username
|
prefs[USERNAME_KEY] = username
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
suspend fun saveRole(role: String){
|
||||||
|
context.dataStore.edit { prefs ->
|
||||||
|
prefs[ROLE_KEY] = role
|
||||||
|
}
|
||||||
|
}
|
||||||
suspend fun clearUsername() {
|
suspend fun clearUsername() {
|
||||||
context.applicationContext.dataStore.edit { it.clear() }
|
context.applicationContext.dataStore.edit { it.clear() }
|
||||||
}
|
}
|
||||||
|
14
app/src/main/java/ru/myitschool/work/data/dto/VisitDTO.kt
Normal file
14
app/src/main/java/ru/myitschool/work/data/dto/VisitDTO.kt
Normal file
@ -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 VisitDTO(
|
||||||
|
@SerialName("scanTime")
|
||||||
|
@Serializable(with = DateSerializer::class) val scanTime : Date?,
|
||||||
|
@SerialName("readerId") val readerId: Long?,
|
||||||
|
@SerialName("type") val type: String?
|
||||||
|
)
|
@ -0,0 +1,9 @@
|
|||||||
|
package ru.myitschool.work.data.dto
|
||||||
|
|
||||||
|
import kotlinx.serialization.SerialName
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class VisitListPagingDTO(
|
||||||
|
@SerialName("content") val content : List<VisitDTO>?
|
||||||
|
)
|
@ -0,0 +1,32 @@
|
|||||||
|
package ru.myitschool.work.data.visitsList
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import io.ktor.client.call.body
|
||||||
|
import io.ktor.client.request.get
|
||||||
|
import io.ktor.client.statement.bodyAsText
|
||||||
|
import io.ktor.http.HttpStatusCode
|
||||||
|
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.VisitListPagingDTO
|
||||||
|
import ru.myitschool.work.utils.NetworkModule
|
||||||
|
|
||||||
|
class VisitListNetworkDataSource(
|
||||||
|
context: Context
|
||||||
|
){
|
||||||
|
private val client = NetworkModule.httpClient
|
||||||
|
private val userDataStoreManager = UserDataStoreManager.getInstance(context)
|
||||||
|
suspend fun getList(pageNum: Int, pageSize: Int):Result<VisitListPagingDTO> = withContext(Dispatchers.IO){
|
||||||
|
runCatching {
|
||||||
|
val username = userDataStoreManager.usernameFlow.first()
|
||||||
|
val result = client.get("${Constants.SERVER_ADDRESS}/api/$username/visits?page=$pageNum&size=$pageSize")
|
||||||
|
|
||||||
|
if (result.status != HttpStatusCode.OK) {
|
||||||
|
error("Status ${result.status}")
|
||||||
|
}
|
||||||
|
result.body()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package ru.myitschool.work.data.visitsList
|
||||||
|
|
||||||
|
import ru.myitschool.work.domain.entities.UserEntity
|
||||||
|
import ru.myitschool.work.domain.entities.VisitEntity
|
||||||
|
import ru.myitschool.work.domain.visitsList.VisitListRepo
|
||||||
|
|
||||||
|
class VisitListRepoImpl(
|
||||||
|
private val networkDataSource: VisitListNetworkDataSource
|
||||||
|
) : VisitListRepo {
|
||||||
|
override suspend fun getList(pageNum: Int, pageSize: Int): Result<List<VisitEntity>> {
|
||||||
|
return networkDataSource.getList(pageNum, pageSize).map { pagingDTO ->
|
||||||
|
pagingDTO.content?.mapNotNull { dto->
|
||||||
|
VisitEntity(
|
||||||
|
scanTime = dto.scanTime ?: return@mapNotNull null,
|
||||||
|
readerId = dto.readerId ?: return@mapNotNull null,
|
||||||
|
type = dto.type ?: return@mapNotNull null
|
||||||
|
)
|
||||||
|
}?: return Result.failure(IllegalStateException("List parse error"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package ru.myitschool.work.domain.entities
|
||||||
|
|
||||||
|
import java.util.Date
|
||||||
|
|
||||||
|
data class VisitEntity(
|
||||||
|
val scanTime : Date,
|
||||||
|
val readerId: Long,
|
||||||
|
val type: String
|
||||||
|
)
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.myitschool.work.domain.visitsList
|
||||||
|
|
||||||
|
class GetVisitListUseCase(
|
||||||
|
private val repo: VisitListRepo
|
||||||
|
) {
|
||||||
|
suspend operator fun invoke(pageNum : Int, pageSize: Int) = repo.getList(pageNum, pageSize)
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package ru.myitschool.work.domain.visitsList
|
||||||
|
|
||||||
|
import ru.myitschool.work.domain.entities.UserEntity
|
||||||
|
import ru.myitschool.work.domain.entities.VisitEntity
|
||||||
|
|
||||||
|
interface VisitListRepo {
|
||||||
|
suspend fun getList(pageNum : Int, pageSize: Int) : Result<List<VisitEntity>>
|
||||||
|
}
|
@ -42,12 +42,27 @@ class LoginFragment : Fragment(R.layout.fragment_login) {
|
|||||||
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
package ru.myitschool.work.ui.main
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.paging.PagingState
|
||||||
|
import ru.myitschool.work.domain.entities.VisitEntity
|
||||||
|
|
||||||
|
class VisitListPagingSource(
|
||||||
|
private val request: suspend(pageNum: Int, pageSize: Int) ->Result<List<VisitEntity>>
|
||||||
|
) : PagingSource<Int, VisitEntity>() {
|
||||||
|
override fun getRefreshKey(state: PagingState<Int, VisitEntity>): 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, VisitEntity> {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
@ -23,9 +23,9 @@ 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(openEntity: OpenEntity){
|
||||||
_state.value = State.Loading
|
_state.value = State.Loading
|
||||||
|
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", 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))
|
||||||
|
}
|
||||||
|
}
|
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