Rewrite MessageCountsProvider to use MessageStore

This commit is contained in:
cketti 2023-01-27 17:52:45 +01:00
parent adabe43efe
commit ef583ba620
7 changed files with 135 additions and 88 deletions

View file

@ -31,7 +31,6 @@ val controllerModule = module {
DefaultMessageCountsProvider(
preferences = get(),
accountSearchConditions = get(),
localStoreProvider = get(),
messageStoreManager = get()
)
}

View file

@ -2,10 +2,9 @@ package com.fsck.k9.controller
import com.fsck.k9.Account
import com.fsck.k9.Preferences
import com.fsck.k9.mail.MessagingException
import com.fsck.k9.mailstore.LocalStoreProvider
import com.fsck.k9.mailstore.MessageStoreManager
import com.fsck.k9.search.AccountSearchConditions
import com.fsck.k9.search.ConditionsTreeNode
import com.fsck.k9.search.LocalSearch
import com.fsck.k9.search.SearchAccount
import com.fsck.k9.search.getAccounts
@ -22,22 +21,14 @@ data class MessageCounts(val unread: Int, val starred: Int)
internal class DefaultMessageCountsProvider(
private val preferences: Preferences,
private val accountSearchConditions: AccountSearchConditions,
private val localStoreProvider: LocalStoreProvider,
private val messageStoreManager: MessageStoreManager
) : MessageCountsProvider {
override fun getMessageCounts(account: Account): MessageCounts {
return try {
val localStore = localStoreProvider.getInstance(account)
val search = LocalSearch()
accountSearchConditions.excludeSpecialFolders(account, search)
accountSearchConditions.limitToDisplayableFolders(account, search)
val search = LocalSearch()
accountSearchConditions.excludeSpecialFolders(account, search)
accountSearchConditions.limitToDisplayableFolders(account, search)
localStore.getMessageCounts(search)
} catch (e: MessagingException) {
Timber.e(e, "Unable to getMessageCounts for account: %s", account)
MessageCounts(0, 0)
}
return getMessageCounts(account, search.conditions)
}
override fun getMessageCounts(searchAccount: SearchAccount): MessageCounts {
@ -47,7 +38,7 @@ internal class DefaultMessageCountsProvider(
var unreadCount = 0
var starredCount = 0
for (account in accounts) {
val accountMessageCount = getMessageCountsWithLocalSearch(account, search)
val accountMessageCount = getMessageCounts(account, search.conditions)
unreadCount += accountMessageCount.unread
starredCount += accountMessageCount.starred
}
@ -69,13 +60,16 @@ internal class DefaultMessageCountsProvider(
}
}
private fun getMessageCountsWithLocalSearch(account: Account, search: LocalSearch): MessageCounts {
private fun getMessageCounts(account: Account, conditions: ConditionsTreeNode): MessageCounts {
return try {
val localStore = localStoreProvider.getInstance(account)
localStore.getMessageCounts(search)
} catch (e: MessagingException) {
val messageStore = messageStoreManager.getMessageStore(account)
return MessageCounts(
unread = messageStore.getUnreadMessageCount(conditions),
starred = messageStore.getStarredMessageCount(conditions)
)
} catch (e: Exception) {
Timber.e(e, "Unable to getMessageCounts for account: %s", account)
MessageCounts(0, 0)
MessageCounts(unread = 0, starred = 0)
}
}
}

View file

@ -29,7 +29,6 @@ import androidx.core.database.CursorKt;
import com.fsck.k9.Account;
import com.fsck.k9.Clock;
import com.fsck.k9.DI;
import com.fsck.k9.controller.MessageCounts;
import com.fsck.k9.Preferences;
import com.fsck.k9.controller.MessagingControllerCommands.PendingCommand;
import com.fsck.k9.controller.PendingCommandSerializer;
@ -1005,72 +1004,6 @@ public class LocalStore {
return folderMap;
}
public int getUnreadMessageCount(LocalSearch search) throws MessagingException {
StringBuilder whereBuilder = new StringBuilder();
List<String> queryArgs = new ArrayList<>();
SqlQueryBuilder.buildWhereClause(search.getConditions(), whereBuilder, queryArgs);
String where = whereBuilder.toString();
final String[] selectionArgs = queryArgs.toArray(new String[queryArgs.size()]);
final String sqlQuery = "SELECT SUM(read=0) " +
"FROM messages " +
"JOIN folders ON (folders.id = messages.folder_id) " +
"WHERE (messages.empty = 0 AND messages.deleted = 0)" +
(!TextUtils.isEmpty(where) ? " AND (" + where + ")" : "");
return database.execute(false, new DbCallback<Integer>() {
@Override
public Integer doDbWork(SQLiteDatabase db) {
Cursor cursor = db.rawQuery(sqlQuery, selectionArgs);
try {
if (cursor.moveToFirst()) {
return cursor.getInt(0);
} else {
return 0;
}
} finally {
cursor.close();
}
}
});
}
private int getStarredMessageCount(LocalSearch search) throws MessagingException {
StringBuilder whereBuilder = new StringBuilder();
List<String> queryArgs = new ArrayList<>();
SqlQueryBuilder.buildWhereClause(search.getConditions(), whereBuilder, queryArgs);
String where = whereBuilder.toString();
final String[] selectionArgs = queryArgs.toArray(new String[queryArgs.size()]);
final String sqlQuery = "SELECT SUM(flagged=1) " +
"FROM messages " +
"JOIN folders ON (folders.id = messages.folder_id) " +
"WHERE (messages.empty = 0 AND messages.deleted = 0)" +
(!TextUtils.isEmpty(where) ? " AND (" + where + ")" : "");
return database.execute(false, new DbCallback<Integer>() {
@Override
public Integer doDbWork(SQLiteDatabase db) {
Cursor cursor = db.rawQuery(sqlQuery, selectionArgs);
try {
if (cursor.moveToFirst()) {
return cursor.getInt(0);
} else {
return 0;
}
} finally {
cursor.close();
}
}
});
}
public MessageCounts getMessageCounts(LocalSearch search) throws MessagingException {
return new MessageCounts(getUnreadMessageCount(search), getStarredMessageCount(search));
}
public List<NotificationMessage> getNotificationMessages() throws MessagingException {
return database.execute(false, db -> {
try (Cursor cursor = db.rawQuery(

View file

@ -5,6 +5,7 @@ import com.fsck.k9.mail.Flag
import com.fsck.k9.mail.FolderClass
import com.fsck.k9.mail.FolderType
import com.fsck.k9.mail.Header
import com.fsck.k9.search.ConditionsTreeNode
import java.util.Date
/**
@ -226,6 +227,16 @@ interface MessageStore {
*/
fun getUnreadMessageCount(folderId: Long): Int
/**
* Retrieve the number of unread messages matching [conditions].
*/
fun getUnreadMessageCount(conditions: ConditionsTreeNode): Int
/**
* Retrieve the number of starred messages matching [conditions].
*/
fun getStarredMessageCount(conditions: ConditionsTreeNode): Int
/**
* Update a folder's name and type.
*/

View file

@ -15,6 +15,7 @@ import com.fsck.k9.mailstore.MoreMessages
import com.fsck.k9.mailstore.SaveMessageData
import com.fsck.k9.mailstore.StorageManager
import com.fsck.k9.message.extractors.BasicPartInfoExtractor
import com.fsck.k9.search.ConditionsTreeNode
import java.util.Date
class K9MessageStore(
@ -180,6 +181,14 @@ class K9MessageStore(
return retrieveFolderOperations.getUnreadMessageCount(folderId)
}
override fun getUnreadMessageCount(conditions: ConditionsTreeNode): Int {
return retrieveFolderOperations.getUnreadMessageCount(conditions)
}
override fun getStarredMessageCount(conditions: ConditionsTreeNode): Int {
return retrieveFolderOperations.getStarredMessageCount(conditions)
}
override fun getSize(): Long {
return databaseOperations.getSize()
}

View file

@ -12,6 +12,8 @@ import com.fsck.k9.mailstore.FolderNotFoundException
import com.fsck.k9.mailstore.LockableDatabase
import com.fsck.k9.mailstore.MoreMessages
import com.fsck.k9.mailstore.toFolderType
import com.fsck.k9.search.ConditionsTreeNode
import com.fsck.k9.search.SqlQueryBuilder
internal class RetrieveFolderOperations(private val lockableDatabase: LockableDatabase) {
fun <T> getFolder(folderId: Long, mapper: FolderMapper<T>): T? {
@ -170,6 +172,37 @@ $displayModeSelection
}
}
fun getUnreadMessageCount(conditions: ConditionsTreeNode): Int {
return getMessageCount(condition = "messages.read = 0", conditions)
}
fun getStarredMessageCount(conditions: ConditionsTreeNode): Int {
return getMessageCount(condition = "messages.flagged = 1", conditions)
}
private fun getMessageCount(condition: String, extraConditions: ConditionsTreeNode): Int {
val whereBuilder = StringBuilder()
val queryArgs = mutableListOf<String>()
SqlQueryBuilder.buildWhereClause(extraConditions, whereBuilder, queryArgs)
val where = if (whereBuilder.isNotEmpty()) "AND ($whereBuilder)" else ""
val selectionArgs = queryArgs.toTypedArray()
val query =
"""
SELECT COUNT(messages.id)
FROM messages
JOIN folders ON (folders.id = messages.folder_id)
WHERE (messages.empty = 0 AND messages.deleted = 0 AND $condition) $where
"""
return lockableDatabase.execute(false) { db ->
db.rawQuery(query, selectionArgs).use { cursor ->
if (cursor.moveToFirst()) cursor.getInt(0) else 0
}
}
}
fun hasMoreMessages(folderId: Long): MoreMessages {
return getFolder(folderId) { it.moreMessages } ?: throw FolderNotFoundException(folderId)
}

View file

@ -6,6 +6,8 @@ import com.fsck.k9.mail.FolderType
import com.fsck.k9.mailstore.FolderNotFoundException
import com.fsck.k9.mailstore.MoreMessages
import com.fsck.k9.mailstore.toDatabaseFolderType
import com.fsck.k9.search.LocalSearch
import com.fsck.k9.search.SearchSpecification
import com.fsck.k9.storage.RobolectricTest
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.fail
@ -379,6 +381,68 @@ class RetrieveFolderOperationsTest : RobolectricTest() {
assertThat(result).isEqualTo(2)
}
@Test
fun `get unread message count with condition from empty folder`() {
sqliteDatabase.createFolder(integrate = true)
val result = retrieveFolderOperations.getUnreadMessageCount(unifiedInboxConditions)
assertThat(result).isEqualTo(0)
}
@Test
fun `get unread message count with condition from non-existent folder`() {
val result = retrieveFolderOperations.getUnreadMessageCount(unifiedInboxConditions)
assertThat(result).isEqualTo(0)
}
@Test
fun `get unread message count with condition from non-empty folder`() {
val folderId1 = sqliteDatabase.createFolder(integrate = true)
sqliteDatabase.createMessage(folderId = folderId1, read = false)
sqliteDatabase.createMessage(folderId = folderId1, read = false)
sqliteDatabase.createMessage(folderId = folderId1, read = true)
val folderId2 = sqliteDatabase.createFolder(integrate = true)
sqliteDatabase.createMessage(folderId = folderId2, read = false)
sqliteDatabase.createMessage(folderId = folderId2, read = true)
val result = retrieveFolderOperations.getUnreadMessageCount(unifiedInboxConditions)
assertThat(result).isEqualTo(3)
}
@Test
fun `get starred message count with condition from empty folder`() {
sqliteDatabase.createFolder(integrate = true)
val result = retrieveFolderOperations.getStarredMessageCount(unifiedInboxConditions)
assertThat(result).isEqualTo(0)
}
@Test
fun `get starred message count with condition from non-existent folder`() {
val result = retrieveFolderOperations.getStarredMessageCount(unifiedInboxConditions)
assertThat(result).isEqualTo(0)
}
@Test
fun `get starred message count with condition from non-empty folder`() {
val folderId1 = sqliteDatabase.createFolder(integrate = true)
sqliteDatabase.createMessage(folderId = folderId1, flagged = false)
sqliteDatabase.createMessage(folderId = folderId1, flagged = true)
val folderId2 = sqliteDatabase.createFolder(integrate = true)
sqliteDatabase.createMessage(folderId = folderId2, flagged = true)
sqliteDatabase.createMessage(folderId = folderId2, flagged = true)
sqliteDatabase.createMessage(folderId = folderId2, flagged = false)
val result = retrieveFolderOperations.getStarredMessageCount(unifiedInboxConditions)
assertThat(result).isEqualTo(3)
}
@Test
fun `get 'more messages' value from non-existent folder`() {
try {
@ -415,4 +479,8 @@ class RetrieveFolderOperationsTest : RobolectricTest() {
assertThat(result).isEqualTo(MoreMessages.TRUE)
}
private val unifiedInboxConditions = LocalSearch().apply {
and(SearchSpecification.SearchField.INTEGRATE, "1", SearchSpecification.Attribute.EQUALS)
}.conditions
}