Add MessageListRepository
Remove the "message list changed" notification mechanism provided by `EmailProvider` and use a simple callback mechanism instead.
This commit is contained in:
parent
18b177e18e
commit
e9b91f3654
11 changed files with 114 additions and 59 deletions
|
@ -5,10 +5,10 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.fsck.k9.DI;
|
||||
import com.fsck.k9.mailstore.LocalMessage;
|
||||
import com.fsck.k9.provider.EmailProvider;
|
||||
import com.fsck.k9.mailstore.MessageListRepository;
|
||||
|
||||
/**
|
||||
* Cache to bridge the time needed to write (user-initiated) changes to the database.
|
||||
|
@ -149,8 +149,7 @@ public class EmailProviderCache {
|
|||
}
|
||||
|
||||
private void notifyChange() {
|
||||
Uri uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI, "account/" + mAccountUuid +
|
||||
"/messages");
|
||||
sContext.getContentResolver().notifyChange(uri, null);
|
||||
MessageListRepository messageListRepository = DI.get(MessageListRepository.class);
|
||||
messageListRepository.notifyMessageListChanged(mAccountUuid);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,4 +34,5 @@ val mailStoreModule = module {
|
|||
attachmentCounter = get()
|
||||
)
|
||||
}
|
||||
single { MessageListRepository() }
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import android.content.ContentValues;
|
|||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
|
@ -51,7 +50,6 @@ import com.fsck.k9.mailstore.LockableDatabase.DbCallback;
|
|||
import com.fsck.k9.mailstore.LockableDatabase.SchemaDefinition;
|
||||
import com.fsck.k9.mailstore.StorageManager.InternalStorageProvider;
|
||||
import com.fsck.k9.message.extractors.AttachmentInfoExtractor;
|
||||
import com.fsck.k9.provider.EmailProvider;
|
||||
import com.fsck.k9.provider.EmailProvider.MessageColumns;
|
||||
import com.fsck.k9.search.LocalSearch;
|
||||
import com.fsck.k9.search.SearchSpecification.Attribute;
|
||||
|
@ -166,7 +164,6 @@ public class LocalStore {
|
|||
private static final int THREAD_FLAG_UPDATE_BATCH_SIZE = 500;
|
||||
|
||||
private final Context context;
|
||||
private final ContentResolver contentResolver;
|
||||
private final PendingCommandSerializer pendingCommandSerializer;
|
||||
private final AttachmentInfoExtractor attachmentInfoExtractor;
|
||||
|
||||
|
@ -184,7 +181,6 @@ public class LocalStore {
|
|||
*/
|
||||
private LocalStore(final Account account, final Context context) throws MessagingException {
|
||||
this.context = context;
|
||||
this.contentResolver = context.getContentResolver();
|
||||
|
||||
pendingCommandSerializer = PendingCommandSerializer.getInstance();
|
||||
attachmentInfoExtractor = DI.get(AttachmentInfoExtractor.class);
|
||||
|
@ -726,8 +722,8 @@ public class LocalStore {
|
|||
}
|
||||
|
||||
public void notifyChange() {
|
||||
Uri uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI, "account/" + account.getUuid() + "/messages");
|
||||
contentResolver.notifyChange(uri, null);
|
||||
MessageListRepository messageListRepository = DI.get(MessageListRepository.class);
|
||||
messageListRepository.notifyMessageListChanged(account.getUuid());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package com.fsck.k9.mailstore
|
||||
|
||||
import java.util.concurrent.CopyOnWriteArraySet
|
||||
|
||||
class MessageListRepository {
|
||||
private val listeners = CopyOnWriteArraySet<Pair<String, MessageListChangedListener>>()
|
||||
|
||||
fun addListener(accountUuid: String, listener: MessageListChangedListener) {
|
||||
listeners.add(accountUuid to listener)
|
||||
}
|
||||
|
||||
fun removeListener(listener: MessageListChangedListener) {
|
||||
val entries = listeners.filter { it.second == listener }.toSet()
|
||||
listeners.removeAll(entries)
|
||||
}
|
||||
|
||||
fun notifyMessageListChanged(accountUuid: String) {
|
||||
for (listener in listeners) {
|
||||
if (listener.first == accountUuid) {
|
||||
listener.second.onMessageListChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun interface MessageListChangedListener {
|
||||
fun onMessageListChanged()
|
||||
}
|
|
@ -46,7 +46,7 @@ public class EmailProvider extends ContentProvider {
|
|||
public static String AUTHORITY;
|
||||
public static Uri CONTENT_URI;
|
||||
|
||||
public static Uri getNotificationUri(String accountUuid) {
|
||||
private static Uri getNotificationUri(String accountUuid) {
|
||||
return Uri.withAppendedPath(CONTENT_URI, "account/" + accountUuid + "/messages");
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
package com.fsck.k9.mailstore
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
|
||||
private const val ACCOUNT_UUID = "00000000-0000-4000-0000-000000000000"
|
||||
|
||||
class MessageListRepositoryTest {
|
||||
private val messageListRepository = MessageListRepository()
|
||||
|
||||
@Test
|
||||
fun `adding and removing listener`() {
|
||||
var messageListChanged = 0
|
||||
val listener = MessageListChangedListener {
|
||||
messageListChanged++
|
||||
}
|
||||
messageListRepository.addListener(ACCOUNT_UUID, listener)
|
||||
|
||||
messageListRepository.notifyMessageListChanged(ACCOUNT_UUID)
|
||||
|
||||
assertThat(messageListChanged).isEqualTo(1)
|
||||
|
||||
messageListRepository.removeListener(listener)
|
||||
|
||||
messageListRepository.notifyMessageListChanged(ACCOUNT_UUID)
|
||||
|
||||
assertThat(messageListChanged).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `only notify listener when account UUID matches`() {
|
||||
var messageListChanged = 0
|
||||
val listener = MessageListChangedListener {
|
||||
messageListChanged++
|
||||
}
|
||||
messageListRepository.addListener(ACCOUNT_UUID, listener)
|
||||
|
||||
messageListRepository.notifyMessageListChanged("otherAccountUuid")
|
||||
|
||||
assertThat(messageListChanged).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `notifyMessageListChanged() without any listeners should not throw`() {
|
||||
messageListRepository.notifyMessageListChanged(ACCOUNT_UUID)
|
||||
}
|
||||
}
|
|
@ -1,17 +1,14 @@
|
|||
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.controller.MessageCounts
|
||||
import com.fsck.k9.controller.MessageCountsProvider
|
||||
import com.fsck.k9.mailstore.MessageListChangedListener
|
||||
import com.fsck.k9.mailstore.MessageListRepository
|
||||
import com.fsck.k9.preferences.AccountManager
|
||||
import com.fsck.k9.provider.EmailProvider
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
|
@ -26,7 +23,7 @@ import kotlinx.coroutines.launch
|
|||
class AccountsViewModel(
|
||||
accountManager: AccountManager,
|
||||
private val messageCountsProvider: MessageCountsProvider,
|
||||
private val contentResolver: ContentResolver
|
||||
private val messageListRepository: MessageListRepository
|
||||
) : ViewModel() {
|
||||
private val displayAccountFlow: Flow<List<DisplayAccount>> = accountManager.getAccountsFlow()
|
||||
.flatMapLatest { accounts ->
|
||||
|
@ -47,21 +44,17 @@ class AccountsViewModel(
|
|||
|
||||
private fun getMessageCountsFlow(account: Account): Flow<MessageCounts> {
|
||||
return callbackFlow {
|
||||
val notificationUri = EmailProvider.getNotificationUri(account.uuid)
|
||||
|
||||
send(messageCountsProvider.getMessageCounts(account))
|
||||
|
||||
val contentObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
launch {
|
||||
send(messageCountsProvider.getMessageCounts(account))
|
||||
}
|
||||
val listener = MessageListChangedListener {
|
||||
launch {
|
||||
send(messageCountsProvider.getMessageCounts(account))
|
||||
}
|
||||
}
|
||||
contentResolver.registerContentObserver(notificationUri, false, contentObserver)
|
||||
messageListRepository.addListener(account.uuid, listener)
|
||||
|
||||
awaitClose {
|
||||
contentResolver.unregisterContentObserver(contentObserver)
|
||||
messageListRepository.removeListener(listener)
|
||||
}
|
||||
}.flowOn(Dispatchers.IO)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,9 @@ import org.koin.androidx.viewmodel.dsl.viewModel
|
|||
import org.koin.dsl.module
|
||||
|
||||
val accountUiModule = module {
|
||||
viewModel { AccountsViewModel(accountManager = get(), messageCountsProvider = get(), contentResolver = get()) }
|
||||
viewModel {
|
||||
AccountsViewModel(accountManager = get(), messageCountsProvider = get(), messageListRepository = get())
|
||||
}
|
||||
factory { AccountImageLoader(accountFallbackImageProvider = get()) }
|
||||
factory { AccountFallbackImageProvider(context = get()) }
|
||||
factory { AccountImageModelLoaderFactory(contactPhotoLoader = get(), accountFallbackImageProvider = get()) }
|
||||
|
|
|
@ -8,5 +8,7 @@ val messageListUiModule = module {
|
|||
factory { DefaultFolderProvider() }
|
||||
factory { MessageListExtractor(get(), get()) }
|
||||
factory { MessageListLoader(get(), get(), get(), get()) }
|
||||
factory { MessageListLiveDataFactory(get(), get(), get()) }
|
||||
factory {
|
||||
MessageListLiveDataFactory(messageListLoader = get(), preferences = get(), messageListRepository = get())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package com.fsck.k9.ui.messagelist
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.database.ContentObserver
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.fsck.k9.Preferences
|
||||
import com.fsck.k9.provider.EmailProvider
|
||||
import com.fsck.k9.mailstore.MessageListChangedListener
|
||||
import com.fsck.k9.mailstore.MessageListRepository
|
||||
import com.fsck.k9.search.getAccountUuids
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -16,53 +13,43 @@ import kotlinx.coroutines.withContext
|
|||
class MessageListLiveData(
|
||||
private val messageListLoader: MessageListLoader,
|
||||
private val preferences: Preferences,
|
||||
private val contentResolver: ContentResolver,
|
||||
private val messageListRepository: MessageListRepository,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
val config: MessageListConfig
|
||||
) : LiveData<MessageListInfo>() {
|
||||
|
||||
private val contentObserver = object : ContentObserver(Handler()) {
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
loadMessageListAsync()
|
||||
}
|
||||
private val messageListChangedListener = MessageListChangedListener {
|
||||
loadMessageListAsync()
|
||||
}
|
||||
|
||||
private fun loadMessageListAsync() {
|
||||
coroutineScope.launch(Dispatchers.Main) {
|
||||
value = withContext(Dispatchers.IO) {
|
||||
val messageList = withContext(Dispatchers.IO) {
|
||||
messageListLoader.getMessageList(config)
|
||||
}
|
||||
value = messageList
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActive() {
|
||||
super.onActive()
|
||||
|
||||
registerContentObserverAsync()
|
||||
registerMessageListChangedListenerAsync()
|
||||
loadMessageListAsync()
|
||||
}
|
||||
|
||||
override fun onInactive() {
|
||||
super.onInactive()
|
||||
contentResolver.unregisterContentObserver(contentObserver)
|
||||
messageListRepository.removeListener(messageListChangedListener)
|
||||
}
|
||||
|
||||
private fun registerContentObserverAsync() {
|
||||
coroutineScope.launch(Dispatchers.Main) {
|
||||
val notificationUris = withContext(Dispatchers.IO) {
|
||||
getNotificationUris()
|
||||
}
|
||||
private fun registerMessageListChangedListenerAsync() {
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
val accountUuids = config.search.getAccountUuids(preferences)
|
||||
|
||||
for (notificationUri in notificationUris) {
|
||||
contentResolver.registerContentObserver(notificationUri, false, contentObserver)
|
||||
for (accountUuid in accountUuids) {
|
||||
messageListRepository.addListener(accountUuid, messageListChangedListener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNotificationUris(): List<Uri> {
|
||||
val accountUuids = config.search.getAccountUuids(preferences)
|
||||
return accountUuids.map { accountUuid ->
|
||||
EmailProvider.getNotificationUri(accountUuid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
package com.fsck.k9.ui.messagelist
|
||||
|
||||
import android.content.ContentResolver
|
||||
import com.fsck.k9.Preferences
|
||||
import com.fsck.k9.mailstore.MessageListRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
||||
class MessageListLiveDataFactory(
|
||||
private val messageListLoader: MessageListLoader,
|
||||
private val preferences: Preferences,
|
||||
private val contentResolver: ContentResolver
|
||||
private val messageListRepository: MessageListRepository
|
||||
) {
|
||||
fun create(coroutineScope: CoroutineScope, config: MessageListConfig): MessageListLiveData {
|
||||
return MessageListLiveData(messageListLoader, preferences, contentResolver, coroutineScope, config)
|
||||
return MessageListLiveData(messageListLoader, preferences, messageListRepository, coroutineScope, config)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue