Show account unread count on account list in drawer

This commit is contained in:
Art O Cathain 2021-06-05 15:27:36 +10:00
parent 295b9d8d28
commit 886a5a0610
6 changed files with 101 additions and 7 deletions

View file

@ -0,0 +1,2 @@
<!-- for use as badge background where we only want the number and no shape -->
<shape/>

View file

@ -33,7 +33,7 @@ dependencies {
implementation "de.cketti.library.changelog:ckchangelog-core:2.0.0-beta01"
implementation "com.splitwise:tokenautocomplete:4.0.0-beta01"
implementation "de.cketti.safecontentresolver:safe-content-resolver-v21:1.0.0"
implementation 'com.mikepenz:materialdrawer:8.4.0'
implementation 'com.mikepenz:materialdrawer:8.4.1'
implementation 'com.mikepenz:materialdrawer-iconics:8.3.3'
implementation 'com.mikepenz:fontawesome-typeface:5.9.0.0-kotlin@aar'
implementation 'com.github.ByteHamster:SearchPreference:v2.0.0'

View file

@ -21,6 +21,7 @@ import com.fsck.k9.mailstore.DisplayFolder
import com.fsck.k9.mailstore.Folder
import com.fsck.k9.ui.account.AccountImageLoader
import com.fsck.k9.ui.account.AccountsViewModel
import com.fsck.k9.ui.account.DisplayAccount
import com.fsck.k9.ui.base.Theme
import com.fsck.k9.ui.base.ThemeManager
import com.fsck.k9.ui.folders.FolderIconProvider
@ -68,6 +69,7 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
private val headerView: AccountHeaderView = AccountHeaderView(parent).apply {
attachToSliderView(this@K9Drawer.sliderView)
dividerBelowHeader = false
displayBadgesOnCurrentProfileImage = false
}
private val folderIconProvider: FolderIconProvider = FolderIconProvider(parent.theme)
private val swipeRefreshLayout: SwipeRefreshLayout
@ -120,7 +122,7 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
addFooterItems()
accountsViewModel.accountsLiveData.observeNotNull(parent) { accounts ->
accountsViewModel.displayAccountsLiveData.observeNotNull(parent) { accounts ->
setAccounts(accounts)
}
@ -159,11 +161,12 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
}
}
private fun setAccounts(accounts: List<Account>) {
private fun setAccounts(displayAccounts: List<DisplayAccount>) {
val oldSelectedBackgroundColor = selectedBackgroundColor
var newActiveProfile: IProfile? = null
val accountItems = accounts.map { account ->
val accountItems = displayAccounts.map { accountAndUnread ->
val account = accountAndUnread.account
val drawerId = (account.accountNumber + 1 shl DRAWER_ACCOUNT_SHIFT).toLong()
val drawerColors = getDrawerColorsForAccount(account)
@ -179,6 +182,12 @@ class K9Drawer(private val parent: MessageList, savedInstanceState: Bundle?) : K
descriptionTextColor = selectedTextColor
selectedColorInt = drawerColors.selectedColor
icon = ImageHolder(createAccountImageUri(account))
accountAndUnread.unreadCount.takeIf { it > 0 }?.let { unreadCount ->
badgeText = unreadCount.toString()
badgeStyle = BadgeStyle().apply {
textColorStateList = selectedTextColor
}
}
}
if (account.uuid == openedAccountUuid) {

View file

@ -1,8 +1,86 @@
package com.fsck.k9.ui.account
import android.content.ContentResolver
import android.database.ContentObserver
import android.os.Handler
import android.os.Looper
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import com.fsck.k9.Account
import com.fsck.k9.AccountsChangeListener
import com.fsck.k9.Preferences
import com.fsck.k9.controller.UnreadMessageCountProvider
import com.fsck.k9.provider.EmailProvider
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.launch
class AccountsViewModel(preferences: Preferences) : ViewModel() {
val accountsLiveData = AccountsLiveData(preferences)
@OptIn(ExperimentalCoroutinesApi::class)
class AccountsViewModel(
val preferences: Preferences,
val unreadMessageCountProvider: UnreadMessageCountProvider,
private val contentResolver: ContentResolver
) :
ViewModel() {
private val accountsFlow: Flow<List<Account>> =
callbackFlow {
send(preferences.accounts)
val accountsChangeListener = AccountsChangeListener {
launch {
send(preferences.accounts)
}
}
preferences.addOnAccountsChangeListener(accountsChangeListener)
awaitClose {
preferences.removeOnAccountsChangeListener(accountsChangeListener)
}
}.flowOn(Dispatchers.IO)
private val displayAccountFlow: Flow<List<DisplayAccount>> = accountsFlow
.flatMapLatest { accounts ->
val unreadCountFlows: List<Flow<Int>> = accounts.map { account ->
getUnreadCountFlow(account)
}
combine(unreadCountFlows) { unreadCounts ->
unreadCounts.mapIndexed { index, unreadCount ->
DisplayAccount(account = accounts[index], unreadCount)
}
}
}
private fun getUnreadCountFlow(account: Account): Flow<Int> {
return callbackFlow {
val notificationUri = EmailProvider.getNotificationUri(account.uuid)
send(unreadMessageCountProvider.getUnreadMessageCount(account))
val contentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
launch {
send(unreadMessageCountProvider.getUnreadMessageCount(account))
}
}
}
contentResolver.registerContentObserver(notificationUri, false, contentObserver)
awaitClose {
contentResolver.unregisterContentObserver(contentObserver)
}
}.flowOn(Dispatchers.IO)
}
val displayAccountsLiveData: LiveData<List<DisplayAccount>> = displayAccountFlow.asLiveData()
}

View file

@ -0,0 +1,5 @@
package com.fsck.k9.ui.account
import com.fsck.k9.Account
data class DisplayAccount(val account: Account, val unreadCount: Int)

View file

@ -4,7 +4,7 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module
val accountUiModule = module {
viewModel { AccountsViewModel(preferences = get()) }
viewModel { AccountsViewModel(preferences = get(), unreadMessageCountProvider = get(), contentResolver = get()) }
factory { AccountImageLoader(accountFallbackImageProvider = get()) }
factory { AccountFallbackImageProvider(context = get()) }
factory { AccountImageModelLoaderFactory(contactPhotoLoader = get(), accountFallbackImageProvider = get()) }