Merge pull request #5640 from k9mail/remove_FoldersLiveData

Remove FoldersLiveData
This commit is contained in:
cketti 2021-09-04 18:02:32 +02:00 committed by GitHub
commit a40189167c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 120 additions and 145 deletions

View file

@ -29,6 +29,10 @@ object DI {
fun <T : Any> get(clazz: Class<T>): T {
return koinGet(clazz)
}
inline fun <reified T : Any> get(): T {
return koinGet(T::class.java)
}
}
interface EarlyInit

View file

@ -2,6 +2,9 @@ package com.fsck.k9.mailstore
import com.fsck.k9.Account
import com.fsck.k9.Account.FolderMode
import com.fsck.k9.DI
import com.fsck.k9.controller.MessagingController
import com.fsck.k9.controller.SimpleMessagingListener
import com.fsck.k9.mail.FolderClass
import com.fsck.k9.preferences.AccountManager
import kotlinx.coroutines.CoroutineDispatcher
@ -52,6 +55,42 @@ class FolderRepository(
}.sortedWith(sortForDisplay)
}
fun getDisplayFoldersFlow(account: Account, displayMode: FolderMode): Flow<List<DisplayFolder>> {
val messagingController = DI.get<MessagingController>()
return callbackFlow {
send(getDisplayFolders(account, displayMode))
val listener = object : SimpleMessagingListener() {
override fun folderStatusChanged(statusChangedAccount: Account, folderId: Long) {
if (statusChangedAccount.uuid == account.uuid) {
launch {
send(getDisplayFolders(account, displayMode))
}
}
}
}
messagingController.addListener(listener)
awaitClose {
messagingController.removeListener(listener)
}
}.buffer(capacity = Channel.CONFLATED)
.distinctUntilChanged()
.flowOn(ioDispatcher)
}
fun getDisplayFoldersFlow(account: Account): Flow<List<DisplayFolder>> {
return accountManager.getAccountFlow(account.uuid)
.map { latestAccount ->
AccountContainer(latestAccount, latestAccount.folderDisplayMode)
}
.distinctUntilChanged()
.flatMapLatest { (account, folderDisplayMode) ->
getDisplayFoldersFlow(account, folderDisplayMode)
}
}
fun getFolder(account: Account, folderId: Long): Folder? {
val messageStore = messageStoreManager.getMessageStore(account)
return messageStore.getFolder(folderId) { folder ->
@ -244,6 +283,11 @@ class FolderRepository(
get() = if (syncClass == FolderClass.INHERITED) displayClass else syncClass
}
private data class AccountContainer(
val account: Account,
val folderDisplayMode: FolderMode
)
data class Folder(val id: Long, val name: String, val type: FolderType, val isLocalOnly: Boolean)
data class RemoteFolder(val id: Long, val serverId: String, val name: String, val type: FolderType)

View file

@ -94,6 +94,7 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
private var folderBadgeStyle: BadgeStyle? = null
private var openedAccountUuid: String? = null
private var openedFolderId: Long? = null
private var latestFolderList: List<DisplayFolder>? = null
val layout: DrawerLayout
get() = drawer
@ -292,7 +293,7 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
if (oldSelectedBackgroundColor != selectedBackgroundColor) {
// Recreate list of folders with updated account color
setUserFolders(foldersViewModel.getFolderListLiveData().value)
setUserFolders(latestFolderList)
}
}
@ -352,6 +353,7 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
}
private fun setUserFolders(folders: List<DisplayFolder>?) {
this.latestFolderList = folders
clearUserFolders()
var openedFolderDrawerId: Long = -1

View file

@ -7,7 +7,6 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import com.fsck.k9.Account
import com.fsck.k9.Account.FolderMode
@ -20,7 +19,6 @@ import com.fsck.k9.ui.R
import com.fsck.k9.ui.base.K9Activity
import com.fsck.k9.ui.folders.FolderIconProvider
import com.fsck.k9.ui.folders.FolderNameFormatter
import com.fsck.k9.ui.folders.FoldersLiveData
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter
import java.util.Locale
@ -42,11 +40,6 @@ class ChooseFolderActivity : K9Activity() {
private var scrollToFolderId: Long? = null
private var messageReference: String? = null
private var showDisplayableOnly = false
private var foldersLiveData: FoldersLiveData? = null
private val folderListObserver = Observer<List<DisplayFolder>> { folders ->
updateFolderList(folders)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -60,12 +53,14 @@ class ChooseFolderActivity : K9Activity() {
initializeFolderList()
viewModel.getFolders().observe(this) { folders ->
updateFolderList(folders)
}
val savedDisplayMode = savedInstanceState?.getString(STATE_DISPLAY_MODE)?.let { FolderMode.valueOf(it) }
val displayMode = savedDisplayMode ?: getInitialDisplayMode()
foldersLiveData = viewModel.getFolders(account, displayMode).apply {
observe(this@ChooseFolderActivity, folderListObserver)
}
viewModel.setDisplayMode(account, displayMode)
}
private fun decodeArguments(savedInstanceState: Bundle?): Boolean {
@ -137,7 +132,7 @@ class ChooseFolderActivity : K9Activity() {
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
scrollToFolderId?.let { folderId -> outState.putLong(STATE_SCROLL_TO_FOLDER_ID, folderId) }
outState.putString(STATE_DISPLAY_MODE, foldersLiveData?.displayMode?.name)
outState.putString(STATE_DISPLAY_MODE, viewModel.currentDisplayMode?.name)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -181,10 +176,7 @@ class ChooseFolderActivity : K9Activity() {
}
private fun setDisplayMode(displayMode: FolderMode) {
foldersLiveData?.removeObserver(folderListObserver)
foldersLiveData = viewModel.getFolders(account, displayMode).apply {
observe(this@ChooseFolderActivity, folderListObserver)
}
viewModel.setDisplayMode(account, displayMode)
}
private fun returnResult(folderId: Long, displayName: String) {

View file

@ -1,22 +1,39 @@
package com.fsck.k9.ui.choosefolder
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.fsck.k9.Account
import com.fsck.k9.Account.FolderMode
import com.fsck.k9.ui.folders.FoldersLiveData
import com.fsck.k9.ui.folders.FoldersLiveDataFactory
import com.fsck.k9.mailstore.DisplayFolder
import com.fsck.k9.mailstore.FolderRepository
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
class ChooseFolderViewModel(private val foldersLiveDataFactory: FoldersLiveDataFactory) : ViewModel() {
private var foldersLiveData: FoldersLiveData? = null
fun getFolders(account: Account, displayMode: FolderMode): FoldersLiveData {
val liveData = foldersLiveData
if (liveData != null && liveData.account.uuid == account.uuid && liveData.displayMode == displayMode) {
return liveData
@OptIn(ExperimentalCoroutinesApi::class)
class ChooseFolderViewModel(private val folderRepository: FolderRepository) : ViewModel() {
private val inputFlow = MutableSharedFlow<DisplayMode>(replay = 1)
private val foldersFlow = inputFlow
.flatMapLatest { (account, displayMode) ->
folderRepository.getDisplayFoldersFlow(account, displayMode)
}
return foldersLiveDataFactory.create(account, displayMode).also {
foldersLiveData = it
var currentDisplayMode: FolderMode? = null
private set
fun getFolders(): LiveData<List<DisplayFolder>> {
return foldersFlow.asLiveData()
}
fun setDisplayMode(account: Account, displayMode: FolderMode) {
currentDisplayMode = displayMode
viewModelScope.launch {
inputFlow.emit(DisplayMode(account, displayMode))
}
}
}
private data class DisplayMode(val account: Account, val displayMode: FolderMode)

View file

@ -1,58 +0,0 @@
package com.fsck.k9.ui.folders
import androidx.lifecycle.LiveData
import com.fsck.k9.Account
import com.fsck.k9.Account.FolderMode
import com.fsck.k9.AccountsChangeListener
import com.fsck.k9.Preferences
import com.fsck.k9.controller.MessagingController
import com.fsck.k9.controller.SimpleMessagingListener
import com.fsck.k9.mailstore.DisplayFolder
import com.fsck.k9.mailstore.FolderRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class FoldersLiveData(
private val folderRepository: FolderRepository,
private val messagingController: MessagingController,
private val preferences: Preferences,
val account: Account,
val displayMode: FolderMode?
) : LiveData<List<DisplayFolder>>() {
private val messagingListener = object : SimpleMessagingListener() {
override fun folderStatusChanged(account: Account, folderId: Long) {
if (account.uuid == this@FoldersLiveData.account.uuid) {
loadFoldersAsync()
}
}
}
private val accountsListener = AccountsChangeListener {
loadFoldersAsync()
}
private fun loadFoldersAsync() {
GlobalScope.launch(Dispatchers.Main) {
val displayFolders = withContext(Dispatchers.IO) {
folderRepository.getDisplayFolders(account, displayMode)
}
value = displayFolders
}
}
override fun onActive() {
super.onActive()
messagingController.addListener(messagingListener)
preferences.addOnAccountsChangeListener(accountsListener)
loadFoldersAsync()
}
override fun onInactive() {
super.onInactive()
messagingController.removeListener(messagingListener)
preferences.removeOnAccountsChangeListener(accountsListener)
}
}

View file

@ -1,17 +0,0 @@
package com.fsck.k9.ui.folders
import com.fsck.k9.Account
import com.fsck.k9.Account.FolderMode
import com.fsck.k9.Preferences
import com.fsck.k9.controller.MessagingController
import com.fsck.k9.mailstore.FolderRepository
class FoldersLiveDataFactory(
private val folderRepository: FolderRepository,
private val messagingController: MessagingController,
private val preferences: Preferences
) {
fun create(account: Account, displayMode: FolderMode? = null): FoldersLiveData {
return FoldersLiveData(folderRepository, messagingController, preferences, account, displayMode)
}
}

View file

@ -1,42 +1,41 @@
package com.fsck.k9.ui.folders
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.fsck.k9.Account
import com.fsck.k9.mailstore.DisplayFolder
import com.fsck.k9.mailstore.FolderRepository
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
class FoldersViewModel(private val foldersLiveDataFactory: FoldersLiveDataFactory) : ViewModel() {
private var currentFoldersLiveData: FoldersLiveData? = null
private val foldersLiveData = MediatorLiveData<List<DisplayFolder>>()
@OptIn(ExperimentalCoroutinesApi::class)
class FoldersViewModel(private val folderRepository: FolderRepository) : ViewModel() {
private val inputFlow = MutableSharedFlow<Account?>(replay = 1)
private val foldersFlow = inputFlow
.flatMapLatest { account ->
if (account == null) {
flowOf(emptyList())
} else {
folderRepository.getDisplayFoldersFlow(account)
}
}
fun getFolderListLiveData(): LiveData<List<DisplayFolder>> {
return foldersLiveData
return foldersFlow.asLiveData()
}
fun loadFolders(account: Account) {
if (currentFoldersLiveData?.account?.uuid == account.uuid) return
viewModelScope.launch {
// When switching accounts we want to remove the old list right away, not keep it until the new list
// has been loaded.
inputFlow.emit(null)
removeCurrentFoldersLiveData()
val liveData = foldersLiveDataFactory.create(account)
currentFoldersLiveData = liveData
foldersLiveData.addSource(liveData) { items ->
foldersLiveData.value = items
}
}
fun stopLoadingFolders() {
removeCurrentFoldersLiveData()
foldersLiveData.value = null
}
private fun removeCurrentFoldersLiveData() {
currentFoldersLiveData?.let {
foldersLiveData.value = emptyList()
currentFoldersLiveData = null
foldersLiveData.removeSource(it)
inputFlow.emit(account)
}
}
}

View file

@ -7,6 +7,5 @@ import org.koin.dsl.module
val foldersUiModule = module {
single { FolderNameFormatterFactory() }
factory { (context: Context) -> FolderNameFormatter(context.resources) }
single { FoldersLiveDataFactory(get(), get(), get()) }
viewModel { FoldersViewModel(get()) }
viewModel { FoldersViewModel(folderRepository = get()) }
}

View file

@ -4,6 +4,6 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val manageFoldersUiModule = module {
viewModel { ManageFoldersViewModel(foldersLiveDataFactory = get()) }
viewModel { ManageFoldersViewModel(folderRepository = get()) }
viewModel { FolderSettingsViewModel(preferences = get(), folderRepository = get(), messagingController = get()) }
}

View file

@ -1,21 +1,14 @@
package com.fsck.k9.ui.managefolders
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import com.fsck.k9.Account
import com.fsck.k9.ui.folders.FoldersLiveData
import com.fsck.k9.ui.folders.FoldersLiveDataFactory
import com.fsck.k9.mailstore.DisplayFolder
import com.fsck.k9.mailstore.FolderRepository
class ManageFoldersViewModel(private val foldersLiveDataFactory: FoldersLiveDataFactory) : ViewModel() {
private var foldersLiveData: FoldersLiveData? = null
fun getFolders(account: Account): FoldersLiveData {
val liveData = foldersLiveData
if (liveData != null && liveData.account.uuid == account.uuid) {
return liveData
}
return foldersLiveDataFactory.create(account).also {
foldersLiveData = it
}
class ManageFoldersViewModel(private val folderRepository: FolderRepository) : ViewModel() {
fun getFolders(account: Account): LiveData<List<DisplayFolder>> {
return folderRepository.getDisplayFoldersFlow(account).asLiveData()
}
}