Merge pull request #5213 from k9mail/settings_changed_callback

This commit is contained in:
cketti 2021-03-26 18:20:44 +01:00 committed by GitHub
commit af827bb1fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 104 additions and 103 deletions

View file

@ -2,13 +2,15 @@ package com.fsck.k9
import android.content.Context
import android.content.SharedPreferences
import android.os.AsyncTask
import com.fsck.k9.Account.SortType
import com.fsck.k9.core.BuildConfig
import com.fsck.k9.mail.K9MailLib
import com.fsck.k9.mailstore.LocalStore
import com.fsck.k9.preferences.Storage
import com.fsck.k9.preferences.StorageEditor
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import timber.log.Timber
import timber.log.Timber.DebugTree
@ -21,8 +23,6 @@ object K9 : EarlyInit {
@JvmField
val DEVELOPER_MODE = BuildConfig.DEBUG
private const val VERSION_MIGRATE_OPENPGP_TO_ACCOUNTS = 63
/**
* Name of the [SharedPreferences] file used to store the last known version of the
* accounts' databases.
@ -105,28 +105,6 @@ object K9 : EarlyInit {
if (cachedVersion >= LocalStore.getDbVersion()) {
setDatabasesUpToDate(false)
}
if (cachedVersion < VERSION_MIGRATE_OPENPGP_TO_ACCOUNTS) {
migrateOpenPgpGlobalToAccountSettings()
}
}
private fun migrateOpenPgpGlobalToAccountSettings() {
val storage = preferences.storage
val openPgpProvider = storage.getString("openPgpProvider", null)
val openPgpSupportSignOnly = storage.getBoolean("openPgpSupportSignOnly", false)
for (account in preferences.accounts) {
account.openPgpProvider = openPgpProvider
account.isOpenPgpHideSignOnly = !openPgpSupportSignOnly
preferences.saveAccount(account)
}
preferences.createStorageEditor()
.remove("openPgpProvider")
.remove("openPgpSupportSignOnly")
.commit()
}
@JvmStatic
@ -409,8 +387,7 @@ object K9 : EarlyInit {
isFixedMessageViewTheme = storage.getBoolean("fixedMessageViewTheme", true)
}
@JvmStatic
fun save(editor: StorageEditor) {
internal fun save(editor: StorageEditor) {
editor.putBoolean("enableDebugLogging", isDebugLoggingEnabled)
editor.putBoolean("enableSensitiveLogging", isSensitiveDebugLoggingEnabled)
editor.putEnum("backgroundOperations", backgroundOps)
@ -484,15 +461,9 @@ object K9 : EarlyInit {
@JvmStatic
fun saveSettingsAsync() {
object : AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg voids: Void): Void? {
val editor = preferences.createStorageEditor()
save(editor)
editor.commit()
return null
}
}.execute()
GlobalScope.launch(Dispatchers.IO) {
preferences.saveSettings()
}
}
private inline fun <reified T : Enum<T>> Storage.getEnum(key: String, defaultValue: T): T {

View file

@ -18,7 +18,6 @@ val mainModule = module {
context = get(),
storagePersister = get(),
localStoreProvider = get(),
localKeyStoreManager = get(),
accountPreferenceSerializer = get()
)
}

View file

@ -3,9 +3,7 @@ package com.fsck.k9
import android.content.Context
import androidx.annotation.GuardedBy
import androidx.annotation.RestrictTo
import com.fsck.k9.backend.BackendManager
import com.fsck.k9.mail.MessagingException
import com.fsck.k9.mailstore.LocalStore
import com.fsck.k9.mailstore.LocalStoreProvider
import com.fsck.k9.preferences.Storage
import com.fsck.k9.preferences.StorageEditor
@ -14,19 +12,14 @@ import java.util.HashMap
import java.util.LinkedList
import java.util.UUID
import java.util.concurrent.CopyOnWriteArrayList
import org.koin.core.KoinComponent
import org.koin.core.inject
import timber.log.Timber
class Preferences internal constructor(
private val context: Context,
private val storagePersister: StoragePersister,
private val localStoreProvider: LocalStoreProvider,
private val localKeyStoreManager: LocalKeyStoreManager,
private val accountPreferenceSerializer: AccountPreferenceSerializer
) : KoinComponent {
private val backendManager: BackendManager by inject()
) {
private val accountLock = Any()
@GuardedBy("accountLock")
@ -38,20 +31,13 @@ class Preferences internal constructor(
@GuardedBy("accountLock")
private var newAccount: Account? = null
private val accountsChangeListeners = CopyOnWriteArrayList<AccountsChangeListener>()
private val settingsChangeListeners = CopyOnWriteArrayList<SettingsChangeListener>()
val storage = Storage()
init {
val persistedStorageValues = storagePersister.loadValues()
storage.replaceAll(persistedStorageValues)
if (storage.isEmpty) {
Timber.i("Preferences storage is zero-size, importing from Android-style preferences")
val editor = createStorageEditor()
editor.copy(context.getSharedPreferences("AndroidMail.Main", Context.MODE_PRIVATE))
editor.commit()
}
}
fun createStorageEditor(): StorageEditor {
@ -138,26 +124,16 @@ class Preferences internal constructor(
accountsMap?.remove(account.uuid)
accountsInOrder.remove(account)
try {
backendManager.removeBackend(account)
} catch (e: Exception) {
Timber.e(e, "Failed to reset remote store for account %s", account.uuid)
}
LocalStore.removeAccount(account)
val storageEditor = createStorageEditor()
accountPreferenceSerializer.delete(storageEditor, storage, account)
storageEditor.commit()
localKeyStoreManager.deleteCertificates(account)
if (account === newAccount) {
newAccount = null
}
}
notifyListeners()
notifyAccountsChangeListeners()
}
var defaultAccount: Account?
@ -190,7 +166,15 @@ class Preferences internal constructor(
accountPreferenceSerializer.save(editor, storage, account)
editor.commit()
notifyListeners()
notifyAccountsChangeListeners()
}
fun saveSettings() {
val editor = createStorageEditor()
K9.save(editor)
editor.commit()
notifySettingsChangeListeners()
}
private fun ensureAssignedAccountNumber(account: Account) {
@ -237,10 +221,10 @@ class Preferences internal constructor(
loadAccounts()
}
notifyListeners()
notifyAccountsChangeListeners()
}
private fun notifyListeners() {
private fun notifyAccountsChangeListeners() {
for (listener in accountsChangeListeners) {
listener.onAccountsChanged()
}
@ -254,6 +238,20 @@ class Preferences internal constructor(
accountsChangeListeners.remove(accountsChangeListener)
}
private fun notifySettingsChangeListeners() {
for (listener in settingsChangeListeners) {
listener.onSettingsChanged()
}
}
fun addSettingsChangeListener(settingsChangeListener: SettingsChangeListener) {
settingsChangeListeners.add(settingsChangeListener)
}
fun removeSettingsChangeListener(settingsChangeListener: SettingsChangeListener) {
settingsChangeListeners.remove(settingsChangeListener)
}
companion object {
@JvmStatic
fun getPreferences(context: Context): Preferences {

View file

@ -0,0 +1,5 @@
package com.fsck.k9
interface SettingsChangeListener {
fun onSettingsChanged()
}

View file

@ -228,18 +228,6 @@ public class LocalStore {
return schemaDefinitionFactory.getDatabaseVersion();
}
public static void removeAccount(Account account) {
try {
removeInstance(account);
} catch (Exception e) {
Timber.e(e, "Failed to reset local store for account %s", account.getUuid());
}
}
private static void removeInstance(Account account) {
DI.get(LocalStoreProvider.class).removeInstance(account);
}
public void switchLocalStorage(final String newStorageProviderId) throws MessagingException {
database.switchProvider(newStorageProviderId);
}

View file

@ -12,6 +12,7 @@ class StorageMigrationTo6(
fun performLegacyMigrations() {
rewriteKeyguardPrivacy()
rewriteTheme()
migrateOpenPgpGlobalToAccountSettings()
}
private fun rewriteKeyguardPrivacy() {
@ -39,6 +40,26 @@ class StorageMigrationTo6(
migrationsHelper.writeValue(db, "theme", newTheme.toString())
}
private fun migrateOpenPgpGlobalToAccountSettings() {
val accountUuidsListValue = migrationsHelper.readValue(db, "accountUuids")
if (accountUuidsListValue == null || accountUuidsListValue.isEmpty()) {
return
}
val openPgpProvider = migrationsHelper.readValue(db, "openPgpProvider") ?: ""
val openPgpSupportSignOnly = migrationsHelper.readValue(db, "openPgpSupportSignOnly")?.toBoolean() ?: false
val openPgpHideSignOnly = (!openPgpSupportSignOnly).toString()
val accountUuids = accountUuidsListValue.split(",")
for (accountUuid in accountUuids) {
migrationsHelper.writeValue(db, "$accountUuid.openPgpProvider", openPgpProvider)
migrationsHelper.writeValue(db, "$accountUuid.openPgpHideSignOnly", openPgpHideSignOnly)
}
migrationsHelper.writeValue(db, "openPgpProvider", null)
migrationsHelper.writeValue(db, "openPgpSupportSignOnly", null)
}
companion object {
private const val THEME_ORDINAL_LIGHT = 0
private const val THEME_ORDINAL_DARK = 1

View file

@ -1,7 +1,10 @@
package com.fsck.k9.account
import com.fsck.k9.Account
import com.fsck.k9.Core
import com.fsck.k9.LocalKeyStoreManager
import com.fsck.k9.Preferences
import com.fsck.k9.backend.BackendManager
import com.fsck.k9.controller.MessagingController
import com.fsck.k9.mailstore.LocalStoreProvider
import timber.log.Timber
@ -12,6 +15,8 @@ import timber.log.Timber
class AccountRemover(
private val localStoreProvider: LocalStoreProvider,
private val messagingController: MessagingController,
private val backendManager: BackendManager,
private val localKeyStoreManager: LocalKeyStoreManager,
private val preferences: Preferences
) {
@ -25,19 +30,36 @@ class AccountRemover(
val accountName = account.description
Timber.v("Removing account '%s'…", accountName)
try {
val localStore = localStoreProvider.getInstance(account)
localStore.delete()
} catch (e: Exception) {
Timber.w(e, "Error removing message database for account '%s'", accountName)
// Ignore, this may lead to localStores on sd-cards that are currently not inserted to be left
}
removeLocalStore(account)
messagingController.deleteAccount(account)
removeBackend(account)
preferences.deleteAccount(account)
localKeyStoreManager.deleteCertificates(account)
Core.setServicesEnabled()
Timber.v("Finished removing account '%s'.", accountName)
}
private fun removeLocalStore(account: Account) {
try {
val localStore = localStoreProvider.getInstance(account)
localStore.delete()
} catch (e: Exception) {
Timber.w(e, "Error removing message database for account '%s'", account.description)
// Ignore, this may lead to localStores on sd-cards that are currently not inserted to be left
}
localStoreProvider.removeInstance(account)
}
private fun removeBackend(account: Account) {
try {
backendManager.removeBackend(account)
} catch (e: Exception) {
Timber.e(e, "Failed to reset remote store for account %s", account.uuid)
}
}
}

View file

@ -3,7 +3,15 @@ package com.fsck.k9.account
import org.koin.dsl.module
val accountModule = module {
factory { AccountRemover(get(), get(), get()) }
factory {
AccountRemover(
localStoreProvider = get(),
messagingController = get(),
backendManager = get(),
localKeyStoreManager = get(),
preferences = get()
)
}
factory { BackgroundAccountRemover(get()) }
factory { AccountCreator(get(), get()) }
}

View file

@ -558,9 +558,7 @@ class MessageListFragment :
K9.setSortAscending(this.sortType, this.sortAscending)
sortDateAscending = K9.isSortAscending(SortType.SORT_DATE)
val editor = preferences.createStorageEditor()
K9.save(editor)
editor.commit()
K9.saveSettingsAsync()
}
reSort()

View file

@ -18,7 +18,7 @@ val settingsUiModule = module {
single { AccountsLiveData(get()) }
viewModel { SettingsViewModel(get()) }
factory { GeneralSettingsDataStore(get(), get(), get(named("SaveSettingsExecutorService")), get()) }
factory { GeneralSettingsDataStore(jobManager = get(), themeManager = get()) }
single(named("SaveSettingsExecutorService")) {
Executors.newSingleThreadExecutor(NamedThreadFactory("SaveSettings"))
}

View file

@ -5,15 +5,11 @@ import androidx.preference.PreferenceDataStore
import com.fsck.k9.K9
import com.fsck.k9.K9.AppTheme
import com.fsck.k9.K9.SubTheme
import com.fsck.k9.Preferences
import com.fsck.k9.job.K9JobManager
import com.fsck.k9.ui.base.ThemeManager
import java.util.concurrent.ExecutorService
class GeneralSettingsDataStore(
private val preferences: Preferences,
private val jobManager: K9JobManager,
private val executorService: ExecutorService,
private val themeManager: ThemeManager
) : PreferenceDataStore() {
var activity: FragmentActivity? = null
@ -230,12 +226,7 @@ class GeneralSettingsDataStore(
}
private fun saveSettings() {
val editor = preferences.createStorageEditor()
K9.save(editor)
executorService.execute {
editor.commit()
}
K9.saveSettingsAsync()
}
private fun setTheme(value: String?) {