Merge pull request #5209 from k9mail/convert_to_kotlin
This commit is contained in:
commit
cc2413a180
14 changed files with 316 additions and 385 deletions
|
@ -13,7 +13,15 @@ import com.fsck.k9.setup.ServerNameSuggester
|
|||
import org.koin.dsl.module
|
||||
|
||||
val mainModule = module {
|
||||
single { Preferences.getPreferences(get()) }
|
||||
single {
|
||||
Preferences(
|
||||
context = get(),
|
||||
storagePersister = get(),
|
||||
localStoreProvider = get(),
|
||||
localKeyStoreManager = get(),
|
||||
accountPreferenceSerializer = get()
|
||||
)
|
||||
}
|
||||
single { get<Context>().resources }
|
||||
single { get<Context>().contentResolver }
|
||||
single { LocalStoreProvider() }
|
||||
|
|
|
@ -1,329 +0,0 @@
|
|||
|
||||
package com.fsck.k9;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RestrictTo;
|
||||
import androidx.annotation.RestrictTo.Scope;
|
||||
|
||||
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;
|
||||
import com.fsck.k9.preferences.StoragePersister;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
public class Preferences {
|
||||
|
||||
private static Preferences preferences;
|
||||
private AccountPreferenceSerializer accountPreferenceSerializer;
|
||||
|
||||
public static synchronized Preferences getPreferences(Context context) {
|
||||
if (preferences == null) {
|
||||
Context appContext = context.getApplicationContext();
|
||||
CoreResourceProvider resourceProvider = DI.get(CoreResourceProvider.class);
|
||||
LocalKeyStoreManager localKeyStoreManager = DI.get(LocalKeyStoreManager.class);
|
||||
AccountPreferenceSerializer accountPreferenceSerializer = DI.get(AccountPreferenceSerializer.class);
|
||||
LocalStoreProvider localStoreProvider = DI.get(LocalStoreProvider.class);
|
||||
StoragePersister storagePersister = DI.get(StoragePersister.class);
|
||||
|
||||
preferences = new Preferences(appContext, resourceProvider, storagePersister, localStoreProvider, localKeyStoreManager, accountPreferenceSerializer);
|
||||
}
|
||||
return preferences;
|
||||
}
|
||||
|
||||
private Storage storage;
|
||||
@GuardedBy("accountLock")
|
||||
private Map<String, Account> accounts = null;
|
||||
@GuardedBy("accountLock")
|
||||
private List<Account> accountsInOrder = null;
|
||||
@GuardedBy("accountLock")
|
||||
private Account newAccount;
|
||||
|
||||
private final List<AccountsChangeListener> accountsChangeListeners = new CopyOnWriteArrayList<>();
|
||||
private final Context context;
|
||||
private final LocalStoreProvider localStoreProvider;
|
||||
private final CoreResourceProvider resourceProvider;
|
||||
private final LocalKeyStoreManager localKeyStoreManager;
|
||||
private final StoragePersister storagePersister;
|
||||
|
||||
private final Object accountLock = new Object();
|
||||
|
||||
private Preferences(Context context, CoreResourceProvider resourceProvider,
|
||||
StoragePersister storagePersister, LocalStoreProvider localStoreProvider,
|
||||
LocalKeyStoreManager localKeyStoreManager,
|
||||
AccountPreferenceSerializer accountPreferenceSerializer) {
|
||||
this.storage = new Storage();
|
||||
this.storagePersister = storagePersister;
|
||||
this.context = context;
|
||||
this.resourceProvider = resourceProvider;
|
||||
this.localStoreProvider = localStoreProvider;
|
||||
this.localKeyStoreManager = localKeyStoreManager;
|
||||
this.accountPreferenceSerializer = accountPreferenceSerializer;
|
||||
|
||||
Map<String, String> persistedStorageValues = storagePersister.loadValues();
|
||||
storage.replaceAll(persistedStorageValues);
|
||||
|
||||
if (storage.isEmpty()) {
|
||||
Timber.i("Preferences storage is zero-size, importing from Android-style preferences");
|
||||
StorageEditor editor = createStorageEditor();
|
||||
editor.copy(context.getSharedPreferences("AndroidMail.Main", Context.MODE_PRIVATE));
|
||||
editor.commit();
|
||||
}
|
||||
}
|
||||
|
||||
public StorageEditor createStorageEditor() {
|
||||
return storagePersister.createStorageEditor(storage);
|
||||
}
|
||||
|
||||
@RestrictTo(Scope.TESTS)
|
||||
public void clearAccounts() {
|
||||
synchronized (accountLock) {
|
||||
accounts = new HashMap<>();
|
||||
accountsInOrder = new LinkedList<>();
|
||||
}
|
||||
}
|
||||
|
||||
public void loadAccounts() {
|
||||
synchronized (accountLock) {
|
||||
accounts = new HashMap<>();
|
||||
accountsInOrder = new LinkedList<>();
|
||||
String accountUuids = getStorage().getString("accountUuids", null);
|
||||
if ((accountUuids != null) && (accountUuids.length() != 0)) {
|
||||
String[] uuids = accountUuids.split(",");
|
||||
for (String uuid : uuids) {
|
||||
Account newAccount = new Account(uuid);
|
||||
accountPreferenceSerializer.loadAccount(newAccount, storage);
|
||||
accounts.put(uuid, newAccount);
|
||||
accountsInOrder.add(newAccount);
|
||||
}
|
||||
}
|
||||
if ((newAccount != null) && newAccount.getAccountNumber() != -1) {
|
||||
accounts.put(newAccount.getUuid(), newAccount);
|
||||
if (!accountsInOrder.contains(newAccount)) {
|
||||
accountsInOrder.add(newAccount);
|
||||
}
|
||||
newAccount = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the accounts on the system. If no accounts are
|
||||
* registered the method returns an empty array.
|
||||
*
|
||||
* @return all accounts
|
||||
*/
|
||||
public List<Account> getAccounts() {
|
||||
synchronized (accountLock) {
|
||||
if (accounts == null) {
|
||||
loadAccounts();
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(new ArrayList<>(accountsInOrder));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the accounts on the system. If no accounts are
|
||||
* registered the method returns an empty array.
|
||||
*
|
||||
* @return all accounts with {@link Account#isAvailable(Context)}
|
||||
*/
|
||||
public Collection<Account> getAvailableAccounts() {
|
||||
List<Account> allAccounts = getAccounts();
|
||||
Collection<Account> result = new ArrayList<>(allAccounts.size());
|
||||
for (Account account : allAccounts) {
|
||||
if (account.isAvailable(context)) {
|
||||
result.add(account);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Account getAccount(String uuid) {
|
||||
synchronized (accountLock) {
|
||||
if (accounts == null) {
|
||||
loadAccounts();
|
||||
}
|
||||
|
||||
return accounts.get(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
public Account newAccount() {
|
||||
synchronized (accountLock) {
|
||||
String accountUuid = UUID.randomUUID().toString();
|
||||
newAccount = new Account(accountUuid);
|
||||
accountPreferenceSerializer.loadDefaults(newAccount);
|
||||
accounts.put(newAccount.getUuid(), newAccount);
|
||||
accountsInOrder.add(newAccount);
|
||||
|
||||
return newAccount;
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteAccount(Account account) {
|
||||
synchronized (accountLock) {
|
||||
if (accounts != null) {
|
||||
accounts.remove(account.getUuid());
|
||||
}
|
||||
if (accountsInOrder != null) {
|
||||
accountsInOrder.remove(account);
|
||||
}
|
||||
|
||||
try {
|
||||
getBackendManager().removeBackend(account);
|
||||
} catch (Exception e) {
|
||||
Timber.e(e, "Failed to reset remote store for account %s", account.getUuid());
|
||||
}
|
||||
LocalStore.removeAccount(account);
|
||||
|
||||
StorageEditor storageEditor = createStorageEditor();
|
||||
accountPreferenceSerializer.delete(storageEditor, storage, account);
|
||||
storageEditor.commit();
|
||||
localKeyStoreManager.deleteCertificates(account);
|
||||
|
||||
if (newAccount == account) {
|
||||
newAccount = null;
|
||||
}
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Account marked as default. If no account is marked as default
|
||||
* the first account in the list is marked as default and then returned. If
|
||||
* there are no accounts on the system the method returns null.
|
||||
*/
|
||||
public Account getDefaultAccount() {
|
||||
Account defaultAccount;
|
||||
synchronized (accountLock) {
|
||||
String defaultAccountUuid = getStorage().getString("defaultAccountUuid", null);
|
||||
defaultAccount = getAccount(defaultAccountUuid);
|
||||
}
|
||||
|
||||
if (defaultAccount == null) {
|
||||
Collection<Account> accounts = getAvailableAccounts();
|
||||
if (!accounts.isEmpty()) {
|
||||
defaultAccount = accounts.iterator().next();
|
||||
setDefaultAccount(defaultAccount);
|
||||
}
|
||||
}
|
||||
|
||||
return defaultAccount;
|
||||
}
|
||||
|
||||
public void setDefaultAccount(Account account) {
|
||||
createStorageEditor().putString("defaultAccountUuid", account.getUuid()).commit();
|
||||
}
|
||||
|
||||
public Storage getStorage() {
|
||||
return storage;
|
||||
}
|
||||
|
||||
private BackendManager getBackendManager() {
|
||||
return DI.get(BackendManager.class);
|
||||
}
|
||||
|
||||
public void saveAccount(Account account) {
|
||||
ensureAssignedAccountNumber(account);
|
||||
processChangedValues(account);
|
||||
|
||||
StorageEditor editor = createStorageEditor();
|
||||
accountPreferenceSerializer.save(editor, storage, account);
|
||||
editor.commit();
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
private void ensureAssignedAccountNumber(Account account) {
|
||||
if (account.getAccountNumber() != Account.UNASSIGNED_ACCOUNT_NUMBER) {
|
||||
return;
|
||||
}
|
||||
|
||||
int accountNumber = generateAccountNumber();
|
||||
account.setAccountNumber(accountNumber);
|
||||
}
|
||||
|
||||
private void processChangedValues(Account account) {
|
||||
if (account.isChangedVisibleLimits()) {
|
||||
try {
|
||||
localStoreProvider.getInstance(account).resetVisibleLimits(account.getDisplayCount());
|
||||
} catch (MessagingException e) {
|
||||
Timber.e(e, "Failed to load LocalStore!");
|
||||
}
|
||||
}
|
||||
|
||||
account.resetChangeMarkers();
|
||||
}
|
||||
|
||||
public int generateAccountNumber() {
|
||||
List<Integer> accountNumbers = getExistingAccountNumbers();
|
||||
return findNewAccountNumber(accountNumbers);
|
||||
}
|
||||
|
||||
private List<Integer> getExistingAccountNumbers() {
|
||||
List<Account> accounts = getAccounts();
|
||||
List<Integer> accountNumbers = new ArrayList<>(accounts.size());
|
||||
for (Account a : accounts) {
|
||||
accountNumbers.add(a.getAccountNumber());
|
||||
}
|
||||
return accountNumbers;
|
||||
}
|
||||
|
||||
private static int findNewAccountNumber(List<Integer> accountNumbers) {
|
||||
int newAccountNumber = -1;
|
||||
Collections.sort(accountNumbers);
|
||||
for (int accountNumber : accountNumbers) {
|
||||
if (accountNumber > newAccountNumber + 1) {
|
||||
break;
|
||||
}
|
||||
newAccountNumber = accountNumber;
|
||||
}
|
||||
newAccountNumber++;
|
||||
return newAccountNumber;
|
||||
}
|
||||
|
||||
public void move(Account account, boolean mUp) {
|
||||
synchronized (accountLock) {
|
||||
StorageEditor storageEditor = createStorageEditor();
|
||||
accountPreferenceSerializer.move(storageEditor, account, storage, mUp);
|
||||
storageEditor.commit();
|
||||
loadAccounts();
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
private void notifyListeners() {
|
||||
for (AccountsChangeListener listener : accountsChangeListeners) {
|
||||
listener.onAccountsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void addOnAccountsChangeListener(@NonNull AccountsChangeListener accountsChangeListener) {
|
||||
accountsChangeListeners.add(accountsChangeListener);
|
||||
}
|
||||
|
||||
public void removeOnAccountsChangeListener(@NonNull AccountsChangeListener accountsChangeListener) {
|
||||
accountsChangeListeners.remove(accountsChangeListener);
|
||||
}
|
||||
}
|
263
app/core/src/main/java/com/fsck/k9/Preferences.kt
Normal file
263
app/core/src/main/java/com/fsck/k9/Preferences.kt
Normal file
|
@ -0,0 +1,263 @@
|
|||
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
|
||||
import com.fsck.k9.preferences.StoragePersister
|
||||
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")
|
||||
private var accountsMap: MutableMap<String, Account>? = null
|
||||
|
||||
@GuardedBy("accountLock")
|
||||
private var accountsInOrder = mutableListOf<Account>()
|
||||
|
||||
@GuardedBy("accountLock")
|
||||
private var newAccount: Account? = null
|
||||
private val accountsChangeListeners = CopyOnWriteArrayList<AccountsChangeListener>()
|
||||
|
||||
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 {
|
||||
return storagePersister.createStorageEditor(storage)
|
||||
}
|
||||
|
||||
@RestrictTo(RestrictTo.Scope.TESTS)
|
||||
fun clearAccounts() {
|
||||
synchronized(accountLock) {
|
||||
accountsMap = HashMap()
|
||||
accountsInOrder = LinkedList()
|
||||
}
|
||||
}
|
||||
|
||||
fun loadAccounts() {
|
||||
synchronized(accountLock) {
|
||||
val accounts = mutableMapOf<String, Account>()
|
||||
val accountsInOrder = mutableListOf<Account>()
|
||||
|
||||
val accountUuids = storage.getString("accountUuids", null)
|
||||
if (!accountUuids.isNullOrEmpty()) {
|
||||
accountUuids.split(",").forEach { uuid ->
|
||||
val newAccount = Account(uuid)
|
||||
accountPreferenceSerializer.loadAccount(newAccount, storage)
|
||||
|
||||
accounts[uuid] = newAccount
|
||||
accountsInOrder.add(newAccount)
|
||||
}
|
||||
}
|
||||
|
||||
newAccount?.takeIf { it.accountNumber != -1 }?.let { newAccount ->
|
||||
accounts[newAccount.uuid] = newAccount
|
||||
if (newAccount !in accountsInOrder) {
|
||||
accountsInOrder.add(newAccount)
|
||||
}
|
||||
this.newAccount = null
|
||||
}
|
||||
|
||||
this.accountsMap = accounts
|
||||
this.accountsInOrder = accountsInOrder
|
||||
}
|
||||
}
|
||||
|
||||
val accounts: List<Account>
|
||||
get() {
|
||||
synchronized(accountLock) {
|
||||
if (accountsMap == null) {
|
||||
loadAccounts()
|
||||
}
|
||||
|
||||
return accountsInOrder.toList()
|
||||
}
|
||||
}
|
||||
|
||||
val availableAccounts: Collection<Account>
|
||||
get() = accounts.filter { it.isAvailable(context) }
|
||||
|
||||
fun getAccount(uuid: String): Account? {
|
||||
synchronized(accountLock) {
|
||||
if (accountsMap == null) {
|
||||
loadAccounts()
|
||||
}
|
||||
|
||||
return accountsMap!![uuid]
|
||||
}
|
||||
}
|
||||
|
||||
fun newAccount(): Account {
|
||||
val accountUuid = UUID.randomUUID().toString()
|
||||
val account = Account(accountUuid)
|
||||
accountPreferenceSerializer.loadDefaults(account)
|
||||
|
||||
synchronized(accountLock) {
|
||||
newAccount = account
|
||||
accountsMap!![account.uuid] = account
|
||||
accountsInOrder.add(account)
|
||||
}
|
||||
|
||||
return account
|
||||
}
|
||||
|
||||
fun deleteAccount(account: Account) {
|
||||
synchronized(accountLock) {
|
||||
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()
|
||||
}
|
||||
|
||||
var defaultAccount: Account?
|
||||
get() {
|
||||
return getDefaultAccountOrNull() ?: availableAccounts.firstOrNull()?.also { newDefaultAccount ->
|
||||
defaultAccount = newDefaultAccount
|
||||
}
|
||||
}
|
||||
set(account) {
|
||||
requireNotNull(account)
|
||||
|
||||
createStorageEditor()
|
||||
.putString("defaultAccountUuid", account.uuid)
|
||||
.commit()
|
||||
}
|
||||
|
||||
private fun getDefaultAccountOrNull(): Account? {
|
||||
return synchronized(accountLock) {
|
||||
storage.getString("defaultAccountUuid", null)?.let { defaultAccountUuid ->
|
||||
getAccount(defaultAccountUuid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun saveAccount(account: Account) {
|
||||
ensureAssignedAccountNumber(account)
|
||||
processChangedValues(account)
|
||||
|
||||
val editor = createStorageEditor()
|
||||
accountPreferenceSerializer.save(editor, storage, account)
|
||||
editor.commit()
|
||||
|
||||
notifyListeners()
|
||||
}
|
||||
|
||||
private fun ensureAssignedAccountNumber(account: Account) {
|
||||
if (account.accountNumber != Account.UNASSIGNED_ACCOUNT_NUMBER) return
|
||||
|
||||
account.accountNumber = generateAccountNumber()
|
||||
}
|
||||
|
||||
private fun processChangedValues(account: Account) {
|
||||
if (account.isChangedVisibleLimits) {
|
||||
try {
|
||||
localStoreProvider.getInstance(account).resetVisibleLimits(account.displayCount)
|
||||
} catch (e: MessagingException) {
|
||||
Timber.e(e, "Failed to load LocalStore!")
|
||||
}
|
||||
}
|
||||
account.resetChangeMarkers()
|
||||
}
|
||||
|
||||
fun generateAccountNumber(): Int {
|
||||
val accountNumbers = accounts.map { it.accountNumber }
|
||||
return findNewAccountNumber(accountNumbers)
|
||||
}
|
||||
|
||||
private fun findNewAccountNumber(accountNumbers: List<Int>): Int {
|
||||
var newAccountNumber = -1
|
||||
for (accountNumber in accountNumbers.sorted()) {
|
||||
if (accountNumber > newAccountNumber + 1) {
|
||||
break
|
||||
}
|
||||
newAccountNumber = accountNumber
|
||||
}
|
||||
newAccountNumber++
|
||||
|
||||
return newAccountNumber
|
||||
}
|
||||
|
||||
fun move(account: Account, up: Boolean) {
|
||||
synchronized(accountLock) {
|
||||
val storageEditor = createStorageEditor()
|
||||
accountPreferenceSerializer.move(storageEditor, account, storage, up)
|
||||
storageEditor.commit()
|
||||
|
||||
loadAccounts()
|
||||
}
|
||||
|
||||
notifyListeners()
|
||||
}
|
||||
|
||||
private fun notifyListeners() {
|
||||
for (listener in accountsChangeListeners) {
|
||||
listener.onAccountsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
fun addOnAccountsChangeListener(accountsChangeListener: AccountsChangeListener) {
|
||||
accountsChangeListeners.add(accountsChangeListener)
|
||||
}
|
||||
|
||||
fun removeOnAccountsChangeListener(accountsChangeListener: AccountsChangeListener) {
|
||||
accountsChangeListeners.remove(accountsChangeListener)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getPreferences(context: Context): Preferences {
|
||||
return DI.get(Preferences::class.java)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ class K9JobManager(
|
|||
private fun scheduleMailSync() {
|
||||
cancelAllMailSyncJobs()
|
||||
|
||||
preferences.availableAccounts?.forEach { account ->
|
||||
preferences.availableAccounts.forEach { account ->
|
||||
mailSyncWorkerManager.scheduleMailSync(account)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,8 +65,9 @@ class SettingsExporter(
|
|||
|
||||
serializer.startTag(null, ACCOUNTS_ELEMENT)
|
||||
for (accountUuid in accountUuids) {
|
||||
val account = preferences.getAccount(accountUuid)
|
||||
writeAccount(serializer, account, prefs)
|
||||
preferences.getAccount(accountUuid)?.let { account ->
|
||||
writeAccount(serializer, account, prefs)
|
||||
}
|
||||
}
|
||||
serializer.endTag(null, ACCOUNTS_ELEMENT)
|
||||
|
||||
|
|
|
@ -1,18 +1,14 @@
|
|||
package com.fsck.k9.controller;
|
||||
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.fsck.k9.Account;
|
||||
import com.fsck.k9.AccountPreferenceSerializer;
|
||||
import com.fsck.k9.DI;
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.K9RobolectricTest;
|
||||
import com.fsck.k9.Preferences;
|
||||
|
@ -46,7 +42,6 @@ import org.mockito.ArgumentMatchers;
|
|||
import org.mockito.Captor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
@ -62,7 +57,6 @@ import static org.mockito.Mockito.doThrow;
|
|||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
@ -74,7 +68,6 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
private static final String FOLDER_NAME = "Folder";
|
||||
private static final long SENT_FOLDER_ID = 10;
|
||||
private static final int MAXIMUM_SMALL_MESSAGE_SIZE = 1000;
|
||||
private static final String ACCOUNT_UUID = "1";
|
||||
|
||||
private MessagingController controller;
|
||||
private Account account;
|
||||
|
@ -130,6 +123,9 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
}
|
||||
};
|
||||
|
||||
private Preferences preferences;
|
||||
private String accountUuid;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws MessagingException {
|
||||
|
@ -137,7 +133,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
MockitoAnnotations.initMocks(this);
|
||||
appContext = RuntimeEnvironment.application;
|
||||
|
||||
Preferences preferences = Preferences.getPreferences(appContext);
|
||||
preferences = Preferences.getPreferences(appContext);
|
||||
|
||||
controller = new MessagingController(appContext, notificationController, notificationStrategy,
|
||||
localStoreProvider, unreadMessageCountProvider, backendManager, preferences, messageStoreProvider,
|
||||
|
@ -186,7 +182,6 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
@Test
|
||||
public void searchLocalMessagesSynchronous_shouldCallSearchForMessagesOnLocalStore()
|
||||
throws Exception {
|
||||
setAccountsInPreferences(Collections.singletonMap(ACCOUNT_UUID, account));
|
||||
when(search.searchAllAccounts()).thenReturn(true);
|
||||
when(search.getAccountUuids()).thenReturn(new String[0]);
|
||||
|
||||
|
@ -198,7 +193,6 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
@Test
|
||||
public void searchLocalMessagesSynchronous_shouldNotifyWhenStoreFinishesRetrievingAMessage()
|
||||
throws Exception {
|
||||
setAccountsInPreferences(Collections.singletonMap(ACCOUNT_UUID, account));
|
||||
LocalMessage localMessage = mock(LocalMessage.class);
|
||||
when(localMessage.getFolder()).thenReturn(localFolder);
|
||||
when(search.searchAllAccounts()).thenReturn(true);
|
||||
|
@ -215,8 +209,6 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
}
|
||||
|
||||
private void setupRemoteSearch() throws Exception {
|
||||
setAccountsInPreferences(Collections.singletonMap(ACCOUNT_UUID, account));
|
||||
|
||||
remoteMessages = new ArrayList<>();
|
||||
Collections.addAll(remoteMessages, "oldMessageUid", "newMessageUid1", "newMessageUid2");
|
||||
List<String> newRemoteMessages = new ArrayList<>();
|
||||
|
@ -261,7 +253,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
public void searchRemoteMessagesSynchronous_shouldNotifyStartedListingRemoteMessages() throws Exception {
|
||||
setupRemoteSearch();
|
||||
|
||||
controller.searchRemoteMessagesSynchronous(ACCOUNT_UUID, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
controller.searchRemoteMessagesSynchronous(accountUuid, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
|
||||
verify(listener).remoteSearchStarted(FOLDER_ID);
|
||||
}
|
||||
|
@ -270,7 +262,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
public void searchRemoteMessagesSynchronous_shouldQueryRemoteFolder() throws Exception {
|
||||
setupRemoteSearch();
|
||||
|
||||
controller.searchRemoteMessagesSynchronous(ACCOUNT_UUID, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
controller.searchRemoteMessagesSynchronous(accountUuid, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
|
||||
verify(backend).search(FOLDER_NAME, "query", reqFlags, forbiddenFlags, false);
|
||||
}
|
||||
|
@ -279,7 +271,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
public void searchRemoteMessagesSynchronous_shouldAskLocalFolderToDetermineNewMessages() throws Exception {
|
||||
setupRemoteSearch();
|
||||
|
||||
controller.searchRemoteMessagesSynchronous(ACCOUNT_UUID, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
controller.searchRemoteMessagesSynchronous(accountUuid, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
|
||||
verify(localFolder).extractNewMessages(remoteMessages);
|
||||
}
|
||||
|
@ -288,7 +280,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
public void searchRemoteMessagesSynchronous_shouldTryAndGetNewMessages() throws Exception {
|
||||
setupRemoteSearch();
|
||||
|
||||
controller.searchRemoteMessagesSynchronous(ACCOUNT_UUID, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
controller.searchRemoteMessagesSynchronous(accountUuid, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
|
||||
verify(localFolder).getMessage("newMessageUid1");
|
||||
}
|
||||
|
@ -297,7 +289,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
public void searchRemoteMessagesSynchronous_shouldNotTryAndGetOldMessages() throws Exception {
|
||||
setupRemoteSearch();
|
||||
|
||||
controller.searchRemoteMessagesSynchronous(ACCOUNT_UUID, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
controller.searchRemoteMessagesSynchronous(accountUuid, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
|
||||
verify(localFolder, never()).getMessage("oldMessageUid");
|
||||
}
|
||||
|
@ -306,7 +298,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
public void searchRemoteMessagesSynchronous_shouldFetchNewMessages() throws Exception {
|
||||
setupRemoteSearch();
|
||||
|
||||
controller.searchRemoteMessagesSynchronous(ACCOUNT_UUID, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
controller.searchRemoteMessagesSynchronous(accountUuid, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
|
||||
verify(backend).fetchMessage(eq(FOLDER_NAME), eq("newMessageUid2"), fetchProfileCaptor.capture(),
|
||||
eq(MAXIMUM_SMALL_MESSAGE_SIZE));
|
||||
|
@ -316,7 +308,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
public void searchRemoteMessagesSynchronous_shouldNotFetchExistingMessages() throws Exception {
|
||||
setupRemoteSearch();
|
||||
|
||||
controller.searchRemoteMessagesSynchronous(ACCOUNT_UUID, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
controller.searchRemoteMessagesSynchronous(accountUuid, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
|
||||
verify(backend, never()).fetchMessage(eq(FOLDER_NAME), eq("newMessageUid1"), fetchProfileCaptor.capture(),
|
||||
eq(MAXIMUM_SMALL_MESSAGE_SIZE));
|
||||
|
@ -328,7 +320,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
when(backend.search(anyString(), anyString(), nullable(Set.class), nullable(Set.class), eq(false)))
|
||||
.thenThrow(new MessagingException("Test"));
|
||||
|
||||
controller.searchRemoteMessagesSynchronous(ACCOUNT_UUID, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
controller.searchRemoteMessagesSynchronous(accountUuid, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
|
||||
verify(listener).remoteSearchFailed(null, "Test");
|
||||
}
|
||||
|
@ -339,14 +331,14 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
when(backend.search(anyString(), nullable(String.class), nullable(Set.class), nullable(Set.class), eq(false)))
|
||||
.thenThrow(new MessagingException("Test"));
|
||||
|
||||
controller.searchRemoteMessagesSynchronous(ACCOUNT_UUID, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
controller.searchRemoteMessagesSynchronous(accountUuid, FOLDER_ID, "query", reqFlags, forbiddenFlags, listener);
|
||||
|
||||
verify(listener).remoteSearchFinished(FOLDER_ID, 0, 50, Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendPendingMessagesSynchronous_withNonExistentOutbox_shouldNotStartSync() throws MessagingException {
|
||||
when(account.getOutboxFolderId()).thenReturn(FOLDER_ID);
|
||||
account.setOutboxFolderId(FOLDER_ID);
|
||||
when(localFolder.exists()).thenReturn(false);
|
||||
controller.addListener(listener);
|
||||
|
||||
|
@ -433,7 +425,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
}
|
||||
|
||||
private void setupAccountWithMessageToSend() throws MessagingException {
|
||||
when(account.getOutboxFolderId()).thenReturn(FOLDER_ID);
|
||||
account.setOutboxFolderId(FOLDER_ID);
|
||||
account.setSentFolderId(SENT_FOLDER_ID);
|
||||
when(localStore.getFolder(SENT_FOLDER_ID)).thenReturn(sentFolder);
|
||||
when(sentFolder.getDatabaseId()).thenReturn(SENT_FOLDER_ID);
|
||||
|
@ -455,13 +447,12 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
when(backendManager.getBackend(account)).thenReturn(backend);
|
||||
}
|
||||
|
||||
private void configureAccount() throws MessagingException {
|
||||
// TODO use simple account object without mocks
|
||||
account = spy(new Account(ACCOUNT_UUID));
|
||||
DI.get(AccountPreferenceSerializer.class).loadDefaults(account);
|
||||
private void configureAccount() {
|
||||
account = preferences.newAccount();
|
||||
accountUuid = account.getUuid();
|
||||
|
||||
account.setMaximumAutoDownloadMessageSize(MAXIMUM_SMALL_MESSAGE_SIZE);
|
||||
account.setEmail("user@host.com");
|
||||
Mockito.doReturn(true).when(account).isAvailable(appContext);
|
||||
}
|
||||
|
||||
private void configureLocalStore() throws MessagingException {
|
||||
|
@ -474,20 +465,7 @@ public class MessagingControllerTest extends K9RobolectricTest {
|
|||
when(localStoreProvider.getInstance(account)).thenReturn(localStore);
|
||||
}
|
||||
|
||||
private void setAccountsInPreferences(Map<String, Account> newAccounts)
|
||||
throws Exception {
|
||||
// TODO this affects other tests, try to get rid of it
|
||||
Field accounts = Preferences.class.getDeclaredField("accounts");
|
||||
accounts.setAccessible(true);
|
||||
accounts.set(Preferences.getPreferences(appContext), newAccounts);
|
||||
|
||||
Field accountsInOrder = Preferences.class.getDeclaredField("accountsInOrder");
|
||||
accountsInOrder.setAccessible(true);
|
||||
ArrayList<Account> newAccountsInOrder = new ArrayList<>(newAccounts.values());
|
||||
accountsInOrder.set(Preferences.getPreferences(appContext), newAccountsInOrder);
|
||||
}
|
||||
|
||||
private void removeAccountsFromPreferences() {
|
||||
Preferences.getPreferences(appContext).clearAccounts();
|
||||
preferences.clearAccounts();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,7 +135,9 @@ class UnreadWidgetConfigurationFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
|
||||
private fun handleRegularAccount() {
|
||||
val selectedAccount = preferences.getAccount(selectedAccountUuid)
|
||||
val selectedAccount = preferences.getAccount(selectedAccountUuid!!)
|
||||
?: error("Account $selectedAccountUuid not found")
|
||||
|
||||
val accountDescription: String? = selectedAccount.description
|
||||
val summary = if (accountDescription.isNullOrEmpty()) selectedAccount.email else accountDescription
|
||||
|
||||
|
|
|
@ -33,8 +33,8 @@ class EditIdentity : K9Activity() {
|
|||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
identityIndex = intent.getIntExtra(EXTRA_IDENTITY_INDEX, -1)
|
||||
val accountUuid = intent.getStringExtra(EXTRA_ACCOUNT)
|
||||
account = Preferences.getPreferences(this).getAccount(accountUuid)
|
||||
val accountUuid = intent.getStringExtra(EXTRA_ACCOUNT) ?: error("Missing account UUID")
|
||||
account = Preferences.getPreferences(this).getAccount(accountUuid) ?: error("Couldn't find account")
|
||||
|
||||
identity = when {
|
||||
savedInstanceState != null -> savedInstanceState.getParcelable(EXTRA_IDENTITY) ?: error("Missing state")
|
||||
|
|
|
@ -434,6 +434,11 @@ open class MessageList :
|
|||
if (accountUuid != null) {
|
||||
// We've most likely been started by an old unread widget or accounts shortcut
|
||||
val account = preferences.getAccount(accountUuid)
|
||||
if (account == null) {
|
||||
Timber.d("Account %s not found.", accountUuid)
|
||||
return LaunchData(createDefaultLocalSearch())
|
||||
}
|
||||
|
||||
val folderId = defaultFolderProvider.getDefaultFolder(account)
|
||||
val search = LocalSearch().apply {
|
||||
addAccountUuid(accountUuid)
|
||||
|
@ -455,7 +460,7 @@ open class MessageList :
|
|||
}
|
||||
|
||||
private fun createDefaultLocalSearch(): LocalSearch {
|
||||
val account = preferences.defaultAccount
|
||||
val account = preferences.defaultAccount ?: error("No default account available")
|
||||
return LocalSearch().apply {
|
||||
addAccountUuid(account.uuid)
|
||||
addAllowedFolder(defaultFolderProvider.getDefaultFolder(account))
|
||||
|
@ -1141,7 +1146,7 @@ open class MessageList :
|
|||
}
|
||||
|
||||
override fun openMessage(messageReference: MessageReference) {
|
||||
val account = preferences.getAccount(messageReference.accountUuid)
|
||||
val account = preferences.getAccount(messageReference.accountUuid) ?: error("Account not found")
|
||||
val folderId = messageReference.folderId
|
||||
|
||||
val draftsFolderId = account.draftsFolderId
|
||||
|
|
|
@ -1048,7 +1048,7 @@ class MessageListFragment :
|
|||
}
|
||||
|
||||
private fun groupMessagesByAccount(messages: List<MessageReference>): Map<Account, List<MessageReference>> {
|
||||
return messages.groupBy { preferences.getAccount(it.accountUuid) }
|
||||
return messages.groupBy { preferences.getAccount(it.accountUuid)!! }
|
||||
}
|
||||
|
||||
private fun onSpam(messages: List<MessageReference>) {
|
||||
|
|
|
@ -35,7 +35,7 @@ class AutocryptKeyTransferPresenter internal constructor(
|
|||
return
|
||||
}
|
||||
|
||||
account = preferences.getAccount(accountUuid)
|
||||
account = preferences.getAccount(accountUuid) ?: error("Account $accountUuid not found")
|
||||
|
||||
openPgpApiManager.setOpenPgpProvider(
|
||||
account.openPgpProvider,
|
||||
|
|
|
@ -24,7 +24,8 @@ class MessageListExtractor(
|
|||
): MessageListItem {
|
||||
val position = cursor.position
|
||||
val accountUuid = cursor.getString(MLFProjectionInfo.ACCOUNT_UUID_COLUMN)
|
||||
val account = preferences.getAccount(accountUuid)
|
||||
val account = preferences.getAccount(accountUuid) ?: error("Account $accountUuid not found")
|
||||
|
||||
val fromList = cursor.getString(MLFProjectionInfo.SENDER_LIST_COLUMN)
|
||||
val toList = cursor.getString(MLFProjectionInfo.TO_LIST_COLUMN)
|
||||
val ccList = cursor.getString(MLFProjectionInfo.CC_LIST_COLUMN)
|
||||
|
|
|
@ -47,7 +47,9 @@ class AccountSettingsViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
private fun loadAccount(accountUuid: String) = preferences.getAccount(accountUuid)
|
||||
private fun loadAccount(accountUuid: String): Account {
|
||||
return preferences.getAccount(accountUuid) ?: error("Account $accountUuid not found")
|
||||
}
|
||||
|
||||
fun getFolders(account: Account): LiveData<RemoteFolderInfo> {
|
||||
if (foldersLiveData.value == null) {
|
||||
|
|
|
@ -15,7 +15,7 @@ class AccountActivator(
|
|||
private val messagingController: MessagingController
|
||||
) {
|
||||
fun enableAccount(accountUuid: String, incomingServerPassword: String?, outgoingServerPassword: String?) {
|
||||
val account = preferences.getAccount(accountUuid)
|
||||
val account = preferences.getAccount(accountUuid) ?: error("Account $accountUuid not found")
|
||||
|
||||
setAccountPasswords(account, incomingServerPassword, outgoingServerPassword)
|
||||
|
||||
|
|
Loading…
Reference in a new issue