Lab 05: add performances rest

This commit is contained in:
abazov73 2023-12-06 02:29:08 +04:00
parent 2c5adc70ed
commit ca9a734bf3
10 changed files with 224 additions and 18 deletions

View File

@ -28,7 +28,7 @@ interface MyServerService {
suspend fun getPerformances( suspend fun getPerformances(
@Query("_page") page: Int, @Query("_page") page: Int,
@Query("_limit") limit: Int, @Query("_limit") limit: Int,
): List<PersonRemote> ): List<PerformanceRemote>
@GET("events") @GET("events")
suspend fun getEvents( suspend fun getEvents(
@ -41,6 +41,11 @@ interface MyServerService {
@Path("id") id: Int, @Path("id") id: Int,
): PerformanceRemote ): PerformanceRemote
@GET("persons/{id}")
suspend fun getPerson(
@Path("id") id: Int,
): PersonRemote
companion object { companion object {
private const val BASE_URL = "http://10.0.2.2:26000/" private const val BASE_URL = "http://10.0.2.2:26000/"

View File

@ -1,6 +1,8 @@
package com.example.mobile_labs.api.models package com.example.mobile_labs.api.models
import com.example.mobile_labs.api.MyServerService
import com.example.mobile_labs.database.performance.model.Performance import com.example.mobile_labs.database.performance.model.Performance
import com.example.mobile_labs.database.performance.model.PerformanceWithPeople
import com.example.mobile_labs.database.person.model.Person import com.example.mobile_labs.database.person.model.Person
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -12,7 +14,8 @@ data class PerformanceRemote(
val authorId: Int = 0, val authorId: Int = 0,
val directorId: Int = 0, val directorId: Int = 0,
val imageURL: String = "", val imageURL: String = "",
val previewImageURL: String = "" val previewImageURL: String = "",
val actors: List<Int> = listOf()
) )
fun PerformanceRemote.toPerformance(): Performance = Performance( fun PerformanceRemote.toPerformance(): Performance = Performance(
@ -23,4 +26,18 @@ fun PerformanceRemote.toPerformance(): Performance = Performance(
directorId, directorId,
imageURL, imageURL,
previewImageURL previewImageURL
) )
suspend fun PerformanceRemote.toPerformanceWithPeople(service: MyServerService): PerformanceWithPeople {
val actorsList = ArrayList<Person>();
actors.forEach {id ->
actorsList.add(service.getPerson(id).toPerson())
}
return PerformanceWithPeople(
service.getPerformance(id).toPerformance(),
service.getPerson(authorId).toPerson(),
service.getPerson(directorId).toPerson(),
actorsList.toList()
)
}

View File

@ -0,0 +1,112 @@
package com.example.mobile_labs.api.performance
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import com.example.mobile_labs.api.MyServerService
import com.example.mobile_labs.api.models.toPerformance
import com.example.mobile_labs.api.models.toPerson
import com.example.mobile_labs.api.people.RestPersonRepository
import com.example.mobile_labs.database.AppDatabase
import com.example.mobile_labs.database.performance.model.Performance
import com.example.mobile_labs.database.performance.repository.OfflinePerformanceRepository
import com.example.mobile_labs.database.person.repository.OfflinePersonRepository
import com.example.mobile_labs.database.remotekeys.model.RemoteKeyType
import com.example.mobile_labs.database.remotekeys.model.RemoteKeys
import com.example.mobile_labs.database.remotekeys.repository.OfflineRemoteKeyRepository
import retrofit2.HttpException
import java.io.IOException
@OptIn(ExperimentalPagingApi::class)
class PerformanceRemoteMediator(
private val service: MyServerService,
private val dbPerformanceRepository: OfflinePerformanceRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val personRestRepository: RestPersonRepository,
private val database: AppDatabase
) : RemoteMediator<Int, Performance>() {
override suspend fun initialize(): InitializeAction {
return InitializeAction.LAUNCH_INITIAL_REFRESH
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, Performance>
): MediatorResult {
val page = when (loadType) {
LoadType.REFRESH -> {
val remoteKeys = getRemoteKeyClosestToCurrentPosition(state)
remoteKeys?.nextKey?.minus(1) ?: 1
}
LoadType.PREPEND -> {
val remoteKeys = getRemoteKeyForFirstItem(state)
remoteKeys?.prevKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
LoadType.APPEND -> {
val remoteKeys = getRemoteKeyForLastItem(state)
remoteKeys?.nextKey
?: return MediatorResult.Success(endOfPaginationReached = remoteKeys != null)
}
}
try {
val performances = service.getPerformances(page, state.config.pageSize).map { it.toPerformance() }
val endOfPaginationReached = performances.isEmpty()
database.withTransaction {
if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.PERSON)
dbPerformanceRepository.clearPerformances()
}
val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1
val keys = performances.map {
RemoteKeys(
entityId = it.performance_uid !!,
type = RemoteKeyType.PERSON,
prevKey = prevKey,
nextKey = nextKey
)
}
personRestRepository.getAllPeople()
dbRemoteKeyRepository.createRemoteKeys(keys)
dbPerformanceRepository.insertPerformances(performances)
}
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (exception: IOException) {
return MediatorResult.Error(exception)
} catch (exception: HttpException) {
return MediatorResult.Error(exception)
}
}
private suspend fun getRemoteKeyForLastItem(state: PagingState<Int, Performance>): RemoteKeys? {
return state.pages.lastOrNull { it.data.isNotEmpty() }?.data?.lastOrNull()
?.let { performance ->
dbRemoteKeyRepository.getAllRemoteKeys(performance.performance_uid !!, RemoteKeyType.PERSON)
}
}
private suspend fun getRemoteKeyForFirstItem(state: PagingState<Int, Performance>): RemoteKeys? {
return state.pages.firstOrNull { it.data.isNotEmpty() }?.data?.firstOrNull()
?.let { performance ->
dbRemoteKeyRepository.getAllRemoteKeys(performance.performance_uid !!, RemoteKeyType.PERSON)
}
}
private suspend fun getRemoteKeyClosestToCurrentPosition(
state: PagingState<Int, Performance>
): RemoteKeys? {
return state.anchorPosition?.let { position ->
state.closestItemToPosition(position)?.performance_uid?.let { performanceUid ->
dbRemoteKeyRepository.getAllRemoteKeys(performanceUid, RemoteKeyType.PERSON)
}
}
}
}

View File

@ -0,0 +1,53 @@
package com.example.mobile_labs.api.performance
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.mobile_labs.api.MyServerService
import com.example.mobile_labs.api.models.toPerformance
import com.example.mobile_labs.api.models.toPerformanceWithPeople
import com.example.mobile_labs.api.people.RestPersonRepository
import com.example.mobile_labs.common.AppDataContainer
import com.example.mobile_labs.common.PerformanceRepository
import com.example.mobile_labs.common.PersonRepository
import com.example.mobile_labs.database.AppDatabase
import com.example.mobile_labs.database.performance.model.Performance
import com.example.mobile_labs.database.performance.model.PerformanceWithPeople
import com.example.mobile_labs.database.performance.repository.OfflinePerformanceRepository
import com.example.mobile_labs.database.person.model.Person
import com.example.mobile_labs.database.person.repository.OfflinePersonRepository
import com.example.mobile_labs.database.remotekeys.repository.OfflineRemoteKeyRepository
import kotlinx.coroutines.flow.Flow
class RestPerformanceRepository(
private val service: MyServerService,
private val dbPerformanceRepository: OfflinePerformanceRepository,
private val dbRemoteKeyRepository: OfflineRemoteKeyRepository,
private val personRestRepository: RestPersonRepository,
private val database: AppDatabase
) : PerformanceRepository {
override fun getAllPerformances(): Flow<PagingData<Performance>> {
val pagingSourceFactory = { dbPerformanceRepository.getAllPerformancesPagingSource() }
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = AppDataContainer.LIMIT,
enablePlaceholders = false
),
remoteMediator = PerformanceRemoteMediator(
service,
dbPerformanceRepository,
dbRemoteKeyRepository,
personRestRepository,
database,
),
pagingSourceFactory = pagingSourceFactory
).flow
}
override suspend fun getPerformance(uid: Int): PerformanceWithPeople =
service.getPerformance(uid).toPerformanceWithPeople(service)
}

View File

@ -3,6 +3,7 @@ package com.example.mobile_labs.common
import android.content.Context import android.content.Context
import com.example.mobile_labs.api.MyServerService import com.example.mobile_labs.api.MyServerService
import com.example.mobile_labs.api.people.RestPersonRepository import com.example.mobile_labs.api.people.RestPersonRepository
import com.example.mobile_labs.api.performance.RestPerformanceRepository
import com.example.mobile_labs.database.event.repository.EventRepository import com.example.mobile_labs.database.event.repository.EventRepository
import com.example.mobile_labs.database.event.repository.OfflineEventRepository import com.example.mobile_labs.database.event.repository.OfflineEventRepository
import com.example.mobile_labs.database.performance.repository.OfflinePerformanceRepository import com.example.mobile_labs.database.performance.repository.OfflinePerformanceRepository
@ -14,7 +15,7 @@ import com.example.mobile_labs.database.remotekeys.repository.RemoteKeyRepositor
interface AppContainer { interface AppContainer {
val personRestRepository: RestPersonRepository val personRestRepository: RestPersonRepository
val eventRepository: EventRepository val eventRepository: EventRepository
val performanceRepository: PerformanceRepository val performanceRestRepository: RestPerformanceRepository
} }
class AppDataContainer(private val context: Context) : AppContainer { class AppDataContainer(private val context: Context) : AppContainer {
@ -22,7 +23,7 @@ class AppDataContainer(private val context: Context) : AppContainer {
OfflineEventRepository(AppDatabase.getInstance(context).eventDao()) OfflineEventRepository(AppDatabase.getInstance(context).eventDao())
} }
override val performanceRepository: PerformanceRepository by lazy { val performanceRepository: OfflinePerformanceRepository by lazy {
OfflinePerformanceRepository(AppDatabase.getInstance(context).performanceDao()) OfflinePerformanceRepository(AppDatabase.getInstance(context).performanceDao())
} }
@ -42,6 +43,16 @@ class AppDataContainer(private val context: Context) : AppContainer {
) )
} }
override val performanceRestRepository: RestPerformanceRepository by lazy {
RestPerformanceRepository(
MyServerService.getInstance(),
performanceRepository,
remoteKeyRepository,
personRestRepository,
AppDatabase.getInstance(context)
)
}
companion object { companion object {
const val TIMEOUT = 5000L const val TIMEOUT = 5000L
const val LIMIT = 3 const val LIMIT = 3

View File

@ -20,10 +20,10 @@ object AppViewModelProvider {
EventListViewModel(theatreApplication().container.eventRepository) EventListViewModel(theatreApplication().container.eventRepository)
} }
initializer { initializer {
PerformanceListViewModel(theatreApplication().container.performanceRepository) PerformanceListViewModel(theatreApplication().container.performanceRestRepository)
} }
initializer { initializer {
PerformanceViewModel(this.createSavedStateHandle(), theatreApplication().container.performanceRepository) PerformanceViewModel(this.createSavedStateHandle(), theatreApplication().container.performanceRestRepository)
} }
} }
} }

View File

@ -7,8 +7,5 @@ import kotlinx.coroutines.flow.Flow
interface PerformanceRepository { interface PerformanceRepository {
fun getAllPerformances(): Flow<PagingData<Performance>> fun getAllPerformances(): Flow<PagingData<Performance>>
fun getPerformance(uid: Int): Flow<PerformanceWithPeople?> suspend fun getPerformance(uid: Int): PerformanceWithPeople
suspend fun insertPerformance(performance: Performance)
suspend fun updatePerformance(performance: Performance)
suspend fun deletePerformance(performance: Performance)
} }

View File

@ -20,11 +20,14 @@ interface PerformanceDao {
fun getByUid(uid: Int): Flow<PerformanceWithPeople> fun getByUid(uid: Int): Flow<PerformanceWithPeople>
@Insert @Insert
suspend fun insert(performance: Performance) suspend fun insert(vararg performance: Performance)
@Update @Update
suspend fun update(performance: Performance) suspend fun update(performance: Performance)
@Delete @Delete
suspend fun delete(performance: Performance) suspend fun delete(performance: Performance)
@Query("DELETE FROM performances")
suspend fun deleteAll()
} }

View File

@ -3,12 +3,15 @@ package com.example.mobile_labs.database.performance.repository
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.PagingData import androidx.paging.PagingData
import androidx.paging.PagingSource
import com.example.mobile_labs.common.PerformanceRepository import com.example.mobile_labs.common.PerformanceRepository
import com.example.mobile_labs.common.AppDataContainer import com.example.mobile_labs.common.AppDataContainer
import com.example.mobile_labs.database.performance.dao.PerformanceDao import com.example.mobile_labs.database.performance.dao.PerformanceDao
import com.example.mobile_labs.database.performance.model.Performance import com.example.mobile_labs.database.performance.model.Performance
import com.example.mobile_labs.database.performance.model.PerformanceWithPeople import com.example.mobile_labs.database.performance.model.PerformanceWithPeople
import com.example.mobile_labs.database.person.model.Person
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
class OfflinePerformanceRepository(private val performanceDao: PerformanceDao) : class OfflinePerformanceRepository(private val performanceDao: PerformanceDao) :
PerformanceRepository { PerformanceRepository {
@ -19,11 +22,18 @@ class OfflinePerformanceRepository(private val performanceDao: PerformanceDao) :
), ),
pagingSourceFactory = performanceDao::getAll pagingSourceFactory = performanceDao::getAll
).flow ).flow
override fun getPerformance(uid: Int): Flow<PerformanceWithPeople?> = performanceDao.getByUid(uid); override suspend fun getPerformance(uid: Int): PerformanceWithPeople = performanceDao.getByUid(uid).first();
override suspend fun insertPerformance(performance: Performance) = performanceDao.insert(performance); suspend fun insertPerformance(performance: Performance) = performanceDao.insert(performance);
override suspend fun updatePerformance(performance: Performance) = performanceDao.update(performance); suspend fun updatePerformance(performance: Performance) = performanceDao.update(performance);
override suspend fun deletePerformance(performance: Performance) = performanceDao.delete(performance); suspend fun deletePerformance(performance: Performance) = performanceDao.delete(performance);
suspend fun clearPerformances() = performanceDao.deleteAll()
suspend fun insertPerformances(performances: List<Performance>) =
performanceDao.insert(*performances.toTypedArray())
fun getAllPerformancesPagingSource(): PagingSource<Int, Performance> = performanceDao.getAll()
} }

View File

@ -26,8 +26,6 @@ class PerformanceViewModel(
viewModelScope.launch { viewModelScope.launch {
if (studentUid > 0) { if (studentUid > 0) {
performanceUiState = performanceRepository.getPerformance(studentUid) performanceUiState = performanceRepository.getPerformance(studentUid)
.filterNotNull()
.first()
.toUiState() .toUiState()
} }
} }