Lab 04: add pagination

This commit is contained in:
abazov73 2023-12-05 22:26:05 +04:00
parent 7707836c12
commit c40d14e9b4
17 changed files with 140 additions and 69 deletions

View File

@ -76,6 +76,8 @@ dependencies {
implementation("androidx.room:room-ktx:$room_version")
implementation("androidx.room:room-paging:$room_version")
implementation("androidx.paging:paging-compose:3.2.1")
//Tests
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")

View File

@ -29,5 +29,6 @@ class AppDataContainer(private val context: Context) : AppContainer {
companion object {
const val TIMEOUT = 5000L
const val LIMIT = 3
}
}

View File

@ -1,5 +1,6 @@
package com.example.mobile_labs.database.event.dao
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
@ -9,14 +10,17 @@ import com.example.mobile_labs.database.event.model.Event
import com.example.mobile_labs.database.event.model.EventWithPerformance
import com.example.mobile_labs.database.performance.model.Performance
import kotlinx.coroutines.flow.Flow
import java.time.LocalDate
import java.time.format.DateTimeFormatter
@Dao
interface EventDao {
@Query("select * from events order by date asc")
fun getAll(): Flow<List<Event>>
@Query("select * from events where events.date > :dateFrom order by date asc")
fun getAllWithPerformance(dateFrom: String): Flow<List<EventWithPerformance>>
@Query("select * from events where events.date > :dateFrom order by date collate nocase asc")
fun getAllWithPerformance(dateFrom: String? = LocalDate.now().format(
DateTimeFormatter.ofPattern("dd.MM"))): PagingSource<Int, EventWithPerformance>
@Query("select * from events where events.uid = :uid")
fun getByUid(uid: Int): Flow<EventWithPerformance>

View File

@ -1,12 +1,13 @@
package com.example.mobile_labs.database.event.repository
import androidx.paging.PagingData
import com.example.mobile_labs.database.event.model.Event
import com.example.mobile_labs.database.event.model.EventWithPerformance
import kotlinx.coroutines.flow.Flow
interface EventRepository {
fun getAll(): Flow<List<Event>>
fun getAllWithPerformance(): Flow<List<EventWithPerformance>>
fun getAllWithPerformance(): Flow<PagingData<EventWithPerformance>>
fun getEvent(uid: Int): Flow<EventWithPerformance?>
suspend fun insertEvent(event: Event)
suspend fun updateEvent(event: Event)

View File

@ -1,5 +1,9 @@
package com.example.mobile_labs.database.event.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.mobile_labs.database.AppDataContainer
import com.example.mobile_labs.database.event.dao.EventDao
import com.example.mobile_labs.database.event.model.Event
import com.example.mobile_labs.database.event.model.EventWithPerformance
@ -9,9 +13,14 @@ import java.time.format.DateTimeFormatter
class OfflineEventRepository(private val eventDao: EventDao) : EventRepository {
override fun getAll(): Flow<List<Event>> = eventDao.getAll();
override fun getAllWithPerformance(): Flow<List<EventWithPerformance>> = eventDao.getAllWithPerformance(LocalDate.now().format(
DateTimeFormatter.ofPattern("dd.MM")
))
override fun getAllWithPerformance(): Flow<PagingData<EventWithPerformance>> = Pager(
config = PagingConfig(
pageSize = AppDataContainer.LIMIT,
enablePlaceholders = false,
),
pagingSourceFactory = eventDao::getAllWithPerformance
).flow
override fun getEvent(uid: Int): Flow<EventWithPerformance?> = eventDao.getByUid(uid);
override suspend fun insertEvent(event: Event) = eventDao.insert(event);

View File

@ -1,5 +1,6 @@
package com.example.mobile_labs.database.performance.dao
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
@ -13,7 +14,7 @@ import kotlinx.coroutines.flow.Flow
@Dao
interface PerformanceDao {
@Query("select * from performances order by title collate nocase asc")
fun getAll(): Flow<List<Performance>>
fun getAll(): PagingSource<Int, Performance>
@Query("select * from performances where performances.performance_uid = :uid")
fun getByUid(uid: Int): Flow<PerformanceWithPeople>

View File

@ -1,12 +1,22 @@
package com.example.mobile_labs.database.performance.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.mobile_labs.database.AppDataContainer
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.PerformanceWithPeople
import kotlinx.coroutines.flow.Flow
class OfflinePerformanceRepository(private val performanceDao: PerformanceDao) : PerformanceRepository {
override fun getAllPerformances(): Flow<List<Performance>> = performanceDao.getAll();
override fun getAllPerformances(): Flow<PagingData<Performance>> = Pager(
config = PagingConfig(
pageSize = AppDataContainer.LIMIT,
enablePlaceholders = false,
),
pagingSourceFactory = performanceDao::getAll
).flow
override fun getPerformance(uid: Int): Flow<PerformanceWithPeople?> = performanceDao.getByUid(uid);
override suspend fun insertPerformance(performance: Performance) = performanceDao.insert(performance);

View File

@ -1,11 +1,12 @@
package com.example.mobile_labs.database.performance.repository
import androidx.paging.PagingData
import com.example.mobile_labs.database.performance.model.Performance
import com.example.mobile_labs.database.performance.model.PerformanceWithPeople
import kotlinx.coroutines.flow.Flow
interface PerformanceRepository {
fun getAllPerformances(): Flow<List<Performance>>
fun getAllPerformances(): Flow<PagingData<Performance>>
fun getPerformance(uid: Int): Flow<PerformanceWithPeople?>
suspend fun insertPerformance(performance: Performance)
suspend fun updatePerformance(performance: Performance)

View File

@ -1,5 +1,6 @@
package com.example.mobile_labs.database.person.dao
import androidx.paging.PagingSource
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
@ -11,7 +12,7 @@ import kotlinx.coroutines.flow.Flow
@Dao
interface PersonDao {
@Query("select * from people order by last_name collate nocase asc")
fun getAll(): Flow<List<Person>>
fun getAll(): PagingSource<Int, Person>
@Query("select * from people where people.uid = :uid")
fun getByUid(uid: Int): Flow<Person>

View File

@ -1,11 +1,22 @@
package com.example.mobile_labs.database.person.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.mobile_labs.database.AppContainer
import com.example.mobile_labs.database.AppDataContainer
import com.example.mobile_labs.database.person.dao.PersonDao
import com.example.mobile_labs.database.person.model.Person
import kotlinx.coroutines.flow.Flow
class OfflinePersonRepository(private val personDao: PersonDao) : PersonRepository {
override fun getAllPeople(): Flow<List<Person>> = personDao.getAll();
override fun getAllPeople(): Flow<PagingData<Person>> = Pager(
config = PagingConfig(
pageSize = AppDataContainer.LIMIT,
enablePlaceholders = false,
),
pagingSourceFactory = personDao::getAll
).flow;
override fun getPerson(uid: Int): Flow<Person?> = personDao.getByUid(uid);
override suspend fun insertPerson(person: Person) = personDao.insert(person);

View File

@ -1,10 +1,11 @@
package com.example.mobile_labs.database.person.repository
import androidx.paging.PagingData
import com.example.mobile_labs.database.person.model.Person
import kotlinx.coroutines.flow.Flow
interface PersonRepository {
fun getAllPeople(): Flow<List<Person>>
fun getAllPeople(): Flow<PagingData<Person>>
fun getPerson(uid: Int): Flow<Person?>
suspend fun insertPerson(person: Person)
suspend fun updatePerson(person: Person)

View File

@ -35,17 +35,24 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey
import coil.compose.AsyncImage
import com.example.mobile_labs.R
import com.example.mobile_labs.database.AppDatabase
import com.example.mobile_labs.database.event.model.Event
import com.example.mobile_labs.database.event.model.EventWithPerformance
import com.example.mobile_labs.database.performance.model.Performance
import com.example.mobile_labs.database.person.model.Person
import com.example.mobile_labs.ui.AppViewModelProvider
import com.example.mobile_labs.ui.navigation.Screen
import com.example.mobile_labs.ui.person.list.PeopleListViewModel
import com.example.mobile_labs.ui.theme.Mobile_LabsTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext
import java.time.LocalDate
import java.time.format.DateTimeFormatter
@ -55,14 +62,14 @@ fun EventList(
navController: NavController,
viewModel: EventListViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val eventListUiState by viewModel.eventListUiState.collectAsState()
val eventListUiState = viewModel.eventListUiState.collectAsLazyPagingItems()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(all = 10.dp)) {
EventList(
eventList = eventListUiState.eventList,
eventList = eventListUiState,
onClick = {uid : Int ->
val route = Screen.PerformanceView.route.replace("{id}", uid.toString())
navController.navigate(route)
@ -75,13 +82,13 @@ fun EventList(
@Composable
private fun EventList(
modifier: Modifier = Modifier,
eventList: List<EventWithPerformance>,
eventList: LazyPagingItems<EventWithPerformance>,
onClick: (uid: Int) -> Unit
) {
Column(
modifier = modifier
) {
if (eventList.isEmpty()) {
if (eventList.itemCount == 0) {
Text(
text = stringResource(R.string.events_missing_description),
textAlign = TextAlign.Center,
@ -89,13 +96,20 @@ private fun EventList(
)
} else {
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(items = eventList, key = { it.event.uid !! }) { event ->
items(
count = eventList.itemCount,
key = eventList.itemKey(),
contentType = eventList.itemContentType(),
) { index ->
val event = eventList[index]
event?.let{
EventListItem(event = event, modifier = Modifier
.padding(vertical = 7.dp), onClick = onClick)
}
}
}
}
}
}
@Composable
@ -133,7 +147,10 @@ fun SchedulePreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
EventList(eventList = listOf(), onClick = {})
EventList(eventList = MutableStateFlow(
PagingData.empty<EventWithPerformance>()
).collectAsLazyPagingItems(),
onClick = {})
}
}
}

View File

@ -2,10 +2,12 @@ package com.example.mobile_labs.ui.event.list
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import com.example.mobile_labs.database.AppDataContainer
import com.example.mobile_labs.database.event.repository.EventRepository
import com.example.mobile_labs.database.event.model.Event
import com.example.mobile_labs.database.event.model.EventWithPerformance
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@ -14,13 +16,5 @@ import kotlinx.coroutines.flow.stateIn
class EventListViewModel(
private val eventRepository: EventRepository
) : ViewModel() {
val eventListUiState: StateFlow<EventListUiState> = eventRepository.getAllWithPerformance().map {
EventListUiState(it)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
initialValue = EventListUiState()
)
val eventListUiState: Flow<PagingData<EventWithPerformance>> = eventRepository.getAllWithPerformance()
}
data class EventListUiState(val eventList: List<EventWithPerformance> = listOf())

View File

@ -36,6 +36,11 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey
import coil.compose.AsyncImage
import com.example.mobile_labs.R
import com.example.mobile_labs.database.AppDatabase
@ -45,9 +50,11 @@ import com.example.mobile_labs.database.performance.model.Performance
import com.example.mobile_labs.database.person.model.Person
import com.example.mobile_labs.ui.AppViewModelProvider
import com.example.mobile_labs.ui.navigation.Screen
import com.example.mobile_labs.ui.person.list.PeopleList
import com.example.mobile_labs.ui.person.list.PeopleListViewModel
import com.example.mobile_labs.ui.theme.Mobile_LabsTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext
import java.time.LocalDate
import java.time.format.DateTimeFormatter
@ -57,14 +64,14 @@ fun PerformanceList(
navController: NavController,
viewModel: PerformanceListViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val performanceListUiState by viewModel.performanceListUiState.collectAsState()
val performanceListUiState = viewModel.performanceListUiState.collectAsLazyPagingItems()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(all = 10.dp)) {
PerformanceList(
performanceList = performanceListUiState.performanceList,
performanceList = performanceListUiState,
onClick = {uid : Int ->
val route = Screen.PerformanceView.route.replace("{id}", uid.toString())
navController.navigate(route)
@ -77,13 +84,13 @@ fun PerformanceList(
@Composable
private fun PerformanceList(
modifier: Modifier = Modifier,
performanceList: List<Performance>,
performanceList: LazyPagingItems<Performance>,
onClick: (uid: Int) -> Unit
) {
Column(
modifier = modifier
) {
if (performanceList.isEmpty()) {
if (performanceList.itemCount == 0) {
Text(
text = stringResource(R.string.performance_missing_description),
textAlign = TextAlign.Center,
@ -91,13 +98,20 @@ private fun PerformanceList(
)
} else {
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(items = performanceList, key = { it.performance_uid !! }) { performance ->
items(
count = performanceList.itemCount,
key = performanceList.itemKey(),
contentType = performanceList.itemContentType()
) { index ->
val performance = performanceList[index]
performance?.let {
PerformanceListItem(performance = performance, modifier = Modifier
.padding(vertical = 7.dp), onClick = onClick)
}
}
}
}
}
}
@Composable
@ -132,7 +146,12 @@ fun SchedulePreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
PerformanceList(performanceList = listOf(), onClick = {})
PerformanceList(
performanceList = MutableStateFlow(
PagingData.empty<Performance>()
).collectAsLazyPagingItems(),
onClick = {}
)
}
}
}

View File

@ -2,9 +2,12 @@ package com.example.mobile_labs.ui.performance.list
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import com.example.mobile_labs.database.AppDataContainer
import com.example.mobile_labs.database.performance.model.Performance
import com.example.mobile_labs.database.performance.repository.PerformanceRepository
import com.example.mobile_labs.database.person.model.Person
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@ -13,17 +16,5 @@ import kotlinx.coroutines.flow.stateIn
class PerformanceListViewModel(
private val performanceRepository: PerformanceRepository
) : ViewModel() {
val performanceListUiState: StateFlow<PerformanceListUiState> = performanceRepository.getAllPerformances().map {
PerformanceListUiState(it)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
initialValue = PerformanceListUiState()
)
// suspend fun deleteStudent(student: Student) {
// studentRepository.deleteStudent(student)
// }
val performanceListUiState: Flow<PagingData<Performance>> = performanceRepository.getAllPerformances()
}
data class PerformanceListUiState(val performanceList: List<Performance> = listOf())

View File

@ -41,6 +41,11 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.paging.PagingData
import androidx.paging.compose.LazyPagingItems
import androidx.paging.compose.collectAsLazyPagingItems
import androidx.paging.compose.itemContentType
import androidx.paging.compose.itemKey
import coil.compose.AsyncImage
import com.example.mobile_labs.R
import com.example.mobile_labs.ui.navigation.Screen
@ -50,6 +55,7 @@ import com.example.mobile_labs.database.person.model.Person
import com.example.mobile_labs.ui.AppViewModelProvider
import com.example.mobile_labs.ui.theme.Mobile_LabsTheme
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext
import java.time.LocalDate
import java.time.format.DateTimeFormatter
@ -58,7 +64,7 @@ import java.time.format.DateTimeFormatter
fun PeopleList(
viewModel: PeopleListViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
val peopleListUiState by viewModel.personListUiState.collectAsState()
val peopleListUiState = viewModel.personListUiState.collectAsLazyPagingItems()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
@ -67,7 +73,7 @@ fun PeopleList(
PeopleList(
modifier = Modifier
.fillMaxSize(),
peopleList = peopleListUiState.personList
peopleList = peopleListUiState
)
}
}
@ -76,12 +82,12 @@ fun PeopleList(
@Composable
private fun PeopleList(
modifier: Modifier = Modifier,
peopleList: List<Person>,
peopleList: LazyPagingItems<Person>,
) {
Column(
modifier = modifier
) {
if (peopleList.isEmpty()) {
if (peopleList.itemCount == 0) {
Text(
text = stringResource(R.string.people_missing_description),
textAlign = TextAlign.Center,
@ -89,9 +95,15 @@ private fun PeopleList(
)
} else {
LazyColumn(modifier = Modifier.padding(all = 10.dp)) {
items(items = peopleList, key = { it.uid !! }) { person ->
PeopleListItem(person = person, modifier = Modifier
.padding(vertical = 7.dp))
items(
count = peopleList.itemCount,
key = peopleList.itemKey(),
contentType = peopleList.itemContentType()
) { index ->
val person = peopleList[index]
person?.let {
PeopleListItem(person = person, modifier = Modifier.padding(10.dp))
}
}
}
}
@ -130,7 +142,9 @@ fun PeopleListPreview() {
Surface(
color = MaterialTheme.colorScheme.background
) {
PeopleList(peopleList = listOf())
PeopleList(peopleList = MutableStateFlow(
PagingData.empty<Person>()
).collectAsLazyPagingItems())
}
}
}

View File

@ -2,9 +2,11 @@ package com.example.mobile_labs.ui.person.list
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import com.example.mobile_labs.database.AppDataContainer
import com.example.mobile_labs.database.person.model.Person
import com.example.mobile_labs.database.person.repository.PersonRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
@ -13,13 +15,5 @@ import kotlinx.coroutines.flow.stateIn
class PeopleListViewModel(
private val personRepository: PersonRepository
) : ViewModel() {
val personListUiState: StateFlow<PersonListUiState> = personRepository.getAllPeople().map {
PersonListUiState(it)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = AppDataContainer.TIMEOUT),
initialValue = PersonListUiState()
)
val personListUiState: Flow<PagingData<Person>> = personRepository.getAllPeople()
}
data class PersonListUiState(val personList: List<Person> = listOf())