diff --git a/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java b/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java index 8fc4f9c04efd7600cd4d35c76fcd629da4ab6e11..94a9678f1ef66977be44dbdd25a9fbcbe2f46e98 100644 --- a/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java +++ b/app/core/src/main/java/com/fsck/k9/controller/MessagingController.java @@ -28,6 +28,7 @@ import android.os.SystemClock; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; +import androidx.work.ListenableWorker.Result; import com.fsck.k9.Account; import com.fsck.k9.Account.DeletePolicy; import com.fsck.k9.Account.Expunge; @@ -2243,7 +2244,7 @@ public class MessagingController { return !backend.getSupportsTrashFolder(); } - public boolean performPeriodicMailSync(Account account) { + public Result performPeriodicMailSync(Account account) { final CountDownLatch latch = new CountDownLatch(1); MutableBoolean syncError = new MutableBoolean(false); checkMail(account, false, false, true, new SimpleMessagingListener() { @@ -2269,13 +2270,17 @@ public class MessagingController { boolean success = !syncError.getValue(); if (success) { + if (preferences.getAccount(account.getUuid()) == null) { + Timber.e("Account %s not present. Can't perform mail sync.", account.getUuid()); + return Result.failure(); + } long now = System.currentTimeMillis(); Timber.v("Account %s successfully synced @ %tc", account, now); account.setLastSyncTime(now); preferences.saveAccount(account); } - return success; + return success ? Result.success() : Result.retry(); } /** diff --git a/app/core/src/main/java/com/fsck/k9/job/MailSyncWorker.kt b/app/core/src/main/java/com/fsck/k9/job/MailSyncWorker.kt index 193c24a5ccc6c2eb8d86b8ba6a47dd43bcd551a2..d24ee920f3137770d7117aa341890dc58dcbeaac 100644 --- a/app/core/src/main/java/com/fsck/k9/job/MailSyncWorker.kt +++ b/app/core/src/main/java/com/fsck/k9/job/MailSyncWorker.kt @@ -44,9 +44,7 @@ class MailSyncWorker( return Result.success() } - val success = messagingController.performPeriodicMailSync(account) - - return if (success) Result.success() else Result.retry() + return messagingController.performPeriodicMailSync(account) } private fun isBackgroundSyncDisabled(): Boolean { diff --git a/app/k9mail/src/main/AndroidManifest.xml b/app/k9mail/src/main/AndroidManifest.xml index 575306f922f42d9c7c7989c82b9d2a75223af114..58f4db6613fbcb3a2129a686a6d601d14e41cb55 100644 --- a/app/k9mail/src/main/AndroidManifest.xml +++ b/app/k9mail/src/main/AndroidManifest.xml @@ -343,6 +343,15 @@ + + + + + + + . + */ + +package com.fsck.k9.account + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.fsck.k9.Preferences +import com.fsck.k9.activity.setup.accountmanager.EeloAccountCreator +import com.fsck.k9.controller.push.PushController +import java.util.concurrent.Executors +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class AccountSyncReceiver : BroadcastReceiver(), KoinComponent { + + companion object { + private const val ACCOUNT_CREATION_ACTION = "foundation.e.accountmanager.account.create" + private const val ACCOUNT_REMOVAL_ACTION = "foundation.e.accountmanager.account.remove" + } + + private val pushController: PushController by inject() + private val preferences: Preferences by inject() + private val accountRemover: BackgroundAccountRemover by inject() + + override fun onReceive(context: Context?, intent: Intent?) { + if (intent == null) { + return + } + + when (intent.action) { + ACCOUNT_CREATION_ACTION -> createNewAccount(context) + ACCOUNT_REMOVAL_ACTION -> removeAccount(intent) + } + } + + private fun createNewAccount(context: Context?) { + pushController.init() + context?.let { + Executors.newSingleThreadExecutor().execute { + EeloAccountCreator.loadAccountsFromAccountManager( + it.applicationContext, + preferences, + accountRemover, + null + ) + } + } + } + + private fun removeAccount(intent: Intent) { + val account = intent.extras?.getString("account") ?: return + preferences.accounts.forEach { + if (it.email == account) { + accountRemover.removeAccountAsync(it.uuid) + return@forEach + } + } + } +} \ No newline at end of file diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt index 19c1aecf2e373e9df275a34ccca7aa6dc8defe55..ee593e734433c1569ea99066a3a8cb5b0eb3d4b7 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/MessageList.kt @@ -16,7 +16,6 @@ package com.fsck.k9.activity -import android.accounts.AccountManager import android.annotation.SuppressLint import android.app.SearchManager import android.content.Context @@ -53,9 +52,6 @@ import com.fsck.k9.Preferences import com.fsck.k9.account.BackgroundAccountRemover import com.fsck.k9.activity.compose.MessageActions import com.fsck.k9.activity.setup.AccountSetupBasics -import com.fsck.k9.activity.setup.accountmanager.AccountManagerConstants.ACCOUNT_EMAIL_ADDRESS_KEY -import com.fsck.k9.activity.setup.accountmanager.AccountManagerConstants.EELO_ACCOUNT_TYPE -import com.fsck.k9.activity.setup.accountmanager.AccountManagerConstants.GOOGLE_ACCOUNT_TYPE import com.fsck.k9.activity.setup.accountmanager.EeloAccountCreator import com.fsck.k9.controller.MessageReference import com.fsck.k9.controller.MessagingController @@ -157,17 +153,11 @@ open class MessageList : private var viewSwitcher: ViewSwitcher? = null private lateinit var recentChangesSnackbar: Snackbar - private lateinit var accountManager: AccountManager - public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setLayout(R.layout.message_list_loading) - val accounts = preferences.accounts - deleteIncompleteAccounts(accounts) - - accountManager = AccountManager.get(this) - addNewAccountsAutomatically(accounts, savedInstanceState) + addNewAccountsAutomatically(savedInstanceState) } private fun onAccountConfigurationFinish(savedInstanceState: Bundle?) { @@ -276,12 +266,6 @@ open class MessageList : displayViews() } - private fun deleteIncompleteAccounts(accounts: List) { - accounts.filter { !it.isFinishedSetup }.forEach { - accountRemover.removeAccountAsync(it.uuid) - } - } - private fun findFragments() { val fragmentManager = supportFragmentManager messageListFragment = fragmentManager.findFragmentById(R.id.message_list_container) as MessageListFragment? @@ -1704,61 +1688,16 @@ open class MessageList : val messageViewOnly: Boolean = false ) - private fun addNewAccountsAutomatically(accounts: List, savedInstanceState: Bundle?) { + private fun addNewAccountsAutomatically(savedInstanceState: Bundle?) { lifecycleScope.launch(Dispatchers.IO) { - try { - val eeloAccounts: Array = getEeloAccountsOnDevice() - val googleAccounts: Array = getGoogleAccountsOnDevice() - for (eeloAccount in eeloAccounts) { - val emailId: String = accountManager.getUserData(eeloAccount, ACCOUNT_EMAIL_ADDRESS_KEY) - if (!emailId.contains("@")) continue - var accountIsSignedIn = false - for (account in accounts) { - if (emailId == account.email) { - accountIsSignedIn = true - break - } - } - if (!accountIsSignedIn) { - val password: String = accountManager.getPassword(eeloAccount) - EeloAccountCreator.createAccount(applicationContext, emailId, password, false) - } + EeloAccountCreator.loadAccountsFromAccountManager(applicationContext, preferences, accountRemover) { + lifecycleScope.launch(Dispatchers.Main) { + onAccountConfigurationFinish(savedInstanceState) } - - for (googleAccount in googleAccounts) { - val emailId: String = accountManager.getUserData(googleAccount, ACCOUNT_EMAIL_ADDRESS_KEY) - - var accountIsSignedIn = false - for (account in accounts) { - if (emailId == account.email) { - if (account.name == null) { // we need to fix an old bug - account.name = emailId - Preferences.getPreferences(applicationContext).saveAccount(account) - } - accountIsSignedIn = true - break - } - } - if (!accountIsSignedIn) { - EeloAccountCreator.createAccount(applicationContext, emailId, "", true) - } - } - } catch (e: SecurityException) { - Timber.e(e) - } - lifecycleScope.launch(Dispatchers.Main) { - onAccountConfigurationFinish(savedInstanceState) } } } - private fun getEeloAccountsOnDevice(): Array { - return accountManager.getAccountsByType(EELO_ACCOUNT_TYPE) - } - - private fun getGoogleAccountsOnDevice(): Array { - return accountManager.getAccountsByType(GOOGLE_ACCOUNT_TYPE) - } companion object : KoinComponent { private const val EXTRA_SEARCH = "search_bytes" diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt index d1d3946c0f4d9c7ed0e6e02a84220fa607d6f0da..cbfb08b70e9e788a0b6235be0143f34ec71ff991 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt +++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/AccountSetupBasics.kt @@ -331,7 +331,9 @@ class AccountSetupBasics : K9Activity() { account.senderName = getOwnerName() account.email = email - account.name = email + if (account.name == null) { + account.name = email + } return account } diff --git a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/accountmanager/EeloAccountCreator.java b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/accountmanager/EeloAccountCreator.java index 16627d6565934300a69e0b2d7ef98f7189e09281..07571c3ef35df359b3591c33beb86c9ec712bbf4 100644 --- a/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/accountmanager/EeloAccountCreator.java +++ b/app/ui/legacy/src/main/java/com/fsck/k9/activity/setup/accountmanager/EeloAccountCreator.java @@ -16,14 +16,24 @@ package com.fsck.k9.activity.setup.accountmanager; + +import java.util.List; + +import android.accounts.AccountManager; import android.content.Context; +import android.os.Build.VERSION_CODES; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.annotation.WorkerThread; import com.fsck.k9.Account; import com.fsck.k9.Account.DeletePolicy; import com.fsck.k9.Core; import com.fsck.k9.DI; import com.fsck.k9.Preferences; import com.fsck.k9.account.AccountCreator; +import com.fsck.k9.account.BackgroundAccountRemover; import com.fsck.k9.autodiscovery.api.DiscoveredServerSettings; import com.fsck.k9.autodiscovery.api.DiscoveryResults; import com.fsck.k9.autodiscovery.api.DiscoveryTarget; @@ -42,7 +52,87 @@ public class EeloAccountCreator { private static final AccountCreator accountCreator = DI.get(AccountCreator.class); private static final SpecialLocalFoldersCreator localFoldersCreator = DI.get(SpecialLocalFoldersCreator.class); - public static void createAccount(Context context, String emailId, String password, boolean isGoogleAccount) { + @RequiresApi(api = VERSION_CODES.N) + @WorkerThread + public static void loadAccountsFromAccountManager(@NonNull Context context, @NonNull Preferences preferences, + @NonNull BackgroundAccountRemover accountRemover, @Nullable OnAccountLoadCompleteCallBack callBack) { + try { + AccountManager accountManager = AccountManager.get(context); + + List accounts = preferences.getAccounts(); + deleteIncompleteAccounts(accounts, accountRemover); + + loadEeloAccounts(context, accounts, accountManager); + loadGoogleAccounts(context, accounts, accountManager); + } catch (SecurityException e) { + Timber.e(e, "Failed to load accounts from accountManager because of security violation"); + } + if (callBack != null) { + callBack.onAccountLoadComplete(); + } + } + + @RequiresApi(api = VERSION_CODES.N) + private static void loadGoogleAccounts(@NonNull Context context, List accounts, + @NonNull AccountManager accountManager) { + android.accounts.Account[] googleAccounts = + accountManager.getAccountsByType(AccountManagerConstants.GOOGLE_ACCOUNT_TYPE); + for (android.accounts.Account googleAccount : googleAccounts) { + String emailId = + accountManager.getUserData(googleAccount, AccountManagerConstants.ACCOUNT_EMAIL_ADDRESS_KEY); + if (isInvalidEmail(emailId)) { + continue; + } + + boolean accountAlreadyPresent = accounts.stream() + .filter(account -> emailId.equalsIgnoreCase(account.getEmail())) + .peek(account -> updateAccountNameIfMissing(context, emailId, account)) + .findAny().isPresent(); + if (!accountAlreadyPresent) { + createAccount(context, emailId, "", true); + } + } + } + + private static void updateAccountNameIfMissing(@NonNull Context context, String emailId, Account account) { + if (account.getName() == null) { // we need to fix an old bug + account.setName(emailId); + Preferences.getPreferences(context).saveAccount(account); + } + } + + private static boolean isInvalidEmail(String emailId) { + return emailId == null || !emailId.contains("@"); + } + + @RequiresApi(api = VERSION_CODES.N) + private static void loadEeloAccounts(@NonNull Context context, List accounts, + @NonNull AccountManager accountManager) { + android.accounts.Account[] eeloAccounts = + accountManager.getAccountsByType(AccountManagerConstants.EELO_ACCOUNT_TYPE); + for (android.accounts.Account eeloAccount : eeloAccounts) { + String emailId = + accountManager.getUserData(eeloAccount, AccountManagerConstants.ACCOUNT_EMAIL_ADDRESS_KEY); + if (isInvalidEmail(emailId)) { + continue; + } + + boolean isNewAccount = accounts.stream() + .noneMatch(account -> emailId.equalsIgnoreCase(account.getEmail())); + if (isNewAccount) { + String password = accountManager.getPassword(eeloAccount); + createAccount(context, emailId, password, false); + } + } + } + + @RequiresApi(api = VERSION_CODES.N) + private static void deleteIncompleteAccounts(List accounts, BackgroundAccountRemover accountRemover) { + accounts.stream().filter(account -> !account.isFinishedSetup()) + .forEach(account -> accountRemover.removeAccountAsync(account.getUuid())); + } + + private static void createAccount(Context context, String emailId, String password, boolean isGoogleAccount) { Preferences preferences = Preferences.getPreferences(context); Account account = preferences.newAccount(); @@ -137,5 +227,9 @@ public class EeloAccountCreator { ) ); } + + public interface OnAccountLoadCompleteCallBack { + void onAccountLoadComplete(); + } }