Merge pull request #4114 from k9mail/drawer_unread_count
Display unread message count in drawer
This commit is contained in:
commit
d4d1280a9c
7 changed files with 174 additions and 60 deletions
|
@ -1,5 +1,6 @@
|
|||
package com.fsck.k9.mailstore
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.Account.FolderMode
|
||||
import com.fsck.k9.mail.Folder.FolderClass
|
||||
|
@ -10,11 +11,12 @@ class FolderRepository(
|
|||
private val specialFolderSelectionStrategy: SpecialFolderSelectionStrategy,
|
||||
private val account: Account
|
||||
) {
|
||||
private val sortForDisplay = compareByDescending<LocalFolder> { it.serverId == account.inboxFolder }
|
||||
.thenByDescending { it.serverId == account.outboxFolder }
|
||||
.thenByDescending { account.isSpecialFolder(it.serverId) }
|
||||
private val sortForDisplay =
|
||||
compareByDescending<DisplayFolder> { it.folder.serverId == account.inboxFolder }
|
||||
.thenByDescending { it.folder.serverId == account.outboxFolder }
|
||||
.thenByDescending { account.isSpecialFolder(it.folder.serverId) }
|
||||
.thenByDescending { it.isInTopGroup }
|
||||
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name }
|
||||
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.folder.name }
|
||||
|
||||
|
||||
fun getRemoteFolderInfo(): RemoteFolderInfo {
|
||||
|
@ -38,33 +40,72 @@ class FolderRepository(
|
|||
.map { Folder(it.databaseId, it.serverId, it.name, it.type.toFolderType()) }
|
||||
}
|
||||
|
||||
fun getDisplayFolders(): List<Folder> {
|
||||
val folders = localStoreProvider.getInstance(account).getPersonalNamespaces(false)
|
||||
return folders
|
||||
.filter(::shouldDisplayFolder)
|
||||
.sortedWith(sortForDisplay)
|
||||
.map(::createFolderFromLocalFolder)
|
||||
fun getDisplayFolders(): List<DisplayFolder> {
|
||||
val database = localStoreProvider.getInstance(account).database
|
||||
val displayFolders = database.execute(false) { db -> getDisplayFolders(db) }
|
||||
|
||||
return displayFolders.sortedWith(sortForDisplay)
|
||||
}
|
||||
|
||||
private fun shouldDisplayFolder(localFolder: LocalFolder): Boolean {
|
||||
val displayMode = account.folderDisplayMode
|
||||
val displayClass = localFolder.displayClass
|
||||
return when (displayMode) {
|
||||
FolderMode.ALL -> true
|
||||
FolderMode.FIRST_CLASS -> displayClass == FolderClass.FIRST_CLASS
|
||||
FolderMode.FIRST_AND_SECOND_CLASS -> {
|
||||
displayClass == FolderClass.FIRST_CLASS || displayClass == FolderClass.SECOND_CLASS
|
||||
private fun getDisplayFolders(db: SQLiteDatabase): List<DisplayFolder> {
|
||||
val queryBuilder = StringBuilder("""
|
||||
SELECT f.id, f.server_id, f.name, f.top_group, (
|
||||
SELECT COUNT(m.id)
|
||||
FROM messages m
|
||||
WHERE m.folder_id = f.id AND m.empty = 0 AND m.deleted = 0 AND m.read = 0
|
||||
)
|
||||
FROM folders f
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
addDisplayClassSelection(queryBuilder)
|
||||
|
||||
val query = queryBuilder.toString()
|
||||
db.rawQuery(query, null).use { cursor ->
|
||||
val displayFolders = mutableListOf<DisplayFolder>()
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
val id = cursor.getLong(0)
|
||||
val serverId = cursor.getString(1)
|
||||
val name = cursor.getString(2)
|
||||
val type = folderTypeOf(serverId)
|
||||
val isInTopGroup = cursor.getInt(3) == 1
|
||||
val unreadCount = cursor.getInt(4)
|
||||
|
||||
val folder = Folder(id, serverId, name, type)
|
||||
displayFolders.add(DisplayFolder(folder, isInTopGroup, unreadCount))
|
||||
}
|
||||
FolderMode.NOT_SECOND_CLASS -> displayClass != FolderClass.SECOND_CLASS
|
||||
else -> throw AssertionError("Invalid folder display mode: $displayMode")
|
||||
|
||||
return displayFolders
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFolderFromLocalFolder(localFolder: LocalFolder): Folder {
|
||||
return Folder(localFolder.databaseId, localFolder.serverId, localFolder.name, folderTypeOf(localFolder))
|
||||
private fun addDisplayClassSelection(query: StringBuilder) {
|
||||
when (val displayMode = account.folderDisplayMode) {
|
||||
FolderMode.ALL -> Unit // Return all folders
|
||||
FolderMode.FIRST_CLASS -> {
|
||||
query.append(" WHERE f.display_class = '")
|
||||
.append(FolderClass.FIRST_CLASS.name)
|
||||
.append("'")
|
||||
}
|
||||
FolderMode.FIRST_AND_SECOND_CLASS -> {
|
||||
query.append(" WHERE f.display_class IN ('")
|
||||
.append(FolderClass.FIRST_CLASS.name)
|
||||
.append("', '")
|
||||
.append(FolderClass.SECOND_CLASS.name)
|
||||
.append("')")
|
||||
}
|
||||
FolderMode.NOT_SECOND_CLASS -> {
|
||||
query.append(" WHERE f.display_class != '")
|
||||
.append(FolderClass.SECOND_CLASS.name)
|
||||
.append("'")
|
||||
}
|
||||
FolderMode.NONE -> throw AssertionError("Invalid folder display mode: $displayMode")
|
||||
}
|
||||
}
|
||||
|
||||
private fun folderTypeOf(folder: LocalFolder) = when (folder.serverId) {
|
||||
|
||||
private fun folderTypeOf(serverId: String) = when (serverId) {
|
||||
account.inboxFolder -> FolderType.INBOX
|
||||
account.outboxFolder -> FolderType.OUTBOX
|
||||
account.sentFolder -> FolderType.SENT
|
||||
|
@ -89,6 +130,12 @@ class FolderRepository(
|
|||
|
||||
data class Folder(val id: Long, val serverId: String, val name: String, val type: FolderType)
|
||||
|
||||
data class DisplayFolder(
|
||||
val folder: Folder,
|
||||
val isInTopGroup: Boolean,
|
||||
val unreadCount: Int
|
||||
)
|
||||
|
||||
data class RemoteFolderInfo(val folders: List<Folder>, val automaticSpecialFolders: Map<FolderType, Folder?>)
|
||||
|
||||
enum class FolderType {
|
||||
|
|
|
@ -15,9 +15,11 @@ import com.fsck.k9.K9
|
|||
import com.fsck.k9.Preferences
|
||||
import com.fsck.k9.activity.MessageList
|
||||
import com.fsck.k9.helper.Contacts
|
||||
import com.fsck.k9.mailstore.DisplayFolder
|
||||
import com.fsck.k9.mailstore.Folder
|
||||
import com.fsck.k9.mailstore.FolderType
|
||||
import com.fsck.k9.ui.folders.FolderNameFormatter
|
||||
import com.fsck.k9.ui.folders.FoldersLiveData
|
||||
import com.fsck.k9.ui.messagelist.MessageListViewModel
|
||||
import com.fsck.k9.ui.messagelist.MessageListViewModelFactory
|
||||
import com.fsck.k9.ui.settings.SettingsActivity
|
||||
|
@ -56,6 +58,11 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) {
|
|||
private var unifiedInboxSelected: Boolean = false
|
||||
private var openedFolderServerId: String? = null
|
||||
|
||||
private var foldersLiveData: FoldersLiveData? = null
|
||||
private val foldersObserver = Observer<List<DisplayFolder>> { folders ->
|
||||
setUserFolders(folders)
|
||||
}
|
||||
|
||||
|
||||
val layout: DrawerLayout
|
||||
get() = drawer.drawerLayout
|
||||
|
@ -201,9 +208,12 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) {
|
|||
accountHeader.headerBackgroundView.setColorFilter(account.chipColor, PorterDuff.Mode.MULTIPLY)
|
||||
val viewModelProvider = ViewModelProviders.of(parent, MessageListViewModelFactory())
|
||||
val viewModel = viewModelProvider.get(MessageListViewModel::class.java)
|
||||
viewModel.getFolders(account).observe(parent, Observer {
|
||||
folders -> setUserFolders(folders)
|
||||
})
|
||||
|
||||
foldersLiveData?.removeObserver(foldersObserver)
|
||||
foldersLiveData = viewModel.getFolders(account).apply {
|
||||
observe(parent, foldersObserver)
|
||||
}
|
||||
|
||||
updateFolderSettingsItem()
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +238,7 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun setUserFolders(folders: List<Folder>?) {
|
||||
private fun setUserFolders(folders: List<DisplayFolder>?) {
|
||||
clearUserFolders()
|
||||
|
||||
if (folders == null) {
|
||||
|
@ -237,14 +247,22 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) {
|
|||
|
||||
var openedFolderDrawerId: Long = -1
|
||||
for (i in folders.indices.reversed()) {
|
||||
val folder = folders[i]
|
||||
val displayFolder = folders[i]
|
||||
val folder = displayFolder.folder
|
||||
val drawerId = folder.id shl DRAWER_FOLDER_SHIFT
|
||||
drawer.addItemAtPosition(PrimaryDrawerItem()
|
||||
|
||||
val drawerItem = PrimaryDrawerItem()
|
||||
.withIcon(getFolderIcon(folder))
|
||||
.withIdentifier(drawerId)
|
||||
.withTag(folder)
|
||||
.withName(getFolderDisplayName(folder)),
|
||||
headerItemCount)
|
||||
.withName(getFolderDisplayName(folder))
|
||||
|
||||
val unreadCount = displayFolder.unreadCount
|
||||
if (unreadCount > 0) {
|
||||
drawerItem.withBadge(unreadCount.toString())
|
||||
}
|
||||
|
||||
drawer.addItemAtPosition(drawerItem, headerItemCount)
|
||||
|
||||
userFolderDrawerIds.add(drawerId)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.fsck.k9.ui
|
||||
|
||||
import com.fsck.k9.ui.folders.FolderNameFormatter
|
||||
import com.fsck.k9.ui.folders.FoldersLiveDataFactory
|
||||
import com.fsck.k9.ui.helper.DisplayHtmlUiFactory
|
||||
import com.fsck.k9.ui.helper.HtmlSettingsProvider
|
||||
import com.fsck.k9.ui.helper.HtmlToSpanned
|
||||
|
@ -12,4 +13,5 @@ val uiModule = applicationContext {
|
|||
bean { ThemeManager(get()) }
|
||||
bean { HtmlSettingsProvider(get()) }
|
||||
bean { DisplayHtmlUiFactory(get()) }
|
||||
bean { FoldersLiveDataFactory(get(), get()) }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package com.fsck.k9.ui.folders
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.fsck.k9.Account
|
||||
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,
|
||||
val accountUuid: String
|
||||
) : LiveData<List<DisplayFolder>>() {
|
||||
|
||||
private val listener = object : SimpleMessagingListener() {
|
||||
override fun folderStatusChanged(
|
||||
account: Account?,
|
||||
folderServerId: String?,
|
||||
unreadMessageCount: Int
|
||||
) {
|
||||
if (account?.uuid == accountUuid) {
|
||||
loadFoldersAsync()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadFoldersAsync() {
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
value = withContext(Dispatchers.IO) { folderRepository.getDisplayFolders() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActive() {
|
||||
super.onActive()
|
||||
messagingController.addListener(listener)
|
||||
loadFoldersAsync()
|
||||
}
|
||||
|
||||
override fun onInactive() {
|
||||
super.onInactive()
|
||||
messagingController.removeListener(listener)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.fsck.k9.ui.folders
|
||||
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.controller.MessagingController
|
||||
import com.fsck.k9.mailstore.FolderRepositoryManager
|
||||
|
||||
class FoldersLiveDataFactory(
|
||||
private val folderRepositoryManager: FolderRepositoryManager,
|
||||
private val messagingController: MessagingController
|
||||
) {
|
||||
fun create(account: Account): FoldersLiveData {
|
||||
val folderRepository = folderRepositoryManager.getFolderRepository(account)
|
||||
return FoldersLiveData(folderRepository, messagingController, account.uuid)
|
||||
}
|
||||
}
|
|
@ -1,38 +1,22 @@
|
|||
package com.fsck.k9.ui.messagelist
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.mailstore.Folder
|
||||
import com.fsck.k9.mailstore.FolderRepositoryManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import com.fsck.k9.ui.folders.FoldersLiveData
|
||||
import com.fsck.k9.ui.folders.FoldersLiveDataFactory
|
||||
|
||||
class MessageListViewModel(private val folderRepositoryManager: FolderRepositoryManager) : ViewModel() {
|
||||
private val foldersLiveData = MutableLiveData<List<Folder>>()
|
||||
private var account: Account? = null
|
||||
class MessageListViewModel(private val foldersLiveDataFactory: FoldersLiveDataFactory) : ViewModel() {
|
||||
private var foldersLiveData: FoldersLiveData? = null
|
||||
|
||||
|
||||
fun getFolders(account: Account): LiveData<List<Folder>> {
|
||||
if (foldersLiveData.value == null || this.account != account) {
|
||||
this.account = account
|
||||
loadFolders(account)
|
||||
fun getFolders(account: Account): FoldersLiveData {
|
||||
val liveData = foldersLiveData
|
||||
if (liveData != null && liveData.accountUuid == account.uuid) {
|
||||
return liveData
|
||||
}
|
||||
|
||||
return foldersLiveData
|
||||
}
|
||||
|
||||
private fun loadFolders(account: Account) {
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
val folders = async {
|
||||
val folderRepository = folderRepositoryManager.getFolderRepository(account)
|
||||
folderRepository.getDisplayFolders()
|
||||
}.await()
|
||||
|
||||
foldersLiveData.value = folders
|
||||
return foldersLiveDataFactory.create(account).also {
|
||||
foldersLiveData = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,15 +2,15 @@ package com.fsck.k9.ui.messagelist
|
|||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.fsck.k9.mailstore.FolderRepositoryManager
|
||||
import com.fsck.k9.ui.folders.FoldersLiveDataFactory
|
||||
import org.koin.standalone.KoinComponent
|
||||
import org.koin.standalone.inject
|
||||
|
||||
class MessageListViewModelFactory : ViewModelProvider.Factory, KoinComponent {
|
||||
private val folderRepositoryManager: FolderRepositoryManager by inject()
|
||||
private val foldersLiveDataFactory: FoldersLiveDataFactory by inject()
|
||||
|
||||
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return MessageListViewModel(folderRepositoryManager) as T
|
||||
return MessageListViewModel(foldersLiveDataFactory) as T
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue