Add support for SpecialFolderSelection to FolderListPreference

This commit is contained in:
cketti 2018-11-12 18:28:59 +01:00
parent a519c91b64
commit 5e00abfaac
9 changed files with 158 additions and 55 deletions

View file

@ -3,20 +3,37 @@ package com.fsck.k9.mailstore
import com.fsck.k9.Account
import com.fsck.k9.Account.FolderMode
import com.fsck.k9.mail.Folder.FolderClass
import com.fsck.k9.mail.Folder.FolderType as RemoteFolderType
class FolderRepository(private val account: Account) {
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 { it.isInTopGroup }
.thenBy(String.CASE_INSENSITIVE_ORDER) { it.name }
fun getRemoteFolders(): List<Folder> {
fun getRemoteFolderInfo(): RemoteFolderInfo {
val folders = getRemoteFolders()
val automaticSpecialFolders = mapOf(
FolderType.ARCHIVE to specialFolderSelectionStrategy.selectSpecialFolder(folders, FolderType.ARCHIVE),
FolderType.DRAFTS to specialFolderSelectionStrategy.selectSpecialFolder(folders, FolderType.DRAFTS),
FolderType.SENT to specialFolderSelectionStrategy.selectSpecialFolder(folders, FolderType.SENT),
FolderType.SPAM to specialFolderSelectionStrategy.selectSpecialFolder(folders, FolderType.SPAM),
FolderType.TRASH to specialFolderSelectionStrategy.selectSpecialFolder(folders, FolderType.TRASH)
)
return RemoteFolderInfo(folders, automaticSpecialFolders)
}
private fun getRemoteFolders(): List<Folder> {
val folders = account.localStore.getPersonalNamespaces(false)
return folders
.filterNot { it.isLocalOnly }
.map(::createFolderFromLocalFolder)
.map { Folder(it.databaseId, it.serverId, it.name, it.type.toFolderType()) }
}
fun getDisplayFolders(): List<Folder> {
@ -55,10 +72,23 @@ class FolderRepository(private val account: Account) {
account.spamFolder -> FolderType.SPAM
else -> FolderType.REGULAR
}
private fun RemoteFolderType.toFolderType(): FolderType = when (this) {
RemoteFolderType.REGULAR -> FolderType.REGULAR
RemoteFolderType.INBOX -> FolderType.INBOX
RemoteFolderType.OUTBOX -> FolderType.REGULAR // We currently don't support remote Outbox folders
RemoteFolderType.DRAFTS -> FolderType.DRAFTS
RemoteFolderType.SENT -> FolderType.SENT
RemoteFolderType.TRASH -> FolderType.TRASH
RemoteFolderType.SPAM -> FolderType.SPAM
RemoteFolderType.ARCHIVE -> FolderType.ARCHIVE
}
}
data class Folder(val id: Long, val serverId: String, val name: String, val type: FolderType)
data class RemoteFolderInfo(val folders: List<Folder>, val automaticSpecialFolders: Map<FolderType, Folder?>)
enum class FolderType {
REGULAR,
INBOX,

View file

@ -2,6 +2,6 @@ package com.fsck.k9.mailstore
import com.fsck.k9.Account
class FolderRepositoryManager {
fun getFolderRepository(account: Account) = FolderRepository(account)
class FolderRepositoryManager(private val specialFolderSelectionStrategy: SpecialFolderSelectionStrategy) {
fun getFolderRepository(account: Account) = FolderRepository(specialFolderSelectionStrategy, account)
}

View file

@ -3,8 +3,9 @@ package com.fsck.k9.mailstore
import org.koin.dsl.module.applicationContext
val mailStoreModule = applicationContext {
bean { FolderRepositoryManager() }
bean { FolderRepositoryManager(get()) }
bean { MessageViewInfoExtractor(get(), get(), get()) }
bean { StorageManager.getInstance(get()) }
bean { SearchStatusManager() }
bean { SpecialFolderSelectionStrategy() }
}

View file

@ -0,0 +1,10 @@
package com.fsck.k9.mailstore
/**
* Implements the automatic special folder selection strategy.
*/
class SpecialFolderSelectionStrategy {
fun selectSpecialFolder(folders: List<Folder>, type: FolderType): Folder? {
return folders.firstOrNull { folder -> folder.type == type }
}
}

View file

@ -3,9 +3,9 @@ package com.fsck.k9.ui.settings.account
import android.content.Context
import android.support.v7.preference.PreferenceDataStore
import com.fsck.k9.Account
import com.fsck.k9.Account.SpecialFolderSelection
import com.fsck.k9.Preferences
import com.fsck.k9.service.MailService
import com.fsck.k9.ui.settings.account.FolderListPreference.Companion.NO_FOLDER_SELECTED_VALUE
import java.util.concurrent.ExecutorService
class AccountSettingsDataStore(
@ -127,15 +127,17 @@ class AccountSettingsDataStore(
"message_format" -> account.messageFormat.name
"quote_style" -> account.quoteStyle.name
"account_quote_prefix" -> account.quotePrefix
"account_setup_auto_expand_folder" -> account.autoExpandFolder.toFolderPreferenceValue()
"account_setup_auto_expand_folder" -> {
loadSpecialFolder(account.autoExpandFolder, SpecialFolderSelection.MANUAL)
}
"folder_display_mode" -> account.folderDisplayMode.name
"folder_target_mode" -> account.folderTargetMode.name
"searchable_folders" -> account.searchableFolders.name
"archive_folder" -> account.archiveFolder.toFolderPreferenceValue()
"drafts_folder" -> account.draftsFolder.toFolderPreferenceValue()
"sent_folder" -> account.sentFolder.toFolderPreferenceValue()
"spam_folder" -> account.spamFolder.toFolderPreferenceValue()
"trash_folder" -> account.trashFolder.toFolderPreferenceValue()
"archive_folder" -> loadSpecialFolder(account.archiveFolder, account.archiveFolderSelection)
"drafts_folder" -> loadSpecialFolder(account.draftsFolder, account.draftsFolderSelection)
"sent_folder" -> loadSpecialFolder(account.sentFolder, account.sentFolderSelection)
"spam_folder" -> loadSpecialFolder(account.spamFolder, account.spamFolderSelection)
"trash_folder" -> loadSpecialFolder(account.trashFolder, account.trashFolderSelection)
"folder_notify_new_mail_mode" -> account.folderNotifyNewMailMode.name
"account_vibrate_pattern" -> account.notificationSetting.vibratePattern.toString()
"account_vibrate_times" -> account.notificationSetting.vibrateTimes.toString()
@ -177,15 +179,15 @@ class AccountSettingsDataStore(
"message_format" -> account.messageFormat = Account.MessageFormat.valueOf(value)
"quote_style" -> account.quoteStyle = Account.QuoteStyle.valueOf(value)
"account_quote_prefix" -> account.quotePrefix = value
"account_setup_auto_expand_folder" -> account.autoExpandFolder = value.toFolderStorageValue()
"account_setup_auto_expand_folder" -> account.autoExpandFolder = extractFolderName(value)
"folder_display_mode" -> account.folderDisplayMode = Account.FolderMode.valueOf(value)
"folder_target_mode" -> account.folderTargetMode = Account.FolderMode.valueOf(value)
"searchable_folders" -> account.searchableFolders = Account.Searchable.valueOf(value)
"archive_folder" -> account.archiveFolder = value.toFolderStorageValue()
"drafts_folder" -> account.draftsFolder = value.toFolderStorageValue()
"sent_folder" -> account.sentFolder = value.toFolderStorageValue()
"spam_folder" -> account.spamFolder = value.toFolderStorageValue()
"trash_folder" -> account.trashFolder = value.toFolderStorageValue()
"archive_folder" -> saveSpecialFolderSelection(value, account::setArchiveFolder)
"drafts_folder" -> saveSpecialFolderSelection(value, account::setDraftsFolder)
"sent_folder" -> saveSpecialFolderSelection(value, account::setSentFolder)
"spam_folder" -> saveSpecialFolderSelection(value, account::setSpamFolder)
"trash_folder" -> saveSpecialFolderSelection(value, account::setTrashFolder)
"folder_notify_new_mail_mode" -> account.folderNotifyNewMailMode = Account.FolderMode.valueOf(value)
"account_vibrate_pattern" -> account.notificationSetting.vibratePattern = value.toInt()
"account_vibrate_times" -> account.notificationSetting.vibrateTimes = value.toInt()
@ -225,7 +227,32 @@ class AccountSettingsDataStore(
MailService.actionRestartPushers(context, null)
}
private fun String.toFolderStorageValue() = if (this == NO_FOLDER_SELECTED_VALUE) null else this
private fun extractFolderName(preferenceValue: String): String? {
val folderValue = preferenceValue.substringAfter(FolderListPreference.FOLDER_VALUE_DELIMITER)
return if (folderValue == FolderListPreference.NO_FOLDER_VALUE) null else folderValue
}
private fun String?.toFolderPreferenceValue() = this ?: NO_FOLDER_SELECTED_VALUE
private fun saveSpecialFolderSelection(
preferenceValue: String,
specialFolderSetter: (String?, SpecialFolderSelection) -> Unit
) {
val specialFolder = extractFolderName(preferenceValue)
val specialFolderSelection = if (preferenceValue.startsWith(FolderListPreference.AUTOMATIC_PREFIX)) {
SpecialFolderSelection.AUTOMATIC
} else {
SpecialFolderSelection.MANUAL
}
specialFolderSetter(specialFolder, specialFolderSelection)
}
private fun loadSpecialFolder(specialFolder: String?, specialFolderSelection: SpecialFolderSelection): String {
val prefix = when (specialFolderSelection) {
SpecialFolderSelection.AUTOMATIC -> FolderListPreference.AUTOMATIC_PREFIX
SpecialFolderSelection.MANUAL -> FolderListPreference.MANUAL_PREFIX
}
return prefix + (specialFolder ?: FolderListPreference.NO_FOLDER_VALUE)
}
}

View file

@ -13,6 +13,9 @@ import com.fsck.k9.activity.setup.AccountSetupIncoming
import com.fsck.k9.activity.setup.AccountSetupOutgoing
import com.fsck.k9.controller.MessagingController
import com.fsck.k9.crypto.OpenPgpApiHelper
import com.fsck.k9.mailstore.Folder
import com.fsck.k9.mailstore.FolderType
import com.fsck.k9.mailstore.RemoteFolderInfo
import com.fsck.k9.mailstore.StorageManager
import com.fsck.k9.ui.endtoend.AutocryptKeyTransferActivity
import com.fsck.k9.ui.settings.onClick
@ -241,15 +244,30 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
}
private fun loadFolders(account: Account) {
viewModel.getFolders(account).observe(this@AccountSettingsFragment) { folders ->
if (folders != null) {
FOLDER_LIST_PREFERENCES.forEach {
(findPreference(it) as? FolderListPreference)?.folders = folders
}
viewModel.getFolders(account).observe(this@AccountSettingsFragment) { remoteFolderInfo ->
if (remoteFolderInfo != null) {
setFolders(PREFERENCE_AUTO_EXPAND_FOLDER, remoteFolderInfo.folders)
setFolders(PREFERENCE_ARCHIVE_FOLDER, remoteFolderInfo, FolderType.ARCHIVE)
setFolders(PREFERENCE_DRAFTS_FOLDER, remoteFolderInfo, FolderType.DRAFTS)
setFolders(PREFERENCE_SENT_FOLDER, remoteFolderInfo, FolderType.SENT)
setFolders(PREFERENCE_SPAM_FOLDER, remoteFolderInfo, FolderType.SPAM)
setFolders(PREFERENCE_TRASH_FOLDER, remoteFolderInfo, FolderType.TRASH)
}
}
}
private fun setFolders(preferenceKey: String, folders: List<Folder>) {
val folderListPreference = findPreference(preferenceKey) as? FolderListPreference ?: return
folderListPreference.setFolders(folders)
}
private fun setFolders(preferenceKey: String, remoteFolderInfo: RemoteFolderInfo, type: FolderType?) {
val folderListPreference = findPreference(preferenceKey) as? FolderListPreference ?: return
val automaticFolder = remoteFolderInfo.automaticSpecialFolders[type]
folderListPreference.setFolders(remoteFolderInfo.folders, automaticFolder)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val openPgpKeyPreference = findPreference(PREFERENCE_OPENPGP_KEY) as? OpenPgpKeyPreference
if (openPgpKeyPreference?.handleOnActivityResult(requestCode, resultCode, data) == true) {
@ -291,15 +309,6 @@ class AccountSettingsFragment : PreferenceFragmentCompat() {
private const val PREFERENCE_TRASH_FOLDER = "trash_folder"
private const val DELETE_POLICY_MARK_AS_READ = "MARK_AS_READ"
private val FOLDER_LIST_PREFERENCES = listOf(
PREFERENCE_AUTO_EXPAND_FOLDER,
PREFERENCE_ARCHIVE_FOLDER,
PREFERENCE_DRAFTS_FOLDER,
PREFERENCE_SENT_FOLDER,
PREFERENCE_SPAM_FOLDER,
PREFERENCE_TRASH_FOLDER
)
fun create(accountUuid: String, rootKey: String?) = AccountSettingsFragment().withArguments(
ARG_ACCOUNT_UUID to accountUuid,
PreferenceFragmentCompat.ARG_PREFERENCE_ROOT to rootKey)

View file

@ -5,8 +5,8 @@ import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel
import com.fsck.k9.Account
import com.fsck.k9.Preferences
import com.fsck.k9.mailstore.Folder
import com.fsck.k9.mailstore.FolderRepositoryManager
import com.fsck.k9.mailstore.RemoteFolderInfo
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.launch
import org.jetbrains.anko.coroutines.experimental.bg
@ -16,7 +16,7 @@ class AccountSettingsViewModel(
private val folderRepositoryManager: FolderRepositoryManager
) : ViewModel() {
private val accountLiveData = MutableLiveData<Account>()
private val foldersLiveData = MutableLiveData<List<Folder>>()
private val foldersLiveData = MutableLiveData<RemoteFolderInfo>()
fun getAccount(accountUuid: String): LiveData<Account> {
if (accountLiveData.value == null) {
@ -44,7 +44,7 @@ class AccountSettingsViewModel(
private fun loadAccount(accountUuid: String) = preferences.getAccount(accountUuid)
fun getFolders(account: Account): LiveData<List<Folder>> {
fun getFolders(account: Account): LiveData<RemoteFolderInfo> {
if (foldersLiveData.value == null) {
loadFolders(account)
}
@ -55,11 +55,11 @@ class AccountSettingsViewModel(
private fun loadFolders(account: Account) {
val folderRepository = folderRepositoryManager.getFolderRepository(account)
launch(UI) {
val folders = bg {
folderRepository.getRemoteFolders()
val remoteFolderInfo = bg {
folderRepository.getRemoteFolderInfo()
}.await()
foldersLiveData.value = folders
foldersLiveData.value = remoteFolderInfo
}
}
}

View file

@ -28,19 +28,8 @@ constructor(
defStyleRes: Int = 0
) : ListPreference(context, attrs, defStyleAttr, defStyleRes), KoinComponent {
private val folderNameFormatter: FolderNameFormatter by inject()
private val noFolderSelectedName = SpannableString(context.getString(R.string.account_settings_no_folder_selected))
.apply { setSpan(StyleSpan(Typeface.ITALIC), 0, this.length, 0) }
var folders: List<Folder>
get() = throw UnsupportedOperationException()
set(folders) {
entries = (listOf(noFolderSelectedName) + folders.map { folderNameFormatter.displayName(it) }).toTypedArray()
entryValues = (listOf(NO_FOLDER_SELECTED_VALUE) + folders.map { it.serverId }).toTypedArray()
isEnabled = true
}
private val noFolderSelectedName = context.getString(R.string.account_settings_no_folder_selected).italicize()
private lateinit var automaticFolderOption: CharSequence
init {
entries = emptyArray()
@ -48,6 +37,30 @@ constructor(
isEnabled = false
}
fun setFolders(folders: List<Folder>) {
entries = (listOf(noFolderSelectedName) + getFolderDisplayNames(folders)).toTypedArray()
entryValues = (listOf(NO_FOLDER_SELECTED_VALUE) + getFolderValues(folders)).toTypedArray()
isEnabled = true
}
fun setFolders(folders: List<Folder>, automaticFolder: Folder?) {
val automaticFolderName = if (automaticFolder != null) {
folderNameFormatter.displayName(automaticFolder)
} else {
context.getString(R.string.account_settings_no_folder_selected)
}
val automaticFolderValue = AUTOMATIC_PREFIX + (automaticFolder?.serverId ?: NO_FOLDER_VALUE)
automaticFolderOption = context.getString(R.string.account_settings_automatic_special_folder,
automaticFolderName).italicize()
entries = (listOf(automaticFolderOption) + noFolderSelectedName + getFolderDisplayNames(folders)).toTypedArray()
entryValues = (listOf(automaticFolderValue) + NO_FOLDER_SELECTED_VALUE + getFolderValues(folders)).toTypedArray()
isEnabled = true
}
override fun getSummary(): CharSequence {
// While folders are being loaded the summary returned by ListPreference will be empty. This leads to the
// summary view being hidden. Once folders are loaded the summary updates and the list height changes. This
@ -55,13 +68,25 @@ constructor(
return when {
entries.isEmpty() -> PLACEHOLDER_SUMMARY
value == NO_FOLDER_SELECTED_VALUE -> noFolderSelectedName
value.startsWith(AUTOMATIC_PREFIX) -> automaticFolderOption
else -> super.getSummary()
}
}
private fun getFolderDisplayNames(folders: List<Folder>) = folders.map { folderNameFormatter.displayName(it) }
private fun getFolderValues(folders: List<Folder>) = folders.map { MANUAL_PREFIX + it.serverId }
private fun String.italicize(): CharSequence {
return SpannableString(this).apply { setSpan(StyleSpan(Typeface.ITALIC), 0, this.length, 0) }
}
companion object {
const val NO_FOLDER_SELECTED_VALUE = ""
const val FOLDER_VALUE_DELIMITER = "|"
const val AUTOMATIC_PREFIX = "AUTOMATIC|"
const val MANUAL_PREFIX = "MANUAL|"
const val NO_FOLDER_VALUE = ""
private const val NO_FOLDER_SELECTED_VALUE = MANUAL_PREFIX + NO_FOLDER_VALUE
private const val PLACEHOLDER_SUMMARY = " "
}
}

View file

@ -903,6 +903,7 @@ Please submit bug reports, contribute new features and ask questions at
<string name="account_settings_searchable_none">None</string>
<string name="account_settings_no_folder_selected">None</string>
<string name="account_settings_automatic_special_folder">Automatic (%s)</string>
<string name="font_size_settings_title">Font size</string>
<string name="font_size_settings_description">Configure font size</string>