diff --git a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/SortType.kt b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/SortType.kt index 22bba8b7dc6..e37b5c0b26c 100644 --- a/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/SortType.kt +++ b/core/android/account/src/main/kotlin/net/thunderbird/core/android/account/SortType.kt @@ -8,4 +8,5 @@ enum class SortType(val isDefaultAscending: Boolean) { SORT_UNREAD(true), SORT_FLAGGED(true), SORT_ATTACHMENT(true), + SORT_SIZE(false), } diff --git a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/DisplaySettings.kt b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/DisplaySettings.kt index 1bf3151cc14..250dbe085e0 100644 --- a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/DisplaySettings.kt +++ b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/DisplaySettings.kt @@ -18,6 +18,7 @@ const val DISPLAY_SETTINGS_DEFAULT_SHOULD_SHOW_SETUP_ARCHIVE_FOLDER_DIALOG = tru const val DISPLAY_SETTINGS_DEFAULT_IS_MESSAGE_LIST_SENDER_ABOVE_SUBJECT = false const val DISPLAY_SETTINGS_DEFAULT_IS_SHOW_CONTACT_NAME = false const val DISPLAY_SETTINGS_DEFAULT_IS_SHOW_CONTACT_PICTURE = true +const val DISPLAY_SETTINGS_DEFAULT_IS_SHOW_MESSAGE_SIZE = false const val DISPLAY_SETTINGS_DEFAULT_IS_CHANGE_CONTACT_NAME_COLOR = true const val DISPLAY_SETTINGS_DEFAULT_IS_COLORIZE_MISSING_CONTACT_PICTURE = false const val DISPLAY_SETTINGS_DEFAULT_IS_USE_BACKGROUND_AS_INDICATOR = false @@ -44,6 +45,7 @@ data class DisplaySettings( val isMessageListSenderAboveSubject: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_MESSAGE_LIST_SENDER_ABOVE_SUBJECT, val isShowContactName: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_SHOW_CONTACT_NAME, val isShowContactPicture: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_SHOW_CONTACT_PICTURE, + val isShowMessageSize: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_SHOW_MESSAGE_SIZE, val isChangeContactNameColor: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_CHANGE_CONTACT_NAME_COLOR, val isColorizeMissingContactPictures: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_COLORIZE_MISSING_CONTACT_PICTURE, val isUseBackgroundAsUnreadIndicator: Boolean = DISPLAY_SETTINGS_DEFAULT_IS_USE_BACKGROUND_AS_INDICATOR, diff --git a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/DisplaySettingsPreferenceManager.kt b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/DisplaySettingsPreferenceManager.kt index dadf2e30ee0..5f5d677ed3c 100644 --- a/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/DisplaySettingsPreferenceManager.kt +++ b/core/preference/api/src/commonMain/kotlin/net/thunderbird/core/preference/display/DisplaySettingsPreferenceManager.kt @@ -23,6 +23,7 @@ const val KEY_MESSAGE_VIEW_FIXED_WIDTH_FONT = "messageViewFixedWidthFont" const val KEY_AUTO_FIT_WIDTH = "autofitWidth" const val KEY_MESSAGE_LIST_SENDER_ABOVE_SUBJECT = "messageListSenderAboveSubject" const val KEY_SHOW_CONTACT_PICTURE = "showContactPicture" +const val KEY_SHOW_MESSAGE_SIZE = "showMessageSize" const val KEY_APP_LANGUAGE = "language" const val KEY_SPLIT_VIEW_MODE = "splitViewMode" diff --git a/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/display/DefaultDisplaySettingsPreferenceManager.kt b/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/display/DefaultDisplaySettingsPreferenceManager.kt index 8e2c26520f3..41578c4f584 100644 --- a/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/display/DefaultDisplaySettingsPreferenceManager.kt +++ b/core/preference/impl/src/commonMain/kotlin/net/thunderbird/core/preference/display/DefaultDisplaySettingsPreferenceManager.kt @@ -119,6 +119,10 @@ class DefaultDisplaySettingsPreferenceManager( KEY_SHOW_CONTACT_PICTURE, DISPLAY_SETTINGS_DEFAULT_IS_SHOW_CONTACT_PICTURE, ), + isShowMessageSize = storage.getBoolean( + KEY_SHOW_MESSAGE_SIZE, + DISPLAY_SETTINGS_DEFAULT_IS_SHOW_MESSAGE_SIZE, + ), appLanguage = storage.getStringOrDefault( KEY_APP_LANGUAGE, DISPLAY_SETTINGS_DEFAULT_APP_LANGUAGE, @@ -176,6 +180,10 @@ class DefaultDisplaySettingsPreferenceManager( KEY_SHOW_CONTACT_PICTURE, config.isShowContactPicture, ) + storageEditor.putBoolean( + KEY_SHOW_MESSAGE_SIZE, + config.isShowMessageSize, + ) storageEditor.putBoolean( KEY_USE_BACKGROUND_AS_UNREAD_INDICATOR, config.isUseBackgroundAsUnreadIndicator, diff --git a/feature/widget/message-list-glance/src/debug/kotlin/net/thunderbird/feature/widget/message/list/ui/MessageListWidgetContentPreview.kt b/feature/widget/message-list-glance/src/debug/kotlin/net/thunderbird/feature/widget/message/list/ui/MessageListWidgetContentPreview.kt index 0f54cd6b1ab..d056323a60f 100644 --- a/feature/widget/message-list-glance/src/debug/kotlin/net/thunderbird/feature/widget/message/list/ui/MessageListWidgetContentPreview.kt +++ b/feature/widget/message-list-glance/src/debug/kotlin/net/thunderbird/feature/widget/message/list/ui/MessageListWidgetContentPreview.kt @@ -38,6 +38,7 @@ private fun generateMessageListItems(): ImmutableList { preview = "Preview 1", color = Color.BLUE, isRead = false, + size = 1L, ), generateMessageListItem( displayName = "Bob", @@ -46,6 +47,7 @@ private fun generateMessageListItems(): ImmutableList { preview = "Preview 2", color = Color.RED, isRead = true, + size = 2L * 1024L, ), generateMessageListItem( displayName = "Charlie", @@ -54,6 +56,7 @@ private fun generateMessageListItems(): ImmutableList { preview = "Preview 3", color = Color.RED, isRead = false, + size = 3L * 1024L * 1024L, ), ) } @@ -65,6 +68,7 @@ private fun generateMessageListItem( preview: String, color: Int, isRead: Boolean, + size: Long, ): MessageListItem { return MessageListItem( displayName = displayName, @@ -82,5 +86,6 @@ private fun generateMessageListItem( sortInternalDate = 0, sortIsStarred = false, sortDatabaseId = 0, + sortSize = size, ) } diff --git a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItem.kt b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItem.kt index 5c6a485bd68..8370d26500d 100644 --- a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItem.kt +++ b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItem.kt @@ -19,4 +19,5 @@ internal data class MessageListItem( val sortInternalDate: Long, val sortIsStarred: Boolean, val sortDatabaseId: Long, + val sortSize: Long, ) diff --git a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItemMapper.kt b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItemMapper.kt index ec83978dd0b..820dc769083 100644 --- a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItemMapper.kt +++ b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListItemMapper.kt @@ -51,6 +51,7 @@ internal class MessageListItemMapper( sortInternalDate = message.internalDate, sortIsStarred = message.isStarred, sortDatabaseId = message.id, + sortSize = message.size, ) } diff --git a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListLoader.kt b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListLoader.kt index db4e8f226af..df76896fc4f 100644 --- a/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListLoader.kt +++ b/feature/widget/message-list-glance/src/main/kotlin/net/thunderbird/feature/widget/message/list/MessageListLoader.kt @@ -72,6 +72,7 @@ internal class MessageListLoader( SortType.SORT_SUBJECT -> "${MessageColumns.SUBJECT} COLLATE NOCASE" SortType.SORT_UNREAD -> MessageColumns.READ SortType.SORT_DATE -> MessageColumns.DATE + SortType.SORT_SIZE -> MessageColumns.SIZE } val sortDirection = if (config.sortAscending) " ASC" else " DESC" @@ -122,6 +123,11 @@ internal class MessageListLoader( compareBy(!config.sortAscending) { it.hasAttachments } .thenByDate(config) } + + SortType.SORT_SIZE -> { + compareBy(config.sortAscending) { it.sortSize } + .thenByDate(config) + } }.thenByDescending { it.sortDatabaseId } return this.sortedWith(comparator) diff --git a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItem.kt b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItem.kt index 10d18e99965..c52220ad450 100644 --- a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItem.kt +++ b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItem.kt @@ -19,4 +19,5 @@ internal data class MessageListItem( val sortInternalDate: Long, val sortIsStarred: Boolean, val sortDatabaseId: Long, + val sortSize: Long, ) diff --git a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItemMapper.kt b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItemMapper.kt index f432dfd2189..7afef24857b 100644 --- a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItemMapper.kt +++ b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListItemMapper.kt @@ -51,6 +51,7 @@ internal class MessageListItemMapper( sortInternalDate = message.internalDate, sortIsStarred = message.isStarred, sortDatabaseId = message.id, + sortSize = message.size, ) } diff --git a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListLoader.kt b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListLoader.kt index 578bed9cd7c..b85b0736c81 100644 --- a/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListLoader.kt +++ b/feature/widget/message-list/src/main/kotlin/app/k9mail/feature/widget/message/list/MessageListLoader.kt @@ -72,7 +72,7 @@ internal class MessageListLoader( SortType.SORT_SUBJECT -> "${MessageColumns.SUBJECT} COLLATE NOCASE" SortType.SORT_UNREAD -> MessageColumns.READ SortType.SORT_DATE -> MessageColumns.DATE - else -> MessageColumns.DATE + SortType.SORT_SIZE -> MessageColumns.SIZE } val sortDirection = if (config.sortAscending) " ASC" else " DESC" @@ -123,6 +123,11 @@ internal class MessageListLoader( compareBy(!config.sortAscending) { it.hasAttachments } .thenByDate(config) } + + SortType.SORT_SIZE -> { + compareBy(config.sortAscending) { it.sortSize } + .thenByDate(config) + } }.thenByDescending { it.sortDatabaseId } return this.sortedWith(comparator) diff --git a/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalMessage.java b/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalMessage.java index 30762662986..45763327481 100644 --- a/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalMessage.java +++ b/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalMessage.java @@ -43,6 +43,7 @@ public class LocalMessage extends MimeMessage { private boolean headerNeedsUpdating = false; private LocalFolder mFolder; private GeneralSettingsManager generalSettingsManager; + private long size; LocalMessage(LocalStore localStore, String uid, LocalFolder folder, GeneralSettingsManager generalSettingsManager) { this.localStore = localStore; @@ -137,6 +138,8 @@ void populateFromGetMessageCursor(Cursor cursor) throws MessagingException { Log.d("No headers available for this message!"); } + this.size = cursor.isNull(LocalStore.MSG_INDEX_SIZE) ? 0 : cursor.getLong(LocalStore.MSG_INDEX_SIZE); + headerNeedsUpdating = false; } @@ -453,4 +456,8 @@ public int hashCode() { private String getAccountUuid() { return getAccount().getUuid(); } + + public long getSize() { + return size; + } } diff --git a/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalStore.java b/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalStore.java index a6028e62dbc..fb6d167e2a7 100644 --- a/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalStore.java +++ b/legacy/core/src/main/java/com/fsck/k9/mailstore/LocalStore.java @@ -1,4 +1,3 @@ - package com.fsck.k9.mailstore; @@ -76,7 +75,7 @@ public class LocalStore { "subject, sender_list, date, uid, flags, messages.id, to_list, cc_list, " + "bcc_list, reply_to_list, attachment_count, internal_date, messages.message_id, " + "folder_id, preview, threads.id, threads.root, deleted, read, flagged, answered, " + - "forwarded, message_part_id, messages.mime_type, preview_type, header "; + "forwarded, message_part_id, messages.mime_type, preview_type, header, size "; static final int MSG_INDEX_SUBJECT = 0; static final int MSG_INDEX_SENDER_LIST = 1; @@ -104,9 +103,10 @@ public class LocalStore { static final int MSG_INDEX_MIME_TYPE = 23; static final int MSG_INDEX_PREVIEW_TYPE = 24; static final int MSG_INDEX_HEADER_DATA = 25; + static final int MSG_INDEX_SIZE = 26; - static final int MSG_INDEX_NOTIFICATION_ID = 26; - static final int MSG_INDEX_NOTIFICATION_TIMESTAMP = 27; + static final int MSG_INDEX_NOTIFICATION_ID = 27; + static final int MSG_INDEX_NOTIFICATION_TIMESTAMP = 28; static final String GET_FOLDER_COLS = "folders.id, name, visible_limit, last_updated, status, " + diff --git a/legacy/core/src/main/java/com/fsck/k9/mailstore/MessageColumns.kt b/legacy/core/src/main/java/com/fsck/k9/mailstore/MessageColumns.kt index a9af58a25aa..b36701976ab 100644 --- a/legacy/core/src/main/java/com/fsck/k9/mailstore/MessageColumns.kt +++ b/legacy/core/src/main/java/com/fsck/k9/mailstore/MessageColumns.kt @@ -21,4 +21,5 @@ object MessageColumns { const val FLAGGED = "flagged" const val ANSWERED = "answered" const val FORWARDED = "forwarded" + const val SIZE = "size" } diff --git a/legacy/core/src/main/java/com/fsck/k9/mailstore/SaveMessageDataCreator.kt b/legacy/core/src/main/java/com/fsck/k9/mailstore/SaveMessageDataCreator.kt index 8a3f3cb28ef..4dfd4d7a510 100644 --- a/legacy/core/src/main/java/com/fsck/k9/mailstore/SaveMessageDataCreator.kt +++ b/legacy/core/src/main/java/com/fsck/k9/mailstore/SaveMessageDataCreator.kt @@ -36,6 +36,7 @@ class SaveMessageDataCreator( previewResult = encryptionResult.previewResult, textForSearchIndex = encryptionResult.textForSearchIndex, encryptionType = encryptionResult.encryptionType, + size = message.size, ) } else { SaveMessageData( @@ -48,6 +49,7 @@ class SaveMessageDataCreator( previewResult = messagePreviewCreator.createPreview(message), textForSearchIndex = messageFulltextCreator.createFulltext(message), encryptionType = null, + size = message.size, ) } } diff --git a/legacy/core/src/test/java/com/fsck/k9/mailstore/MessageListRepositoryTest.kt b/legacy/core/src/test/java/com/fsck/k9/mailstore/MessageListRepositoryTest.kt index 3489cdb0b98..7d100071099 100644 --- a/legacy/core/src/test/java/com/fsck/k9/mailstore/MessageListRepositoryTest.kt +++ b/legacy/core/src/test/java/com/fsck/k9/mailstore/MessageListRepositoryTest.kt @@ -410,6 +410,7 @@ class MessageListRepositoryTest { override val hasAttachments = false override val threadRoot = message.threadRoot override val threadCount = 0 + override val size = 1024L }, ) } diff --git a/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/MessageMapper.kt b/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/MessageMapper.kt index 62ccc05834c..39ab4274b11 100644 --- a/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/MessageMapper.kt +++ b/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/MessageMapper.kt @@ -25,4 +25,5 @@ interface MessageDetailsAccessor { val hasAttachments: Boolean val threadRoot: Long val threadCount: Int + val size: Long } diff --git a/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/SaveMessageData.kt b/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/SaveMessageData.kt index baee428ec2e..2450b01978d 100644 --- a/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/SaveMessageData.kt +++ b/legacy/mailstore/src/main/java/app/k9mail/legacy/mailstore/SaveMessageData.kt @@ -14,4 +14,5 @@ data class SaveMessageData( val previewResult: PreviewResult, val textForSearchIndex: String? = null, val encryptionType: String?, + val size: Long? = null, ) diff --git a/legacy/storage/src/main/java/com/fsck/k9/storage/StoreSchemaDefinition.java b/legacy/storage/src/main/java/com/fsck/k9/storage/StoreSchemaDefinition.java index 3bab948f1a6..87194129a9b 100644 --- a/legacy/storage/src/main/java/com/fsck/k9/storage/StoreSchemaDefinition.java +++ b/legacy/storage/src/main/java/com/fsck/k9/storage/StoreSchemaDefinition.java @@ -12,7 +12,7 @@ class StoreSchemaDefinition implements SchemaDefinition { - static final int DB_VERSION = 88; + static final int DB_VERSION = 89; private final MigrationsHelper migrationsHelper; @@ -141,7 +141,8 @@ private static void dbCreateDatabaseFromScratch(SQLiteDatabase db) { "forwarded INTEGER default 0, " + "message_part_id INTEGER," + "encryption_type TEXT," + - "new_message INTEGER DEFAULT 0" + + "new_message INTEGER DEFAULT 0," + + "size INTEGER" + ")"); db.execSQL("DROP INDEX IF EXISTS new_messages"); diff --git a/legacy/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageListOperations.kt b/legacy/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageListOperations.kt index ac9560bcda5..724b22a7aed 100644 --- a/legacy/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageListOperations.kt +++ b/legacy/storage/src/main/java/com/fsck/k9/storage/messages/RetrieveMessageListOperations.kt @@ -37,7 +37,8 @@ SELECT answered, forwarded, attachment_count, - root + root, + size FROM messages JOIN threads ON (threads.message_id = messages.id) LEFT JOIN FOLDERS ON (folders.id = messages.folder_id) @@ -94,7 +95,8 @@ SELECT aggregated.forwarded AS forwarded, aggregated.attachment_count AS attachment_count, root, - aggregated.thread_count AS thread_count + aggregated.thread_count AS thread_count, + messages.size AS size FROM ( SELECT threads.root AS thread_root, @@ -166,7 +168,8 @@ SELECT answered, forwarded, attachment_count, - root + root, + size FROM threads JOIN messages ON (messages.id = threads.message_id) LEFT JOIN FOLDERS ON (folders.id = messages.folder_id) @@ -233,6 +236,17 @@ private class CursorMessageAccessor(val cursor: Cursor, val includesThreadCount: get() = cursor.getLong(16) override val threadCount: Int get() = if (includesThreadCount) cursor.getInt(17) else 0 + override val size: Long + get() = if (includesThreadCount) { + cursor.getLong(SIZE_COLUMN_INDEX_WITH_THREAD) + } else { + cursor.getLong(SIZE_COLUMN_INDEX_WITHOUT_THREAD) + } + + companion object { + private const val SIZE_COLUMN_INDEX_WITH_THREAD = 18 + private const val SIZE_COLUMN_INDEX_WITHOUT_THREAD = 17 + } } private val AGGREGATED_MESSAGES_COLUMNS = arrayOf( diff --git a/legacy/storage/src/main/java/com/fsck/k9/storage/messages/SaveMessageOperations.kt b/legacy/storage/src/main/java/com/fsck/k9/storage/messages/SaveMessageOperations.kt index 17623085cda..5f8f85d4bf3 100644 --- a/legacy/storage/src/main/java/com/fsck/k9/storage/messages/SaveMessageOperations.kt +++ b/legacy/storage/src/main/java/com/fsck/k9/storage/messages/SaveMessageOperations.kt @@ -411,6 +411,7 @@ internal class SaveMessageOperations( put("message_id", message.messageId) put("mime_type", message.mimeType) put("encryption_type", messageData.encryptionType) + put("size", messageData.size) val previewResult = messageData.previewResult put("preview_type", previewResult.previewType.toDatabaseValue()) diff --git a/legacy/storage/src/main/java/com/fsck/k9/storage/migrations/MigrationTo89.kt b/legacy/storage/src/main/java/com/fsck/k9/storage/migrations/MigrationTo89.kt new file mode 100644 index 00000000000..8556bf1a0a6 --- /dev/null +++ b/legacy/storage/src/main/java/com/fsck/k9/storage/migrations/MigrationTo89.kt @@ -0,0 +1,9 @@ +package com.fsck.k9.storage.migrations + +import android.database.sqlite.SQLiteDatabase + +internal class MigrationTo89(private val db: SQLiteDatabase) { + fun addMessageSizeColumn() { + db.execSQL("ALTER TABLE messages ADD size INTEGER") + } +} diff --git a/legacy/storage/src/main/java/com/fsck/k9/storage/migrations/Migrations.kt b/legacy/storage/src/main/java/com/fsck/k9/storage/migrations/Migrations.kt index 7b2958dae4a..3862b6d299b 100644 --- a/legacy/storage/src/main/java/com/fsck/k9/storage/migrations/Migrations.kt +++ b/legacy/storage/src/main/java/com/fsck/k9/storage/migrations/Migrations.kt @@ -35,5 +35,6 @@ object Migrations { if (oldVersion < 86) MigrationTo86(db, migrationsHelper).addFoldersPushEnabledColumn() if (oldVersion < 87) MigrationTo87(db, migrationsHelper).addFoldersSyncEnabledColumn() if (oldVersion < 88) MigrationTo88(db, migrationsHelper).addFoldersVisibleColumn() + if (oldVersion < 89) MigrationTo89(db).addMessageSizeColumn() } } diff --git a/legacy/storage/src/test/java/com/fsck/k9/storage/migrations/MigrationTo89Test.kt b/legacy/storage/src/test/java/com/fsck/k9/storage/migrations/MigrationTo89Test.kt new file mode 100644 index 00000000000..54693d827b2 --- /dev/null +++ b/legacy/storage/src/test/java/com/fsck/k9/storage/migrations/MigrationTo89Test.kt @@ -0,0 +1,79 @@ +package com.fsck.k9.storage.migrations + +import android.database.sqlite.SQLiteDatabase +import org.junit.After +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class MigrationTo89Test { + private val database = createDatabaseVersion88() + + @After + fun tearDown() { + database.close() + } + + @Test + fun `should add size column to messages table`() { + MigrationTo89(database).addMessageSizeColumn() + + assertColumnExists(database, "messages", "size") + } + + private fun createDatabaseVersion88(): SQLiteDatabase { + return SQLiteDatabase.create(null).apply { + execSQL( + "CREATE TABLE messages (" + + "id INTEGER PRIMARY KEY, " + + "deleted INTEGER default 0, " + + "folder_id INTEGER, " + + "uid TEXT, " + + "subject TEXT, " + + "date INTEGER, " + + "flags TEXT, " + + "sender_list TEXT, " + + "to_list TEXT, " + + "cc_list TEXT, " + + "bcc_list TEXT, " + + "reply_to_list TEXT, " + + "attachment_count INTEGER, " + + "internal_date INTEGER, " + + "message_id TEXT, " + + "preview_type TEXT default \"none\", " + + "preview TEXT, " + + "mime_type TEXT, " + + "normalized_subject_hash INTEGER, " + + "empty INTEGER default 0, " + + "read INTEGER default 0, " + + "flagged INTEGER default 0, " + + "answered INTEGER default 0, " + + "forwarded INTEGER default 0, " + + "message_part_id INTEGER," + + "encryption_type TEXT," + + "new_message INTEGER DEFAULT 0" + + ")", + ) + } + } + + private fun assertColumnExists(db: SQLiteDatabase, tableName: String, columnName: String) { + val cursor = db.rawQuery("PRAGMA table_info($tableName)", null) + cursor.use { + val columnIndex = cursor.getColumnIndex("name") + assertTrue("Column index should be valid", columnIndex >= 0) + + var columnExists = false + while (cursor.moveToNext()) { + val currentColumnName = cursor.getString(columnIndex) + if (currentColumnName == columnName) { + columnExists = true + break + } + } + assertTrue("Column '$columnName' should exist in table '$tableName'", columnExists) + } + } +} diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAppearance.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAppearance.kt index 9958682d09c..90dc469ce9a 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAppearance.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListAppearance.kt @@ -9,6 +9,7 @@ data class MessageListAppearance( val stars: Boolean, val senderAboveSubject: Boolean, val showContactPicture: Boolean, + val showMessageSize: Boolean, val showingThreadedList: Boolean, val backGroundAsReadIndicator: Boolean, val showAccountChip: Boolean, diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt index b485a3d04a6..7904e5c4c51 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListFragment.kt @@ -212,6 +212,8 @@ class MessageListFragment : val isShowAccountChip: Boolean get() = isUnifiedInbox || !isSingleAccountMode + private var previousAppearance: MessageListAppearance? = null + override fun onAttach(context: Context) { super.onAttach(context) @@ -701,6 +703,7 @@ class MessageListFragment : stars = !isOutbox && generalSettingsManager.getConfig().display.isShowMessageListStars, senderAboveSubject = generalSettingsManager.getConfig().display.isMessageListSenderAboveSubject, showContactPicture = generalSettingsManager.getConfig().display.isShowContactPicture, + showMessageSize = generalSettingsManager.getConfig().display.isShowMessageSize, showingThreadedList = showingThreadedList, backGroundAsReadIndicator = generalSettingsManager.getConfig().display.isUseBackgroundAsUnreadIndicator, showAccountChip = isShowAccountChip, @@ -721,6 +724,25 @@ class MessageListFragment : messagingController.addListener(activityListener) + // Recreate adapter if appearance settings have changed since last resume. + val currentAppearance = messageListAppearance + if (previousAppearance != null && previousAppearance != currentAppearance) { + // Preserve current adapter state. + val currentMessages = adapter.messages + val currentActiveMessage = adapter.activeMessage + val currentSelected = adapter.selected + + // Recreate adapter with new appearance settings. + adapter = createMessageListAdapter() + recyclerView?.adapter = adapter + + // Restore adapter state. + adapter.messages = currentMessages + adapter.activeMessage = currentActiveMessage + adapter.restoreSelected(currentSelected) + } + previousAppearance = currentAppearance + updateTitle() } @@ -987,6 +1009,7 @@ class MessageListFragment : R.id.set_sort_flag -> changeSort(SortType.SORT_FLAGGED) R.id.set_sort_unread -> changeSort(SortType.SORT_UNREAD) R.id.set_sort_attach -> changeSort(SortType.SORT_ATTACHMENT) + R.id.set_sort_size -> changeSort(SortType.SORT_SIZE) R.id.select_all -> selectAll() R.id.mark_all_as_read -> confirmMarkAllAsRead() R.id.send_messages -> onSendPendingMessages() diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItem.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItem.kt index e961574b734..504b25ec523 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItem.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItem.kt @@ -24,6 +24,7 @@ data class MessageListItem( val messageUid: String, val databaseId: Long, val threadRoot: Long, + val size: Long, ) { val messageReference: MessageReference get() = MessageReference(account.uuid, folderId, messageUid) diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItemMapper.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItemMapper.kt index c4e1ef43d42..d05edd2269a 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItemMapper.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListItemMapper.kt @@ -53,6 +53,7 @@ class MessageListItemMapper( message.messageServerId, message.id, message.threadRoot, + message.size, ) } diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLoader.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLoader.kt index 58b90b66f86..b9e9f57d6ce 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLoader.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/MessageListLoader.kt @@ -113,6 +113,7 @@ class MessageListLoader( SortType.SORT_SUBJECT -> "${MessageColumns.SUBJECT} COLLATE NOCASE" SortType.SORT_UNREAD -> MessageColumns.READ SortType.SORT_DATE -> MessageColumns.DATE + SortType.SORT_SIZE -> MessageColumns.SIZE } val sortDirection = if (config.sortAscending) " ASC" else " DESC" @@ -165,6 +166,10 @@ class MessageListLoader( compareBy(!config.sortAscending) { it.hasAttachments } .thenByDate(config) } + SortType.SORT_SIZE -> { + compareBy(config.sortAscending) { it.size } + .thenByDate(config) + } }.thenByDescending { it.databaseId } return this.sortedWith(comparator) diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/SortTypeToastProvider.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/SortTypeToastProvider.kt index 7c9a34a2113..a0030fb4b3f 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/SortTypeToastProvider.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/SortTypeToastProvider.kt @@ -7,6 +7,7 @@ import net.thunderbird.core.android.account.SortType.SORT_ATTACHMENT import net.thunderbird.core.android.account.SortType.SORT_DATE import net.thunderbird.core.android.account.SortType.SORT_FLAGGED import net.thunderbird.core.android.account.SortType.SORT_SENDER +import net.thunderbird.core.android.account.SortType.SORT_SIZE import net.thunderbird.core.android.account.SortType.SORT_SUBJECT import net.thunderbird.core.android.account.SortType.SORT_UNREAD @@ -21,6 +22,7 @@ class SortTypeToastProvider { SORT_UNREAD -> R.string.sort_unread_first SORT_FLAGGED -> R.string.sort_flagged_first SORT_ATTACHMENT -> R.string.sort_attach_first + SORT_SIZE -> R.string.sort_smallest_first } } else { when (sortType) { @@ -31,6 +33,7 @@ class SortTypeToastProvider { SORT_UNREAD -> R.string.sort_unread_last SORT_FLAGGED -> R.string.sort_flagged_last SORT_ATTACHMENT -> R.string.sort_unattached_first + SORT_SIZE -> R.string.sort_largest_first } } } diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/item/MessageViewHolder.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/item/MessageViewHolder.kt index 3268841f42f..35a5d1071f4 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/item/MessageViewHolder.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/messagelist/item/MessageViewHolder.kt @@ -25,6 +25,7 @@ import com.fsck.k9.contacts.ContactPictureLoader import com.fsck.k9.mail.Address import com.fsck.k9.ui.R import com.fsck.k9.ui.helper.RelativeDateTimeFormatter +import com.fsck.k9.ui.helper.SizeFormatter import com.fsck.k9.ui.messagelist.MessageListAppearance import com.fsck.k9.ui.messagelist.MessageListItem import com.fsck.k9.ui.messagelist.MlfUtils @@ -32,6 +33,7 @@ import com.google.android.material.textview.MaterialTextView import java.util.Locale import kotlin.math.max +@Suppress("LongParameterList") class MessageViewHolder( view: View, private val appearance: MessageListAppearance, @@ -39,6 +41,7 @@ class MessageViewHolder( private val res: Resources, private val contactsPictureLoader: ContactPictureLoader, private val relativeDateTimeFormatter: RelativeDateTimeFormatter, + private val sizeFormatter: SizeFormatter, private val colors: MessageViewHolderColors, ) : MessageListViewHolder(view) { @@ -50,6 +53,7 @@ class MessageViewHolder( val subjectView: MaterialTextView = view.findViewById(R.id.subject) val previewView: MaterialTextView = view.findViewById(R.id.preview) val dateView: MaterialTextView = view.findViewById(R.id.date) + val sizeView: MaterialTextView = view.findViewById(R.id.size) val chipView: ImageView = view.findViewById(R.id.account_color_chip) val threadCountView: MaterialTextView = view.findViewById(R.id.thread_count) val starView: ImageView = view.findViewById(R.id.star) @@ -137,6 +141,16 @@ class MessageViewHolder( dateView.typeface = Typeface.create(dateView.typeface, maybeBoldTypeface) dateView.setTextColor(foregroundColor) dateView.text = displayDate + + if (appearance.showMessageSize && size > 0) { + sizeView.isVisible = true + sizeView.typeface = Typeface.create(sizeView.typeface, maybeBoldTypeface) + sizeView.setTextColor(foregroundColor) + sizeView.text = sizeFormatter.formatSize(size) + } else { + sizeView.isVisible = false + } + attachmentView.isVisible = hasAttachments attachmentView.setColorFilter(foregroundColor) @@ -283,6 +297,7 @@ class MessageViewHolder( res = res, contactsPictureLoader = contactsPictureLoader, relativeDateTimeFormatter = relativeDateTimeFormatter, + sizeFormatter = SizeFormatter(res), colors = colors, ) @@ -324,6 +339,7 @@ class MessageViewHolder( } fontSizes.setViewTextSize(holder.dateView, fontSizes.messageListDate) + fontSizes.setViewTextSize(holder.sizeView, fontSizes.messageListDate) } private fun applyDensityValue(holder: MessageViewHolder, density: UiDensity, res: Resources) { diff --git a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsDataStore.kt b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsDataStore.kt index d630edb024b..7c224510ba6 100644 --- a/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsDataStore.kt +++ b/legacy/ui/legacy/src/main/java/com/fsck/k9/ui/settings/general/GeneralSettingsDataStore.kt @@ -47,6 +47,9 @@ class GeneralSettingsDataStore( "messagelist_show_contact_picture" -> generalSettingsManager.getConfig() .display.isShowContactPicture + "messagelist_show_message_size" -> generalSettingsManager.getConfig() + .display.isShowMessageSize + "messagelist_colorize_missing_contact_pictures" -> generalSettingsManager.getConfig() .display.isColorizeMissingContactPictures @@ -95,6 +98,7 @@ class GeneralSettingsDataStore( "messagelist_show_contact_name" -> setIsShowContactName(isShowContactName = value) "messagelist_change_contact_name_color" -> setIsChangeContactNameColor(isChangeContactNameColor = value) "messagelist_show_contact_picture" -> setIsShowContactPicture(isShowContactPicture = value) + "messagelist_show_message_size" -> setIsShowMessageSize(isShowMessageSize = value) "messagelist_colorize_missing_contact_pictures" -> setIsColorizeMissingContactPictures( isColorizeMissingContactPictures = value, ) @@ -374,6 +378,13 @@ class GeneralSettingsDataStore( } } + private fun setIsShowMessageSize(isShowMessageSize: Boolean) { + skipSaveSettings = true + generalSettingsManager.update { settings -> + settings.copy(display = settings.display.copy(isShowMessageSize = isShowMessageSize)) + } + } + private fun setIsChangeContactNameColor(isChangeContactNameColor: Boolean) { skipSaveSettings = true generalSettingsManager.update { settings -> diff --git a/legacy/ui/legacy/src/main/res/layout/message_list_item.xml b/legacy/ui/legacy/src/main/res/layout/message_list_item.xml index 1f946ba3d4d..48f77a54178 100644 --- a/legacy/ui/legacy/src/main/res/layout/message_list_item.xml +++ b/legacy/ui/legacy/src/main/res/layout/message_list_item.xml @@ -131,11 +131,27 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="4dp" - app:layout_constraintEnd_toStartOf="@+id/date" + app:layout_constraintEnd_toStartOf="@+id/size" app:layout_constraintTop_toTopOf="@+id/top_guideline" app:srcCompat="@drawable/ic_attachment" /> + + + diff --git a/legacy/ui/legacy/src/main/res/values/strings.xml b/legacy/ui/legacy/src/main/res/values/strings.xml index 34a620b190d..5af5f26382a 100644 --- a/legacy/ui/legacy/src/main/res/values/strings.xml +++ b/legacy/ui/legacy/src/main/res/values/strings.xml @@ -593,6 +593,8 @@ Read messages first Messages with attachments first Messages without attachments first + Largest messages first + Smallest messages first Sort by… Date @@ -602,6 +604,7 @@ Star Read/unread Attachments + Size Remove Account @@ -829,6 +832,9 @@ Show contact pictures Show contact pictures in the message list + Show message sizes + Show message sizes in the message list + Mark all as read Colorize contact pictures diff --git a/legacy/ui/legacy/src/main/res/xml/general_settings.xml b/legacy/ui/legacy/src/main/res/xml/general_settings.xml index d5d1a07cd4b..1849eb1903b 100644 --- a/legacy/ui/legacy/src/main/res/xml/general_settings.xml +++ b/legacy/ui/legacy/src/main/res/xml/general_settings.xml @@ -292,6 +292,12 @@ android:title="@string/global_settings_colorize_missing_contact_pictures_label" /> + + .isVisible() = given { actual -> if (!actual.isVisible) {