Merge pull request #4649 from k9mail/folder_database_id
Make "manage folders" screens use database ID to refer to folders
This commit is contained in:
commit
308917e6fd
9 changed files with 206 additions and 53 deletions
|
@ -1,13 +1,17 @@
|
|||
package com.fsck.k9.mailstore
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import androidx.core.content.contentValuesOf
|
||||
import androidx.core.database.getStringOrNull
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.Account.FolderMode
|
||||
import com.fsck.k9.Preferences
|
||||
import com.fsck.k9.mail.FolderClass
|
||||
import com.fsck.k9.mail.FolderType as RemoteFolderType
|
||||
|
||||
class FolderRepository(
|
||||
private val localStoreProvider: LocalStoreProvider,
|
||||
private val preferences: Preferences,
|
||||
private val account: Account
|
||||
) {
|
||||
private val sortForDisplay =
|
||||
|
@ -35,6 +39,105 @@ class FolderRepository(
|
|||
return displayFolders.sortedWith(sortForDisplay)
|
||||
}
|
||||
|
||||
fun getFolderDetails(folderId: Long): FolderDetails? {
|
||||
val database = localStoreProvider.getInstance(account).database
|
||||
return database.execute(false) { db ->
|
||||
db.query(
|
||||
"folders",
|
||||
arrayOf(
|
||||
"server_id",
|
||||
"name",
|
||||
"top_group",
|
||||
"integrate",
|
||||
"poll_class",
|
||||
"display_class",
|
||||
"notify_class",
|
||||
"push_class"
|
||||
),
|
||||
"id = ?",
|
||||
arrayOf(folderId.toString()),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
).use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
val serverId = cursor.getString(0)
|
||||
FolderDetails(
|
||||
folder = Folder(
|
||||
id = folderId,
|
||||
serverId = serverId,
|
||||
name = cursor.getString(1),
|
||||
type = folderTypeOf(serverId)
|
||||
),
|
||||
isInTopGroup = cursor.getInt(2) == 1,
|
||||
isIntegrate = cursor.getInt(3) == 1,
|
||||
syncClass = cursor.getStringOrNull(4).toFolderClass(),
|
||||
displayClass = cursor.getStringOrNull(5).toFolderClass(),
|
||||
notifyClass = cursor.getStringOrNull(6).toFolderClass(),
|
||||
pushClass = cursor.getStringOrNull(7).toFolderClass()
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFolderDetails(folderDetails: FolderDetails) {
|
||||
val database = localStoreProvider.getInstance(account).database
|
||||
database.execute(false) { db ->
|
||||
val contentValues = contentValuesOf(
|
||||
"top_group" to folderDetails.isInTopGroup,
|
||||
"integrate" to folderDetails.isIntegrate,
|
||||
"poll_class" to folderDetails.syncClass.name,
|
||||
"display_class" to folderDetails.displayClass.name,
|
||||
"notify_class" to folderDetails.notifyClass.name,
|
||||
"push_class" to folderDetails.pushClass.name
|
||||
)
|
||||
db.update("folders", contentValues, "id = ?", arrayOf(folderDetails.folder.id.toString()))
|
||||
}
|
||||
|
||||
saveFolderDetailsToPreferences(folderDetails)
|
||||
}
|
||||
|
||||
private fun saveFolderDetailsToPreferences(folderDetails: FolderDetails) {
|
||||
val folder = folderDetails.folder
|
||||
val editor = preferences.createStorageEditor()
|
||||
|
||||
val id = "${account.uuid}:${folderDetails.folder.serverId}"
|
||||
|
||||
// There can be a lot of folders. For the defaults, let's not save prefs, saving space, except for INBOX.
|
||||
val inboxServerId = account.inboxFolder
|
||||
if (folderDetails.displayClass == FolderClass.NO_CLASS && folder.serverId != inboxServerId) {
|
||||
editor.remove("$id.displayMode")
|
||||
} else {
|
||||
editor.putString("$id.displayMode", folderDetails.displayClass.name)
|
||||
}
|
||||
|
||||
if (folderDetails.syncClass == FolderClass.INHERITED && folder.serverId != inboxServerId) {
|
||||
editor.remove("$id.syncMode")
|
||||
} else {
|
||||
editor.putString("$id.syncMode", folderDetails.syncClass.name)
|
||||
}
|
||||
|
||||
if (folderDetails.notifyClass == FolderClass.INHERITED && folder.serverId != inboxServerId) {
|
||||
editor.remove("$id.notifyMode")
|
||||
} else {
|
||||
editor.putString("$id.notifyMode", folderDetails.notifyClass.name)
|
||||
}
|
||||
|
||||
if (folderDetails.pushClass == FolderClass.SECOND_CLASS && folder.serverId != inboxServerId) {
|
||||
editor.remove("$id.pushMode")
|
||||
} else {
|
||||
editor.putString("$id.pushMode", folderDetails.pushClass.name)
|
||||
}
|
||||
|
||||
editor.putBoolean("$id.inTopGroup", folderDetails.isInTopGroup)
|
||||
editor.putBoolean("$id.integrate", folderDetails.isIntegrate)
|
||||
|
||||
editor.commit()
|
||||
}
|
||||
|
||||
private fun getDisplayFolders(db: SQLiteDatabase, displayMode: FolderMode): List<DisplayFolder> {
|
||||
val queryBuilder = StringBuilder("""
|
||||
SELECT f.id, f.server_id, f.name, f.top_group, (
|
||||
|
@ -114,6 +217,10 @@ class FolderRepository(
|
|||
RemoteFolderType.ARCHIVE -> FolderType.ARCHIVE
|
||||
}
|
||||
|
||||
private fun String?.toFolderClass(): FolderClass {
|
||||
return this?.let { FolderClass.valueOf(this) } ?: FolderClass.NO_CLASS
|
||||
}
|
||||
|
||||
fun setIncludeInUnifiedInbox(serverId: String, includeInUnifiedInbox: Boolean) {
|
||||
val localStore = localStoreProvider.getInstance(account)
|
||||
val folder = localStore.getFolder(serverId)
|
||||
|
@ -141,6 +248,16 @@ class FolderRepository(
|
|||
|
||||
data class Folder(val id: Long, val serverId: String, val name: String, val type: FolderType)
|
||||
|
||||
data class FolderDetails(
|
||||
val folder: Folder,
|
||||
val isInTopGroup: Boolean,
|
||||
val isIntegrate: Boolean,
|
||||
val syncClass: FolderClass,
|
||||
val displayClass: FolderClass,
|
||||
val notifyClass: FolderClass,
|
||||
val pushClass: FolderClass
|
||||
)
|
||||
|
||||
data class DisplayFolder(
|
||||
val folder: Folder,
|
||||
val isInTopGroup: Boolean,
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package com.fsck.k9.mailstore
|
||||
|
||||
import com.fsck.k9.Account
|
||||
import com.fsck.k9.Preferences
|
||||
|
||||
class FolderRepositoryManager(
|
||||
private val localStoreProvider: LocalStoreProvider,
|
||||
private val specialFolderSelectionStrategy: SpecialFolderSelectionStrategy
|
||||
private val preferences: Preferences
|
||||
) {
|
||||
fun getFolderRepository(account: Account) = FolderRepository(localStoreProvider, account)
|
||||
fun getFolderRepository(account: Account) = FolderRepository(localStoreProvider, preferences, account)
|
||||
}
|
||||
|
|
|
@ -376,6 +376,10 @@ public class LocalStore {
|
|||
return new LocalFolder(this, serverId);
|
||||
}
|
||||
|
||||
public LocalFolder getFolder(long folderId) {
|
||||
return new LocalFolder(this, folderId);
|
||||
}
|
||||
|
||||
public LocalFolder getFolder(String serverId, String name, FolderType type) {
|
||||
return new LocalFolder(this, serverId, name, type);
|
||||
}
|
||||
|
|
|
@ -8,12 +8,11 @@ import com.fsck.k9.ui.R
|
|||
import com.mikepenz.fastadapter.items.AbstractItem
|
||||
|
||||
class FolderListItem(
|
||||
override var identifier: Long,
|
||||
val folderId: Long,
|
||||
private val folderIconResource: Int,
|
||||
val displayName: String,
|
||||
val serverId: String
|
||||
val displayName: String
|
||||
) : AbstractItem<FolderListViewHolder>() {
|
||||
|
||||
override var identifier: Long = folderId
|
||||
override val layoutRes = R.layout.folder_list_item
|
||||
override val type: Int = 1
|
||||
|
||||
|
|
|
@ -2,13 +2,17 @@ package com.fsck.k9.ui.managefolders
|
|||
|
||||
import androidx.preference.PreferenceDataStore
|
||||
import com.fsck.k9.mail.FolderClass
|
||||
import com.fsck.k9.mailstore.LocalFolder
|
||||
import com.fsck.k9.mailstore.FolderDetails
|
||||
import com.fsck.k9.mailstore.FolderRepository
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class FolderSettingsDataStore(private val folder: LocalFolder) : PreferenceDataStore() {
|
||||
class FolderSettingsDataStore(
|
||||
private val folderRepository: FolderRepository,
|
||||
private var folder: FolderDetails
|
||||
) : PreferenceDataStore() {
|
||||
private val saveScope = CoroutineScope(GlobalScope.coroutineContext + Dispatchers.IO)
|
||||
|
||||
override fun getBoolean(key: String?, defValue: Boolean): Boolean {
|
||||
|
@ -21,8 +25,8 @@ class FolderSettingsDataStore(private val folder: LocalFolder) : PreferenceDataS
|
|||
|
||||
override fun putBoolean(key: String?, value: Boolean) {
|
||||
return when (key) {
|
||||
"folder_settings_in_top_group" -> updateFolder { isInTopGroup = value }
|
||||
"folder_settings_include_in_integrated_inbox" -> updateFolder { isIntegrate = value }
|
||||
"folder_settings_in_top_group" -> updateFolder(folder.copy(isInTopGroup = value))
|
||||
"folder_settings_include_in_integrated_inbox" -> updateFolder(folder.copy(isIntegrate = value))
|
||||
else -> error("Unknown key: $key")
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +34,8 @@ class FolderSettingsDataStore(private val folder: LocalFolder) : PreferenceDataS
|
|||
override fun getString(key: String?, defValue: String?): String? {
|
||||
return when (key) {
|
||||
"folder_settings_folder_display_mode" -> folder.displayClass.name
|
||||
"folder_settings_folder_sync_mode" -> folder.rawSyncClass.name
|
||||
"folder_settings_folder_notify_mode" -> folder.rawNotifyClass.name
|
||||
"folder_settings_folder_sync_mode" -> folder.syncClass.name
|
||||
"folder_settings_folder_notify_mode" -> folder.notifyClass.name
|
||||
else -> error("Unknown key: $key")
|
||||
}
|
||||
}
|
||||
|
@ -40,17 +44,23 @@ class FolderSettingsDataStore(private val folder: LocalFolder) : PreferenceDataS
|
|||
val newValue = requireNotNull(value) { "'value' can't be null" }
|
||||
|
||||
when (key) {
|
||||
"folder_settings_folder_display_mode" -> updateFolder { displayClass = FolderClass.valueOf(newValue) }
|
||||
"folder_settings_folder_sync_mode" -> updateFolder { syncClass = FolderClass.valueOf(newValue) }
|
||||
"folder_settings_folder_notify_mode" -> updateFolder { notifyClass = FolderClass.valueOf(newValue) }
|
||||
"folder_settings_folder_display_mode" -> {
|
||||
updateFolder(folder.copy(displayClass = FolderClass.valueOf(newValue)))
|
||||
}
|
||||
"folder_settings_folder_sync_mode" -> {
|
||||
updateFolder(folder.copy(syncClass = FolderClass.valueOf(newValue)))
|
||||
}
|
||||
"folder_settings_folder_notify_mode" -> {
|
||||
updateFolder(folder.copy(notifyClass = FolderClass.valueOf(newValue)))
|
||||
}
|
||||
else -> error("Unknown key: $key")
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFolder(block: LocalFolder.() -> Unit) {
|
||||
private fun updateFolder(newFolder: FolderDetails) {
|
||||
folder = newFolder
|
||||
saveScope.launch {
|
||||
block(folder)
|
||||
folder.save()
|
||||
folderRepository.updateFolderDetails(newFolder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.fsck.k9.ui.managefolders
|
|||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.Preference
|
||||
import com.fsck.k9.ui.R
|
||||
import com.fsck.k9.ui.folders.FolderNameFormatter
|
||||
|
@ -25,18 +26,28 @@ class FolderSettingsFragment : PreferenceFragmentCompat() {
|
|||
|
||||
val arguments = arguments ?: error("Arguments missing")
|
||||
val accountUuid = arguments.getString(EXTRA_ACCOUNT) ?: error("Missing argument '$EXTRA_ACCOUNT'")
|
||||
val folderServerId = arguments.getString(EXTRA_FOLDER_SERVER_ID)
|
||||
?: error("Missing argument '$EXTRA_FOLDER_SERVER_ID'")
|
||||
val folderId = arguments.getLong(EXTRA_FOLDER_ID)
|
||||
|
||||
viewModel.getFolderSettingsLiveData(accountUuid, folderServerId)
|
||||
.observeNotNull(viewLifecycleOwner) { folderSettings ->
|
||||
preferenceManager.preferenceDataStore = folderSettings.dataStore
|
||||
setPreferencesFromResource(R.xml.folder_settings_preferences, null)
|
||||
|
||||
setCategoryTitle(folderSettings)
|
||||
viewModel.getFolderSettingsLiveData(accountUuid, folderId)
|
||||
.observeNotNull(viewLifecycleOwner) { folderSettingsResult ->
|
||||
when (folderSettingsResult) {
|
||||
is FolderNotFound -> navigateBack()
|
||||
is FolderSettingsData -> initPreferences(folderSettingsResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigateBack() {
|
||||
findNavController().popBackStack()
|
||||
}
|
||||
|
||||
private fun initPreferences(folderSettings: FolderSettingsData) {
|
||||
preferenceManager.preferenceDataStore = folderSettings.dataStore
|
||||
setPreferencesFromResource(R.xml.folder_settings_preferences, null)
|
||||
|
||||
setCategoryTitle(folderSettings)
|
||||
}
|
||||
|
||||
private fun setCategoryTitle(folderSettings: FolderSettingsData) {
|
||||
val folderDisplayName = folderNameFormatter.displayName(folderSettings.folder)
|
||||
findPreference<Preference>(PREFERENCE_TOP_CATEGORY)!!.title = folderDisplayName
|
||||
|
@ -44,7 +55,7 @@ class FolderSettingsFragment : PreferenceFragmentCompat() {
|
|||
|
||||
companion object {
|
||||
const val EXTRA_ACCOUNT = "account"
|
||||
const val EXTRA_FOLDER_SERVER_ID = "folderServerId"
|
||||
const val EXTRA_FOLDER_ID = "folderId"
|
||||
|
||||
private const val PREFERENCE_TOP_CATEGORY = "folder_settings"
|
||||
}
|
||||
|
|
|
@ -8,34 +8,42 @@ import com.fsck.k9.Account
|
|||
import com.fsck.k9.Preferences
|
||||
import com.fsck.k9.activity.FolderInfoHolder
|
||||
import com.fsck.k9.mailstore.Folder
|
||||
import com.fsck.k9.mailstore.LocalFolder
|
||||
import com.fsck.k9.mailstore.LocalStoreProvider
|
||||
import com.fsck.k9.mailstore.FolderDetails
|
||||
import com.fsck.k9.mailstore.FolderRepository
|
||||
import com.fsck.k9.mailstore.FolderRepositoryManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
class FolderSettingsViewModel(
|
||||
private val preferences: Preferences,
|
||||
private val localStoreProvider: LocalStoreProvider
|
||||
private val folderRepositoryManager: FolderRepositoryManager
|
||||
) : ViewModel() {
|
||||
private var folderSettingsLiveData: LiveData<FolderSettingsData>? = null
|
||||
private var folderSettingsLiveData: LiveData<FolderSettingsResult>? = null
|
||||
|
||||
fun getFolderSettingsLiveData(accountUuid: String, folderServerId: String): LiveData<FolderSettingsData> {
|
||||
return folderSettingsLiveData ?: createFolderSettingsLiveData(accountUuid, folderServerId).also {
|
||||
fun getFolderSettingsLiveData(accountUuid: String, folderId: Long): LiveData<FolderSettingsResult> {
|
||||
return folderSettingsLiveData ?: createFolderSettingsLiveData(accountUuid, folderId).also {
|
||||
folderSettingsLiveData = it
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFolderSettingsLiveData(
|
||||
accountUuid: String,
|
||||
folderServerId: String
|
||||
): LiveData<FolderSettingsData> {
|
||||
folderId: Long
|
||||
): LiveData<FolderSettingsResult> {
|
||||
return liveData(context = viewModelScope.coroutineContext) {
|
||||
val account = loadAccount(accountUuid)
|
||||
val localFolder = loadLocalFolder(account, folderServerId)
|
||||
val folderRepository = folderRepositoryManager.getFolderRepository(account)
|
||||
val folderDetails = folderRepository.loadFolderDetails(folderId)
|
||||
if (folderDetails == null) {
|
||||
Timber.w("Folder with ID $folderId not found")
|
||||
emit(FolderNotFound)
|
||||
return@liveData
|
||||
}
|
||||
|
||||
val folderSettingsData = FolderSettingsData(
|
||||
folder = createFolderObject(account, folderServerId, localFolder),
|
||||
dataStore = FolderSettingsDataStore(localFolder)
|
||||
folder = createFolderObject(account, folderDetails.folder),
|
||||
dataStore = FolderSettingsDataStore(folderRepository, folderDetails)
|
||||
)
|
||||
emit(folderSettingsData)
|
||||
}
|
||||
|
@ -47,19 +55,23 @@ class FolderSettingsViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun loadLocalFolder(account: Account, folderServerId: String): LocalFolder {
|
||||
private suspend fun FolderRepository.loadFolderDetails(folderId: Long): FolderDetails? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val localStore = localStoreProvider.getInstance(account)
|
||||
val folder = localStore.getFolder(folderServerId)
|
||||
folder.open()
|
||||
folder
|
||||
getFolderDetails(folderId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFolderObject(account: Account, folderServerId: String, localFolder: LocalFolder): Folder {
|
||||
val folderType = FolderInfoHolder.getFolderType(account, folderServerId)
|
||||
return Folder(id = -1, serverId = folderServerId, name = localFolder.name, type = folderType)
|
||||
private fun createFolderObject(account: Account, folder: Folder): Folder {
|
||||
val folderType = FolderInfoHolder.getFolderType(account, folder.serverId)
|
||||
return Folder(
|
||||
id = folder.id,
|
||||
serverId = folder.serverId,
|
||||
name = folder.name,
|
||||
type = folderType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class FolderSettingsData(val folder: Folder, val dataStore: FolderSettingsDataStore)
|
||||
sealed class FolderSettingsResult
|
||||
object FolderNotFound : FolderSettingsResult()
|
||||
data class FolderSettingsData(val folder: Folder, val dataStore: FolderSettingsDataStore) : FolderSettingsResult()
|
||||
|
|
|
@ -5,5 +5,5 @@ import org.koin.dsl.module
|
|||
|
||||
val manageFoldersUiModule = module {
|
||||
viewModel { ManageFoldersViewModel(foldersLiveDataFactory = get()) }
|
||||
viewModel { FolderSettingsViewModel(preferences = get(), localStoreProvider = get()) }
|
||||
viewModel { FolderSettingsViewModel(preferences = get(), folderRepositoryManager = get()) }
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ class ManageFoldersFragment : Fragment() {
|
|||
val folderListAdapter = FastAdapter.with(itemAdapter).apply {
|
||||
setHasStableIds(true)
|
||||
onClickListener = { _, _, item: FolderListItem, _ ->
|
||||
openFolderSettings(item.serverId)
|
||||
openFolderSettings(item.folderId)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -79,18 +79,17 @@ class ManageFoldersFragment : Fragment() {
|
|||
val databaseId = displayFolder.folder.id
|
||||
val folderIconResource = folderIconProvider.getFolderIcon(displayFolder.folder.type)
|
||||
val displayName = folderNameFormatter.displayName(displayFolder.folder)
|
||||
val serverId = displayFolder.folder.serverId
|
||||
|
||||
FolderListItem(databaseId, folderIconResource, displayName, serverId)
|
||||
FolderListItem(databaseId, folderIconResource, displayName)
|
||||
}
|
||||
|
||||
itemAdapter.set(folderListItems)
|
||||
}
|
||||
|
||||
private fun openFolderSettings(folderServerId: String) {
|
||||
private fun openFolderSettings(folderId: Long) {
|
||||
val folderSettingsArguments = bundleOf(
|
||||
FolderSettingsFragment.EXTRA_ACCOUNT to account.uuid,
|
||||
FolderSettingsFragment.EXTRA_FOLDER_SERVER_ID to folderServerId
|
||||
FolderSettingsFragment.EXTRA_FOLDER_ID to folderId
|
||||
)
|
||||
findNavController().navigate(R.id.action_manageFoldersScreen_to_folderSettingsScreen, folderSettingsArguments)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue