migrate profile fragment to paging v3

This commit is contained in:
Konrad Pozniak 2020-06-17 19:58:05 +02:00
parent d1b5c73d8b
commit 444988a623
10 changed files with 80 additions and 132 deletions

View File

@ -1,95 +0,0 @@
/*
* Copyright (C) 2020 Conny Duck
*
* This file is part of Pixelcat.
*
* Pixelcat is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pixelcat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.components.profile
import androidx.paging.DataSource
import androidx.paging.ItemKeyedDataSource
import at.connyduck.pixelcat.db.AccountManager
import at.connyduck.pixelcat.model.Status
import at.connyduck.pixelcat.network.FediverseApi
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class ProfileDataSourceFactory(
private val api: FediverseApi,
private val accountId: String?,
private val accountManager: AccountManager,
private val scope: CoroutineScope
) : DataSource.Factory<String, Status>() {
override fun create(): DataSource<String, Status> {
val source = ProfileImageDataSource(api, accountId, accountManager, scope)
return source
}
}
class ProfileImageDataSource(
private val api: FediverseApi,
private val accountId: String?,
private val accountManager: AccountManager,
private val scope: CoroutineScope
) : ItemKeyedDataSource<String, Status>() {
override fun loadInitial(
params: LoadInitialParams<String>,
callback: LoadInitialCallback<Status>
) {
scope.launch(context = Dispatchers.IO) {
val id = accountId ?: accountManager.activeAccount()?.accountId!!
api.accountStatuses(
id,
limit = params.requestedLoadSize,
onlyMedia = true,
excludeReblogs = true
).fold(
{
callback.onResult(it)
},
{
}
)
}
}
override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<Status>) {
scope.launch(context = Dispatchers.IO) {
val id = accountId ?: accountManager.activeAccount()?.accountId!!
api.accountStatuses(
id,
maxId = params.key,
limit = params.requestedLoadSize,
onlyMedia = true,
excludeReblogs = true
).fold(
{
callback.onResult(it)
},
{
}
)
}
}
override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<Status>) {
// we always load from top
}
override fun getKey(item: Status) = item.id
}

View File

@ -23,6 +23,8 @@ import android.os.Bundle
import android.view.View
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.paging.ExperimentalPagingApi
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.MergeAdapter
import at.connyduck.pixelcat.R
@ -41,6 +43,8 @@ import at.connyduck.pixelcat.util.viewBinding
import at.connyduck.pixelcat.util.withArgs
import com.google.android.material.snackbar.Snackbar
import dagger.android.support.DaggerFragment
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import javax.inject.Inject
class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
@ -60,6 +64,7 @@ class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
private val headerAdapter = ProfileHeaderAdapter()
private lateinit var imageAdapter: ProfileImageAdapter
@ExperimentalPagingApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
if (activity is MainActivity) {
@ -124,12 +129,9 @@ class ProfileFragment : DaggerFragment(R.layout.fragment_profile) {
}
}
)
viewModel.profileImages.observe(
viewLifecycleOwner,
Observer {
imageAdapter.submitList(it)
}
)
lifecycleScope.launch {
viewModel.imageFlow.collectLatest { imageAdapter.submitData(it) }
}
}
private fun onAccountChanged(account: Account?) {

View File

@ -21,7 +21,7 @@ package at.connyduck.pixelcat.components.profile
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagedListAdapter
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import at.connyduck.pixelcat.R
@ -34,7 +34,7 @@ import coil.api.load
class ProfileImageAdapter(
private val imageSizePx: Int
) : PagedListAdapter<Status, ProfileImageViewHolder>(
) : PagingDataAdapter<Status, ProfileImageViewHolder>(
object : DiffUtil.ItemCallback<Status>() {
override fun areItemsTheSame(old: Status, new: Status): Boolean {
return false

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2020 Conny Duck
*
* This file is part of Pixelcat.
*
* Pixelcat is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pixelcat is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.components.profile
import androidx.paging.PagingSource
import at.connyduck.pixelcat.db.AccountManager
import at.connyduck.pixelcat.model.Status
import at.connyduck.pixelcat.network.FediverseApi
class ProfileImagePagingSource(
private val api: FediverseApi,
private val accountId: String?,
private val accountManager: AccountManager
) : PagingSource<String, Status>() {
override suspend fun load(params: LoadParams<String>): LoadResult<String, Status> {
if (params is LoadParams.Prepend) {
// we only load from top
return LoadResult.Page(data = emptyList(), nextKey = null, prevKey = null)
}
val id = accountId ?: accountManager.activeAccount()?.accountId!!
return api.accountStatuses(
id,
maxId = params.key,
limit = params.loadSize,
onlyMedia = true,
excludeReblogs = true
).fold(
{
LoadResult.Page(data = it, prevKey = null, nextKey = it.lastOrNull()?.id)
},
{
LoadResult.Error(it)
}
)
}
}

View File

@ -22,16 +22,18 @@ package at.connyduck.pixelcat.components.profile
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagedList
import androidx.paging.ExperimentalPagingApi
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import at.connyduck.pixelcat.components.util.Error
import at.connyduck.pixelcat.components.util.Success
import at.connyduck.pixelcat.components.util.UiState
import at.connyduck.pixelcat.db.AccountManager
import at.connyduck.pixelcat.model.Account
import at.connyduck.pixelcat.model.Relationship
import at.connyduck.pixelcat.model.Status
import at.connyduck.pixelcat.network.FediverseApi
import com.bumptech.glide.util.Executors
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.launch
import javax.inject.Inject
@ -42,7 +44,14 @@ class ProfileViewModel @Inject constructor(
val profile = MutableLiveData<UiState<Account>>()
val relationship = MutableLiveData<UiState<Relationship>>()
val profileImages = MutableLiveData<PagedList<Status>>()
@OptIn(FlowPreview::class)
@ExperimentalPagingApi
val imageFlow = Pager(
config = PagingConfig(pageSize = 10, enablePlaceholders = false),
pagingSourceFactory = { ProfileImagePagingSource(fediverseApi, accountId, accountManager) }
).flow
.cachedIn(viewModelScope)
val isSelf: Boolean
get() = accountId == null
@ -54,7 +63,6 @@ class ProfileViewModel @Inject constructor(
if (!isSelf) {
loadRelationship(reload)
}
loadImages(reload)
}
fun setAccountInfo(accountId: String?) {
@ -92,22 +100,6 @@ class ProfileViewModel @Inject constructor(
}
}
private fun loadImages(reload: Boolean = false) {
if (profileImages.value == null || reload) {
profileImages.value = PagedList.Builder(
ProfileImageDataSource(
fediverseApi,
accountId,
accountManager,
viewModelScope
),
20
).setNotifyExecutor(Executors.mainThreadExecutor())
.setFetchExecutor(java.util.concurrent.Executors.newSingleThreadExecutor())
.build()
}
}
private suspend fun getAccountId(): String {
return accountId ?: accountManager.activeAccount()?.accountId!!
}

View File

@ -17,8 +17,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.db
import android.util.Log

View File

@ -17,8 +17,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.db.entitity
import androidx.room.Embedded

View File

@ -17,8 +17,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.db.entitity
import androidx.room.Embedded

View File

@ -17,7 +17,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.model
import android.os.Parcelable

View File

@ -17,7 +17,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package at.connyduck.pixelcat.model
import com.squareup.moshi.Json