5 лаба готова и сдана

This commit is contained in:
maxnes3 2023-12-23 11:23:33 +04:00
parent ca101dfb80
commit 8e644e6ba0
17 changed files with 225 additions and 54 deletions

View File

@ -14,7 +14,8 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.MobileApp" android:theme="@style/Theme.MobileApp"
tools:targetApi="31"> tools:targetApi="31"
android:networkSecurityConfig="@xml/network_security_config">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"

View File

@ -35,6 +35,7 @@ class MobileAppDataContainer(private val context: Context): MobileAppContainer {
//OfflineStoryRepository(MobileAppDataBase.getInstance(context).storyDao()) //OfflineStoryRepository(MobileAppDataBase.getInstance(context).storyDao())
RestStoryRepository(ServerService.getInstance(), RestStoryRepository(ServerService.getInstance(),
storyReposLocal, storyReposLocal,
userReposLocal,
MobileAppDataBase.getInstance(context), MobileAppDataBase.getInstance(context),
remoteKeyRepository) remoteKeyRepository)
} }
@ -51,4 +52,8 @@ class MobileAppDataContainer(private val context: Context): MobileAppContainer {
private val storyReposLocal: OfflineStoryRepository by lazy { private val storyReposLocal: OfflineStoryRepository by lazy {
OfflineStoryRepository(MobileAppDataBase.getInstance(context).storyDao()) OfflineStoryRepository(MobileAppDataBase.getInstance(context).storyDao())
} }
private val userReposLocal: OfflineUserRepository by lazy {
OfflineUserRepository(MobileAppDataBase.getInstance(context).userDao())
}
} }

View File

@ -19,7 +19,7 @@ import retrofit2.http.Query
interface ServerService { interface ServerService {
//USER //USER
@GET("USER/get/{id}") @GET("user/get/{id}")
suspend fun getUser( suspend fun getUser(
@Path("id") id: Int, @Path("id") id: Int,
): UserRemote ): UserRemote
@ -39,6 +39,9 @@ interface ServerService {
@Body user: UserRemote @Body user: UserRemote
): UserRemote ): UserRemote
@GET("user/getAll")
suspend fun getUsers(): List<UserRemote>
//STORY //STORY
@GET("story/get/{id}") @GET("story/get/{id}")
suspend fun getStory( suspend fun getStory(

View File

@ -6,11 +6,13 @@ import androidx.paging.PagingState
import androidx.paging.RemoteMediator import androidx.paging.RemoteMediator
import androidx.room.withTransaction import androidx.room.withTransaction
import com.example.mobileapp.api.model.toStory import com.example.mobileapp.api.model.toStory
import com.example.mobileapp.api.model.toUser
import com.example.mobileapp.database.MobileAppDataBase import com.example.mobileapp.database.MobileAppDataBase
import com.example.mobileapp.database.entities.RemoteKeyType import com.example.mobileapp.database.entities.RemoteKeyType
import com.example.mobileapp.database.entities.RemoteKeys import com.example.mobileapp.database.entities.RemoteKeys
import com.example.mobileapp.database.entities.Story import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.database.repositories.OfflineStoryRepository import com.example.mobileapp.database.repositories.OfflineStoryRepository
import com.example.mobileapp.database.repositories.OfflineUserRepository
import com.example.mobileapp.database.repositories.RemoteKeysRepositoryImpl import com.example.mobileapp.database.repositories.RemoteKeysRepositoryImpl
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException import java.io.IOException
@ -18,6 +20,7 @@ import java.io.IOException
@OptIn(ExperimentalPagingApi::class) @OptIn(ExperimentalPagingApi::class)
class ServiceRemoteMediator(private val service: ServerService, class ServiceRemoteMediator(private val service: ServerService,
private val storyRepository: OfflineStoryRepository, private val storyRepository: OfflineStoryRepository,
private val userRepository: OfflineUserRepository,
private val database: MobileAppDataBase, private val database: MobileAppDataBase,
private val dbRemoteKeyRepository: RemoteKeysRepositoryImpl private val dbRemoteKeyRepository: RemoteKeysRepositoryImpl
) : RemoteMediator<Int, Story>() { ) : RemoteMediator<Int, Story>() {
@ -48,12 +51,14 @@ class ServiceRemoteMediator(private val service: ServerService,
} }
try { try {
val users = service.getUsers().map { it.toUser() }
val stories = service.getStories(page, state.config.pageSize).map { it.toStory() } val stories = service.getStories(page, state.config.pageSize).map { it.toStory() }
val endOfPaginationReached = stories.isEmpty() val endOfPaginationReached = stories.isEmpty()
database.withTransaction { database.withTransaction {
if (loadType == LoadType.REFRESH) { if (loadType == LoadType.REFRESH) {
dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.STORY) dbRemoteKeyRepository.deleteRemoteKey(RemoteKeyType.STORY)
storyRepository.clearStories() storyRepository.clearStories()
userRepository.clearUsers()
} }
val prevKey = if (page == 1) null else page - 1 val prevKey = if (page == 1) null else page - 1
val nextKey = if (endOfPaginationReached) null else page + 1 val nextKey = if (endOfPaginationReached) null else page + 1
@ -66,6 +71,7 @@ class ServiceRemoteMediator(private val service: ServerService,
) )
} }
dbRemoteKeyRepository.createRemoteKeys(keys) dbRemoteKeyRepository.createRemoteKeys(keys)
userRepository.insertUsers(users)
storyRepository.insertStories(stories) storyRepository.insertStories(stories)
} }
return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached) return MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)

View File

@ -0,0 +1,64 @@
package com.example.mobileapp.api.repository
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.example.mobileapp.MobileAppContainer
import com.example.mobileapp.api.ServerService
import com.example.mobileapp.api.model.toMail
import com.example.mobileapp.api.model.toMailRemote
import com.example.mobileapp.database.entities.Mail
import com.example.mobileapp.database.repositories.MailRepository
import kotlinx.coroutines.flow.Flow
class RestMailRepository(private var service: ServerService): MailRepository {
override fun getAllMails(): Flow<PagingData<Mail>> {
return Pager(
config = PagingConfig(
pageSize = MobileAppContainer.LIMIT,
enablePlaceholders = false
),
pagingSourceFactory = { MailPagingSource(service) }
).flow
}
override suspend fun getMail(id: Int): Mail? = service.getMail(id).toMail()
override suspend fun insertMail(mail: Mail) {
service.createMail(mail.toMailRemote())
}
override suspend fun updateMail(mail: Mail) {
TODO("Not yet implemented")
}
override suspend fun deleteMail(mail: Mail) {
TODO("Not yet implemented")
}
}
class MailPagingSource(private val service: ServerService) : PagingSource<Int, Mail>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Mail> {
try {
val nextPageNumber = params.key ?: 1
val pageSize = params.loadSize
val response = service.getMails(nextPageNumber, pageSize)
val mails = response.map { it.toMail() } // Преобразование MailRemote в Mail
return LoadResult.Page(
data = mails,
prevKey = if (nextPageNumber == 1) null else nextPageNumber - 1,
nextKey = if (mails.isEmpty()) null else nextPageNumber + 1
)
} catch (exception: Exception) {
return LoadResult.Error(exception)
}
}
override fun getRefreshKey(state: PagingState<Int, Mail>): Int? {
TODO("Not yet implemented")
}
}

View File

@ -0,0 +1,118 @@
package com.example.mobileapp.api.repository
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import androidx.paging.PagingSource
import androidx.paging.PagingState
import com.example.mobileapp.MobileAppContainer
import com.example.mobileapp.api.ServerService
import com.example.mobileapp.api.ServiceRemoteMediator
import com.example.mobileapp.api.model.toMail
import com.example.mobileapp.api.model.toStory
import com.example.mobileapp.api.model.toStoryRemote
import com.example.mobileapp.database.MobileAppDataBase
import com.example.mobileapp.database.entities.Mail
import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.database.repositories.OfflineStoryRepository
import com.example.mobileapp.database.repositories.OfflineUserRepository
import com.example.mobileapp.database.repositories.RemoteKeysRepositoryImpl
import com.example.mobileapp.database.repositories.StoryRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
class RestStoryRepository(private var service: ServerService,
private val dbStoryRepository: OfflineStoryRepository,
private val dbUserRepository: OfflineUserRepository,
private val database: MobileAppDataBase,
private val dbRemoteKeyRepository: RemoteKeysRepositoryImpl
): StoryRepository {
override fun getAllStories(): Flow<PagingData<Story>> {
val pagingSourceFactory = {
dbStoryRepository.getAllStoriesPagingSource()
}
@OptIn(ExperimentalPagingApi::class)
return Pager(
config = PagingConfig(
pageSize = MobileAppContainer.LIMIT,
enablePlaceholders = false
),
remoteMediator = ServiceRemoteMediator(
service,
dbStoryRepository,
dbUserRepository,
database,
dbRemoteKeyRepository,
),
pagingSourceFactory = pagingSourceFactory
).flow
/*return Pager(
config = PagingConfig(
pageSize = MobileAppContainer.LIMIT,
enablePlaceholders = false
),
pagingSourceFactory = { StoryPagingSource(service) }
).flow*/
}
override fun getStoriesByUserId(userId: Int): Flow<PagingData<Story>> {
return Pager(
config = PagingConfig(
pageSize = MobileAppContainer.LIMIT,
enablePlaceholders = false
),
pagingSourceFactory = { StoryPagingSource(service, userId) }
).flow
}
override suspend fun getStoryById(id: Int): Story? = service.getStory(id).toStory()
override suspend fun insertStory(story: Story) {
service.createStory(story.toStoryRemote())
}
override suspend fun updateStory(story: Story) {
story.id?.let {
service.updateStory(it, story.toStoryRemote())
}
}
override suspend fun deleteStory(story: Story) {
try {
story.id?.let { this.service.deleteStory(it) }
dbStoryRepository.deleteStory(story)
}catch (ex: Exception){}
}
}
class StoryPagingSource(private val service: ServerService,
private val userId: Int? = null) : PagingSource<Int, Story>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Story> {
try {
val nextPageNumber = params.key ?: 1
val pageSize = params.loadSize
val response = if (userId != null) {
service.getUserStories(nextPageNumber, pageSize, userId)
} else {
service.getStories(nextPageNumber, pageSize)
}
val stories = response.map { it.toStory() }
return LoadResult.Page(
data = stories,
prevKey = if (nextPageNumber == 1) null else nextPageNumber - 1,
nextKey = if (stories.isEmpty()) null else nextPageNumber + 1
)
} catch (exception: Exception) {
return LoadResult.Error(exception)
}
}
override fun getRefreshKey(state: PagingState<Int, Story>): Int? {
TODO("Not yet implemented")
}
}

View File

@ -208,9 +208,9 @@ fun MailListItem(item: Mail, navController: NavHostController,
} }
val userPhoto = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.post)) } val userPhoto = remember { mutableStateOf<Bitmap>(BitmapFactory.decodeResource(context.resources, R.drawable.post)) }
val userName = remember { mutableStateOf("") } val userName = remember { mutableStateOf("UserName") }
/*LaunchedEffect(Unit){ LaunchedEffect(Unit){
val user = userViewModel.getUser(item.userId) val user = userViewModel.getUser(item.userId)
if (user != null) { if (user != null) {
userName.value = user.email userName.value = user.email
@ -218,7 +218,7 @@ fun MailListItem(item: Mail, navController: NavHostController,
userPhoto.value = user.photo userPhoto.value = user.photo
} }
} }
}*/ }
Card( Card(
modifier = Modifier modifier = Modifier

View File

@ -17,7 +17,7 @@ interface MailDao {
fun getAll(): PagingSource<Int, Mail> fun getAll(): PagingSource<Int, Mail>
@Query("select * from mails where mails.id = :id") @Query("select * from mails where mails.id = :id")
fun getById(id: Int): Flow<Mail?> suspend fun getById(id: Int): Mail?
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(mail: Mail) suspend fun insert(mail: Mail)

View File

@ -21,11 +21,14 @@ interface UserDao {
suspend fun getByLogin(login: String): User? suspend fun getByLogin(login: String): User?
@Insert(onConflict = OnConflictStrategy.IGNORE) @Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(user: User) suspend fun insert(vararg user: User)
@Update @Update
suspend fun update(user: User) suspend fun update(user: User)
@Delete @Delete
suspend fun delete(user: User) suspend fun delete(user: User)
@Query("delete from stories")
suspend fun deleteAll()
} }

View File

@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.Flow
interface MailRepository { interface MailRepository {
fun getAllMails(): Flow<PagingData<Mail>> fun getAllMails(): Flow<PagingData<Mail>>
fun getMail(id: Int): Flow<Mail?> suspend fun getMail(id: Int): Mail?
suspend fun insertMail(mail: Mail) suspend fun insertMail(mail: Mail)

View File

@ -23,7 +23,7 @@ class OfflineMailRepository(private val mailDao: MailDao): MailRepository {
).flow ).flow
} }
override fun getMail(id: Int): Flow<Mail?> = mailDao.getById(id) override suspend fun getMail(id: Int): Mail? = mailDao.getById(id)
override suspend fun insertMail(mail: Mail) = mailDao.insert(mail) override suspend fun insertMail(mail: Mail) = mailDao.insert(mail)

View File

@ -2,6 +2,7 @@ package com.example.mobileapp.database.repositories
import com.example.mobileapp.api.model.UserRemoteSignIn import com.example.mobileapp.api.model.UserRemoteSignIn
import com.example.mobileapp.database.dao.UserDao import com.example.mobileapp.database.dao.UserDao
import com.example.mobileapp.database.entities.Story
import com.example.mobileapp.database.entities.User import com.example.mobileapp.database.entities.User
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -17,4 +18,8 @@ class OfflineUserRepository(private val userDao: UserDao): UserRepository {
override suspend fun updateUser(user: User) = userDao.update(user) override suspend fun updateUser(user: User) = userDao.update(user)
override suspend fun deleteUser(user: User) = userDao.delete(user) override suspend fun deleteUser(user: User) = userDao.delete(user)
suspend fun clearUsers() = userDao.deleteAll()
suspend fun insertUsers(users: List<User>) =
userDao.insert(*users.toTypedArray())
} }

View File

@ -12,7 +12,7 @@ import kotlinx.coroutines.launch
class MailViewModel(private val mailRepository: MailRepository): ViewModel() { class MailViewModel(private val mailRepository: MailRepository): ViewModel() {
val getAllMails: Flow<PagingData<Mail>> = mailRepository.getAllMails().cachedIn(viewModelScope) val getAllMails: Flow<PagingData<Mail>> = mailRepository.getAllMails().cachedIn(viewModelScope)
fun getMail(id: Int): Flow<Mail?> = mailRepository.getMail(id) suspend fun getMail(id: Int): Mail? = mailRepository.getMail(id)
fun insertMail(mail: Mail) = viewModelScope.launch { fun insertMail(mail: Mail) = viewModelScope.launch {
mailRepository.insertMail(mail) mailRepository.insertMail(mail)

View File

@ -84,17 +84,6 @@ fun EditStoryScreen(navController: NavHostController, storyId: Int? = null,
title.value = story.title title.value = story.title
description.value = story.description description.value = story.description
} }
/*storyViewModel.getStoryById(storyId).collect {
if (it != null) {
cover.value = it.cover
}
if (it != null) {
title.value = it.title
}
if (it != null) {
description.value = it.description
}
}*/
} }
} }

View File

@ -58,6 +58,5 @@ fun ListMailScreen(navController: NavHostController,
} }
} }
} }
//DataListScroll(navController, mails)
} }
} }

View File

@ -36,33 +36,12 @@ fun ListStoryScreen(navController: NavHostController,
factory = MobileAppViewModelProvider.Factory factory = MobileAppViewModelProvider.Factory
)) { )) {
val stories = storyViewModel.getStoriesByUserId.collectAsLazyPagingItems() val stories = storyViewModel.getStoriesByUserId.collectAsLazyPagingItems()
/*val stories = remember { mutableStateListOf<Story>() }
LaunchedEffect(Unit){
withContext(Dispatchers.IO) {
storyViewModel.getStoriesByUserId(GlobalUser.getInstance().getUser()?.id!!).collect {
stories.clear()
stories.addAll(it)
}
}
}*/
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(BackgroundItem1) .background(BackgroundItem1)
) { ) {
/*LazyColumn(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
){
item {
addNewListItem(navController, "editstory")
}
itemsIndexed(stories){ _, item ->
StoryListItem(item = item, navController = navController)
}
}*/
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(1) columns = GridCells.Fixed(1)
) { ) {

View File

@ -124,11 +124,12 @@ fun MailViewScreen(navController: NavHostController, mailId: Int,
val postdate = remember { mutableStateOf<Long>(0) } val postdate = remember { mutableStateOf<Long>(0) }
LaunchedEffect(Unit){ LaunchedEffect(Unit){
mailViewModel.getMail(mailId).collect{ val mail = mailViewModel.getMail(mailId)
if (it != null) { if (mail != null) {
message.value = it.message message.value = mail.message
postdate.value = it.postdate!! postdate.value = mail.postdate!!
val user = userViewModel.getUser(it.userId) }
val user = mail?.let { userViewModel.getUser(it.userId) }
if (user != null) { if (user != null) {
if(user.photo != null) { if(user.photo != null) {
photo.value = user.photo photo.value = user.photo
@ -136,8 +137,6 @@ fun MailViewScreen(navController: NavHostController, mailId: Int,
userName.value = user.email userName.value = user.email
} }
} }
}
}
Column( Column(
modifier = Modifier modifier = Modifier