From bb41b1d0603c6db0656c782be83c40938f72944a Mon Sep 17 00:00:00 2001 From: althafvly Date: Thu, 21 Aug 2025 14:10:50 +0530 Subject: [PATCH 1/2] AM: Move e dependencies into libs.versions.toml --- app/build.gradle.kts | 10 +++++----- gradle/libs.versions.toml | 12 ++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 18e97d13d..00aaf4765 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -256,16 +256,16 @@ dependencies { implementation(libs.commons.codec) implementation(libs.commons.lang) - // e-Specific dependencies - (Avoid moving into toml) - implementation("foundation.e:elib:0.0.1-alpha11") - implementation("foundation.e:synctools:58bc6752") { + // e-Specific dependencies + implementation(libs.elib) + implementation(libs.ez.vcard) + implementation(libs.synctools) { exclude(group="androidx.test") exclude(group = "junit") } - implementation("org.mnode.ical4j:ical4j:3.2.19") { + implementation(libs.ical4j) { exclude(group = "commons-logging", module = "commons-logging") } - implementation("com.googlecode.ez-vcard:ez-vcard:0.12.1") // for tests androidTestImplementation(libs.androidx.arch.core.testing) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 35efdfd25..977295c25 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -47,6 +47,12 @@ commons-codec = { strictly = "1.17.1" } #noinspection NewerVersionAvailable commons-lang = { strictly = "3.15.0" } +# --- e-Specific dependencies --- +elib = "0.0.1-alpha11" +ezVcard = "0.12.1" +ical4j = "3.2.19" +synctools = "58bc6752" + [libraries] android-desugaring = { module = "com.android.tools:desugar_jdk_libs_nio", version.ref = "android-desugaring" } androidx-activityCompose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" } @@ -108,6 +114,12 @@ room-testing = { module = "androidx.room:room-testing", version.ref = "room" } unifiedpush = { module = "org.unifiedpush.android:connector", version.ref = "unifiedpush" } unifiedpush-fcm = { module = "org.unifiedpush.android:embedded-fcm-distributor", version.ref = "unifiedpush-fcm" } +# --- e-Specific dependencies --- +elib = { module = "foundation.e:elib", version.ref = "elib" } +ez-vcard = { module = "com.googlecode.ez-vcard:ez-vcard", version.ref = "ezVcard" } +ical4j = { module = "org.mnode.ical4j:ical4j", version.ref = "ical4j" } +synctools = { module = "foundation.e:synctools", version.ref = "synctools" } + [plugins] android-application = { id = "com.android.application", version.ref = "android-agp" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } -- GitLab From 654d48ef4474c474645d06179bf09cdfb3ac95d8 Mon Sep 17 00:00:00 2001 From: althafvly Date: Mon, 4 Aug 2025 16:17:02 +0530 Subject: [PATCH 2/2] AM: Allow changing account type - In old account manager, our app supported multiple types (Google, WebDAV, Yahoo, Murena). Adding this flexibility now ensures we can add new account types in future commits. --- .../resource/LocalAddressBookStoreTest.kt | 2 +- .../at/bitfire/davdroid/sync/SyncerTest.kt | 2 +- .../at/bitfire/davdroid/db/AppDatabase.kt | 3 +- .../davdroid/push/PushMessageHandler.kt | 5 +- .../davdroid/push/PushRegistrationManager.kt | 5 +- .../davdroid/repository/AccountRepository.kt | 38 ++++++----- .../repository/DavCollectionRepository.kt | 4 +- .../davdroid/resource/LocalAddressBook.kt | 3 +- .../resource/LocalAddressBookStore.kt | 18 +++-- .../davdroid/resource/LocalCalendarStore.kt | 5 +- .../davdroid/resource/LocalDataStore.kt | 2 +- .../resource/LocalJtxCollectionStore.kt | 6 +- .../davdroid/resource/LocalTaskListStore.kt | 5 +- .../RefreshCollectionsWorker.kt | 3 +- .../davdroid/settings/AccountSettings.kt | 4 +- .../migration/AccountSettingsMigration17.kt | 8 ++- .../migration/AccountSettingsMigration18.kt | 5 +- .../sync/account/AccountsCleanupWorker.kt | 3 +- .../davdroid/sync/adapter/SyncAdapterImpl.kt | 5 +- .../davdroid/ui/CollectionSelectedUseCase.kt | 3 +- .../bitfire/davdroid/ui/DebugInfoGenerator.kt | 4 +- .../davdroid/ui/account/AccountActivity.kt | 7 +- .../davdroid/ui/account/AccountScreenModel.kt | 7 +- .../ui/account/CollectionSelectedUseCase.kt | 3 +- .../davdroid/ui/setup/AccountDetailsPage.kt | 2 +- .../davdroid/ui/setup/LoginScreenModel.kt | 15 +++-- .../at/bitfire/davdroid/ui/setup/LoginType.kt | 4 ++ .../e/accountmanager/AccountTypes.kt | 66 +++++++++++++++++++ .../e/accountmanager/utils/AccountHelper.kt | 40 +++++++++++ .../e/accountmanager/utils/SystemUtils.kt | 17 ++++- 30 files changed, 223 insertions(+), 71 deletions(-) create mode 100644 app/src/main/kotlin/foundation/e/accountmanager/AccountTypes.kt create mode 100644 app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookStoreTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookStoreTest.kt index f4f92690d..8d1409f0c 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookStoreTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/resource/LocalAddressBookStoreTest.kt @@ -141,7 +141,7 @@ class LocalAddressBookStoreTest { } mockkObject(localAddressBookStore) - every { localAddressBookStore.createAddressBookAccount(any(), any(), any()) } returns null + every { localAddressBookStore.createAddressBookAccount(any(), any(), any(), any()) } returns null assertEquals(null, localAddressBookStore.create(provider, collection)) } diff --git a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncerTest.kt b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncerTest.kt index ee5440936..c37523a9c 100644 --- a/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncerTest.kt +++ b/app/src/androidTest/kotlin/at/bitfire/davdroid/sync/SyncerTest.kt @@ -219,7 +219,7 @@ class SyncerTest { throw NotImplementedError() } - override fun updateAccount(oldAccount: Account, newAccount: Account) { + override fun updateAccount(oldAccount: Account, newAccount: Account, accountType: String) { throw NotImplementedError() } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/db/AppDatabase.kt b/app/src/main/kotlin/at/bitfire/davdroid/db/AppDatabase.kt index 3b2df226f..738d97944 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/db/AppDatabase.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/db/AppDatabase.kt @@ -33,6 +33,7 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import foundation.e.accountmanager.utils.AccountHelper import java.io.Writer import javax.inject.Singleton @@ -97,7 +98,7 @@ abstract class AppDatabase: RoomDatabase() { // remove all accounts because they're unfortunately useless without database val am = AccountManager.get(context) - for (account in am.getAccountsByType(context.getString(R.string.account_type))) + for (account in AccountHelper.getAllAccounts(am)) am.removeAccountExplicitly(account) } }) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/push/PushMessageHandler.kt b/app/src/main/kotlin/at/bitfire/davdroid/push/PushMessageHandler.kt index 8c36463bd..a59625ea3 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/push/PushMessageHandler.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/push/PushMessageHandler.kt @@ -15,6 +15,7 @@ import at.bitfire.davdroid.sync.SyncDataType import at.bitfire.davdroid.sync.TasksAppManager import at.bitfire.davdroid.sync.worker.SyncWorkerManager import dagger.Lazy +import foundation.e.accountmanager.AccountTypes import org.unifiedpush.android.connector.data.PushMessage import org.xmlpull.v1.XmlPullParserException import java.io.StringReader @@ -70,7 +71,7 @@ class PushMessageHandler @Inject constructor( syncDataTypes += SyncDataType.TASKS // Schedule sync for all the types identified - val account = accountRepository.fromName(service.accountName) + val account = accountRepository.fromName(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) for (syncDataType in syncDataTypes) syncWorkerManager.enqueueOneTime(account, syncDataType, fromPush = true) } @@ -81,7 +82,7 @@ class PushMessageHandler @Inject constructor( val service = instance.toLongOrNull()?.let { serviceRepository.getBlocking(it) } if (service != null) { logger.warning("Got push message without topic and service, syncing all accounts") - val account = accountRepository.fromName(service.accountName) + val account = accountRepository.fromName(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) syncWorkerManager.enqueueOneTimeAllAuthorities(account, fromPush = true) } else { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationManager.kt b/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationManager.kt index ad4e882d4..0d912facc 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationManager.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationManager.kt @@ -34,6 +34,7 @@ import at.bitfire.davdroid.repository.DavServiceRepository import at.bitfire.davdroid.sync.account.InvalidAccountException import dagger.Lazy import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.AccountTypes import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runInterruptible @@ -179,7 +180,7 @@ class PushRegistrationManager @Inject constructor( if (subscribeTo.isEmpty()) return - val account = accountRepository.get().fromName(service.accountName) + val account = accountRepository.get().fromName(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) httpClientBuilder.get() .fromAccountAsync(account) .build() @@ -293,7 +294,7 @@ class PushRegistrationManager @Inject constructor( if (from.isEmpty()) return - val account = accountRepository.get().fromName(service.accountName) + val account = accountRepository.get().fromName(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) httpClientBuilder.get() .fromAccountAsync(account) .build() diff --git a/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt b/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt index f83a3e583..49e8a87a3 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt @@ -8,7 +8,6 @@ import android.accounts.Account import android.accounts.AccountManager import android.accounts.OnAccountsUpdateListener import android.content.Context -import at.bitfire.davdroid.R import at.bitfire.davdroid.db.HomeSet import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.db.ServiceType @@ -28,6 +27,8 @@ import at.bitfire.davdroid.sync.worker.SyncWorkerManager import at.bitfire.vcard4android.GroupMethod import dagger.Lazy import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.AccountTypes +import foundation.e.accountmanager.utils.AccountHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow @@ -56,7 +57,6 @@ class AccountRepository @Inject constructor( private val tasksAppManager: Lazy ) { - private val accountType = context.getString(R.string.account_type) private val accountManager = AccountManager.get(context) /** @@ -70,8 +70,8 @@ class AccountRepository @Inject constructor( * * @return account if account creation was successful; null otherwise (for instance because an account with this name already exists) */ - fun createBlocking(accountName: String, credentials: Credentials?, config: DavResourceFinder.Configuration, groupMethod: GroupMethod): Account? { - val account = fromName(accountName) + fun createBlocking(accountName: String, accountType: String, credentials: Credentials?, config: DavResourceFinder.Configuration, groupMethod: GroupMethod): Account? { + val account = fromName(accountName, accountType) // create Android account val userData = AccountSettings.initialUserData(credentials) @@ -113,8 +113,8 @@ class AccountRepository @Inject constructor( return account } - suspend fun delete(accountName: String): Boolean { - val account = fromName(accountName) + suspend fun delete(accountName: String, accountType: String): Boolean { + val account = fromName(accountName, accountType) // remove account directly (bypassing the authenticator, which is our own) return try { accountManager.removeAccountExplicitly(account) @@ -136,7 +136,7 @@ class AccountRepository @Inject constructor( } } - fun exists(accountName: String): Boolean = + fun exists(accountName: String, accountType: String): Boolean = if (accountName.isEmpty()) false else @@ -144,14 +144,16 @@ class AccountRepository @Inject constructor( .getAccountsByType(accountType) .any { it.name == accountName } - fun fromName(accountName: String) = + fun fromName(accountName: String, accountType: String) = Account(accountName, accountType) - fun getAll(): Array = accountManager.getAccountsByType(accountType) + fun getAll(): Array { + return AccountHelper.getAllAccounts(accountManager) + } fun getAllFlow() = callbackFlow> { val listener = OnAccountsUpdateListener { accounts -> - trySend(accounts.filter { it.type == accountType }.toSet()) + trySend(accounts.filter { it.type in AccountTypes.getAccountTypes() }.toSet()) } withContext(Dispatchers.Default) { // causes disk I/O accountManager.addOnAccountsUpdatedListener(listener, null, true) @@ -175,12 +177,12 @@ class AccountRepository @Inject constructor( * @throws IllegalArgumentException if the new account name already exists * @throws Exception (or sub-classes) on other errors */ - suspend fun rename(oldName: String, newName: String) { - val oldAccount = fromName(oldName) - val newAccount = fromName(newName) + suspend fun rename(accountType: String, oldName: String, newName: String) { + val oldAccount = fromName(accountType, oldName) + val newAccount = fromName(accountType, newName) // check whether new account name already exists - if (accountManager.getAccountsByType(context.getString(R.string.account_type)).contains(newAccount)) + if (accountManager.getAccountsByType(accountType).contains(newAccount)) throw IllegalArgumentException("Account with name \"$newName\" already exists") // rename account @@ -215,16 +217,18 @@ class AccountRepository @Inject constructor( // update account name references in database serviceRepository.renameAccount(oldName, newName) + val addressBookAccountType = AccountTypes.getAddressBookForAccountType(accountType) + try { // update address books - localAddressBookStore.get().updateAccount(oldAccount, newAccount) + localAddressBookStore.get().updateAccount(oldAccount, newAccount, addressBookAccountType) } catch (e: Exception) { logger.log(Level.WARNING, "Couldn't change address books to renamed account", e) } try { // update calendar events - localCalendarStore.get().updateAccount(oldAccount, newAccount) + localCalendarStore.get().updateAccount(oldAccount, newAccount, accountType) } catch (e: Exception) { logger.log(Level.WARNING, "Couldn't change calendars to renamed account", e) } @@ -232,7 +236,7 @@ class AccountRepository @Inject constructor( try { // update account_name of local tasks val dataStore = tasksAppManager.get().getDataStore() - dataStore?.updateAccount(oldAccount, newAccount) + dataStore?.updateAccount(oldAccount, newAccount, accountType) } catch (e: Exception) { logger.log(Level.WARNING, "Couldn't change task lists to renamed account", e) } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt b/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt index ddb4a70a3..5271d5441 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/repository/DavCollectionRepository.kt @@ -24,7 +24,6 @@ import at.bitfire.dav4jvm.property.webdav.DisplayName import at.bitfire.dav4jvm.property.webdav.NS_WEBDAV import at.bitfire.dav4jvm.property.webdav.ResourceType import at.bitfire.davdroid.Constants -import at.bitfire.davdroid.R import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.db.CollectionType @@ -34,6 +33,7 @@ import at.bitfire.davdroid.network.HttpClient import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker import at.bitfire.davdroid.util.DavUtils import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.AccountTypes import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.runInterruptible import net.fortuna.ical4j.model.Calendar @@ -170,7 +170,7 @@ class DavCollectionRepository @Inject constructor( /** Deletes the given collection from the server and the database. */ suspend fun deleteRemote(collection: Collection) { val service = serviceRepository.getBlocking(collection.serviceId) ?: throw IllegalArgumentException("Service not found") - val account = Account(service.accountName, context.getString(R.string.account_type)) + val account = Account(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) httpClientBuilder.get().fromAccount(account).build().use { httpClient -> runInterruptible(ioDispatcher) { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBook.kt b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBook.kt index 1037010fd..e38a85c1d 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBook.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBook.kt @@ -36,6 +36,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.AccountTypes import java.util.LinkedList import java.util.Optional import java.util.logging.Level @@ -94,7 +95,7 @@ open class LocalAddressBook @AssistedInject constructor( val account = accountManager.getUserData(addressBookAccount, USER_DATA_COLLECTION_ID)?.toLongOrNull()?.let { collectionId -> collectionRepository.get(collectionId)?.let { collection -> serviceRepository.getBlocking(collection.serviceId)?.let { service -> - Account(service.accountName, context.getString(R.string.account_type)) + Account(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) } } } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBookStore.kt b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBookStore.kt index 6fbb821e9..4c480385a 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBookStore.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalAddressBookStore.kt @@ -16,6 +16,7 @@ import androidx.core.content.contentValuesOf import androidx.core.os.bundleOf import at.bitfire.davdroid.R import at.bitfire.davdroid.db.Collection +import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.repository.DavServiceRepository import at.bitfire.davdroid.settings.Settings import at.bitfire.davdroid.settings.SettingsManager @@ -24,6 +25,8 @@ import at.bitfire.davdroid.sync.account.setAndVerifyUserData import at.bitfire.davdroid.util.DavUtils.lastSegment import com.google.common.base.CharMatcher import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.AccountTypes +import foundation.e.accountmanager.utils.AccountHelper import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow @@ -89,10 +92,11 @@ class LocalAddressBookStore @Inject constructor( override fun create(provider: ContentProviderClient, fromCollection: Collection): LocalAddressBook? { val service = serviceRepository.getBlocking(fromCollection.serviceId) ?: throw IllegalArgumentException("Couldn't fetch DB service from collection") - val account = Account(service.accountName, context.getString(R.string.account_type)) + val account = Account(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) val name = accountName(fromCollection) val addressBookAccount = createAddressBookAccount( + service = service, account = account, name = name, id = fromCollection.id @@ -109,9 +113,9 @@ class LocalAddressBookStore @Inject constructor( } @OpenForTesting - internal fun createAddressBookAccount(account: Account, name: String, id: Long): Account? { + internal fun createAddressBookAccount(service: Service, account: Account, name: String, id: Long): Account? { // create address book account with reference to account, collection ID and URL - val addressBookAccount = Account(name, context.getString(R.string.account_type_address_book)) + val addressBookAccount = Account(name, AccountTypes.getAddressBookTypeFromPrincipal(service.principal)) val userData = bundleOf( LocalAddressBook.USER_DATA_ACCOUNT_NAME to account.name, LocalAddressBook.USER_DATA_ACCOUNT_TYPE to account.type, @@ -168,9 +172,9 @@ class LocalAddressBookStore @Inject constructor( * @param oldAccount The old account * @param newAccount The new account */ - override fun updateAccount(oldAccount: Account, newAccount: Account) { + override fun updateAccount(oldAccount: Account, newAccount: Account, accountType: String) { val accountManager = AccountManager.get(context) - accountManager.getAccountsByType(context.getString(R.string.account_type_address_book)) + accountManager.getAccountsByType(AccountTypes.getAddressBookForAccountType(accountType)) .filter { addressBookAccount -> accountManager.getUserData(addressBookAccount, LocalAddressBook.USER_DATA_ACCOUNT_NAME) == oldAccount.name && accountManager.getUserData(addressBookAccount, LocalAddressBook.USER_DATA_ACCOUNT_TYPE) == oldAccount.type @@ -193,7 +197,7 @@ class LocalAddressBookStore @Inject constructor( */ fun deleteByCollectionId(id: Long) { val accountManager = AccountManager.get(context) - val addressBookAccount = accountManager.getAccountsByType(context.getString(R.string.account_type_address_book)).firstOrNull { account -> + val addressBookAccount = AccountHelper.getAllAddressBooks(accountManager).firstOrNull { account -> accountManager.getUserData(account, LocalAddressBook.USER_DATA_COLLECTION_ID)?.toLongOrNull() == id } if (addressBookAccount != null) @@ -208,7 +212,7 @@ class LocalAddressBookStore @Inject constructor( */ fun getAddressBookAccounts(account: Account): List = AccountManager.get(context).let { accountManager -> - accountManager.getAccountsByType(context.getString(R.string.account_type_address_book)) + AccountHelper.getAllAddressBooks(accountManager) .filter { addressBookAccount -> account.name == accountManager.getUserData( addressBookAccount, diff --git a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalCalendarStore.kt b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalCalendarStore.kt index 21eb8edcc..3d32c843c 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalCalendarStore.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalCalendarStore.kt @@ -24,6 +24,7 @@ import at.bitfire.ical4android.util.DateUtils import at.bitfire.ical4android.util.MiscUtils.asSyncAdapter import at.bitfire.synctools.storage.calendar.AndroidCalendarProvider import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.AccountTypes import java.util.logging.Level import java.util.logging.Logger import javax.inject.Inject @@ -50,7 +51,7 @@ class LocalCalendarStore @Inject constructor( override fun create(client: ContentProviderClient, fromCollection: Collection): LocalCalendar? { val service = serviceRepository.getBlocking(fromCollection.serviceId) ?: throw IllegalArgumentException("Couldn't fetch DB service from collection") - val account = Account(service.accountName, context.getString(R.string.account_type)) + val account = Account(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) // If the collection doesn't have a color, use a default color. val collectionWithColor = @@ -138,7 +139,7 @@ class LocalCalendarStore @Inject constructor( return values } - override fun updateAccount(oldAccount: Account, newAccount: Account) { + override fun updateAccount(oldAccount: Account, newAccount: Account, accountType: String) { val values = contentValuesOf(Calendars.ACCOUNT_NAME to newAccount.name) val uri = Calendars.CONTENT_URI.asSyncAdapter(oldAccount) context.contentResolver.acquireContentProviderClient(CalendarContract.AUTHORITY)?.use { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalDataStore.kt b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalDataStore.kt index 8e8a7e4c4..5edac05f2 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalDataStore.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalDataStore.kt @@ -77,6 +77,6 @@ interface LocalDataStore> { * @param oldAccount The old account. * @param newAccount The new account. */ - fun updateAccount(oldAccount: Account, newAccount: Account) + fun updateAccount(oldAccount: Account, newAccount: Account, accountType: String) } \ No newline at end of file diff --git a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalJtxCollectionStore.kt b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalJtxCollectionStore.kt index 856af2de7..e802efbb8 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalJtxCollectionStore.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalJtxCollectionStore.kt @@ -11,7 +11,6 @@ import android.content.ContentValues import android.content.Context import androidx.core.content.contentValuesOf import at.bitfire.davdroid.Constants -import at.bitfire.davdroid.R import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.repository.PrincipalRepository @@ -22,6 +21,7 @@ import at.bitfire.ical4android.TaskProvider import at.techbee.jtx.JtxContract import at.techbee.jtx.JtxContract.asSyncAdapter import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.AccountTypes import java.util.logging.Logger import javax.inject.Inject @@ -48,7 +48,7 @@ class LocalJtxCollectionStore @Inject constructor( override fun create(provider: ContentProviderClient, fromCollection: Collection): LocalJtxCollection? { val service = serviceDao.get(fromCollection.serviceId) ?: throw IllegalArgumentException("Couldn't fetch DB service from collection") - val account = Account(service.accountName, context.getString(R.string.account_type)) + val account = Account(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) // If the collection doesn't have a color, use a default color. val collectionWithColor = @@ -103,7 +103,7 @@ class LocalJtxCollectionStore @Inject constructor( localCollection.update(values) } - override fun updateAccount(oldAccount: Account, newAccount: Account) { + override fun updateAccount(oldAccount: Account, newAccount: Account, accountType: String) { TaskProvider.acquire(context, TaskProvider.ProviderName.JtxBoard)?.use { provider -> val values = contentValuesOf(JtxContract.JtxCollection.ACCOUNT_NAME to newAccount.name) val uri = JtxContract.JtxCollection.CONTENT_URI.asSyncAdapter(oldAccount) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalTaskListStore.kt b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalTaskListStore.kt index 80bd8e12c..e061c1efc 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalTaskListStore.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/resource/LocalTaskListStore.kt @@ -23,6 +23,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.AccountTypes import org.dmfs.tasks.contract.TaskContract.TaskListColumns import org.dmfs.tasks.contract.TaskContract.TaskLists import org.dmfs.tasks.contract.TaskContract.Tasks @@ -58,7 +59,7 @@ class LocalTaskListStore @AssistedInject constructor( override fun create(provider: ContentProviderClient, fromCollection: Collection): LocalTaskList? { val service = serviceDao.get(fromCollection.serviceId) ?: throw IllegalArgumentException("Couldn't fetch DB service from collection") - val account = Account(service.accountName, context.getString(R.string.account_type)) + val account = Account(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) logger.log(Level.INFO, "Adding local task list", fromCollection) val uri = create(account, provider, providerName, fromCollection) @@ -109,7 +110,7 @@ class LocalTaskListStore @AssistedInject constructor( localCollection.update(valuesFromCollectionInfo(fromCollection, withColor = accountSettings.getManageCalendarColors())) } - override fun updateAccount(oldAccount: Account, newAccount: Account) { + override fun updateAccount(oldAccount: Account, newAccount: Account, accountType: String) { TaskProvider.acquire(context, providerName)?.use { provider -> val values = contentValuesOf(Tasks.ACCOUNT_NAME to newAccount.name) val uri = Tasks.getContentUri(providerName.authority) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt b/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt index 552f334f2..5d48e1da4 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt @@ -34,6 +34,7 @@ import at.bitfire.davdroid.ui.NotificationRegistry import at.bitfire.davdroid.ui.account.AccountSettingsActivity import dagger.assisted.Assisted import dagger.assisted.AssistedInject +import foundation.e.accountmanager.AccountTypes import kotlinx.coroutines.flow.map import kotlinx.coroutines.runInterruptible import java.util.logging.Level @@ -136,7 +137,7 @@ class RefreshCollectionsWorker @AssistedInject constructor( val serviceId: Long = inputData.getLong(ARG_SERVICE_ID, -1) val service = serviceRepository.getBlocking(serviceId) val account = service?.let { service -> - Account(service.accountName, applicationContext.getString(R.string.account_type)) + Account(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) } override suspend fun doWork(): Result { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt index d3ee0a82b..9021818aa 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt @@ -10,7 +10,6 @@ import android.os.Bundle import android.os.Looper import androidx.annotation.WorkerThread import androidx.core.os.bundleOf -import at.bitfire.davdroid.R import at.bitfire.davdroid.settings.AccountSettings.Companion.CREDENTIALS_LOCK import at.bitfire.davdroid.settings.AccountSettings.Companion.CREDENTIALS_LOCK_AT_LOGIN_AND_SETTINGS import at.bitfire.davdroid.settings.migration.AccountSettingsMigration @@ -24,6 +23,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.AccountTypes import net.openid.appauth.AuthState import java.util.Collections import java.util.logging.Level @@ -69,7 +69,7 @@ class AccountSettings @AssistedInject constructor( val accountManager: AccountManager = AccountManager.get(context) init { val allowedAccountTypes = arrayOf( - context.getString(R.string.account_type), + *AccountTypes.getAccountTypes().toTypedArray(), "at.bitfire.davdroid.test" // R.strings.account_type_test in androidTest ) if (!allowedAccountTypes.contains(account.type)) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration17.kt b/app/src/main/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration17.kt index 7c255ddad..48c6b3b88 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration17.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration17.kt @@ -8,7 +8,6 @@ import android.accounts.Account import android.accounts.AccountManager import android.content.Context import android.provider.ContactsContract -import at.bitfire.davdroid.R import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.repository.DavCollectionRepository import at.bitfire.davdroid.repository.DavServiceRepository @@ -22,6 +21,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import dagger.multibindings.IntKey import dagger.multibindings.IntoMap +import foundation.e.accountmanager.AccountTypes import kotlinx.coroutines.runBlocking import java.util.logging.Level import java.util.logging.Logger @@ -41,7 +41,11 @@ class AccountSettingsMigration17 @Inject constructor( ): AccountSettingsMigration { override fun migrate(account: Account) { - val addressBookAccountType = context.getString(R.string.account_type_address_book) + val addressBookAccountType = if (account.type in AccountTypes.getAddressBookTypes()) { + account.type + } else { + AccountTypes.getAddressBookForAccountType(account.type) + } try { context.contentResolver.acquireContentProviderClient(ContactsContract.AUTHORITY) } catch (e: SecurityException) { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration18.kt b/app/src/main/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration18.kt index a855460cb..b47de4544 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration18.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/settings/migration/AccountSettingsMigration18.kt @@ -7,7 +7,6 @@ package at.bitfire.davdroid.settings.migration import android.accounts.Account import android.accounts.AccountManager import android.content.Context -import at.bitfire.davdroid.R import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.db.Service import at.bitfire.davdroid.resource.LocalAddressBook @@ -19,6 +18,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import dagger.multibindings.IntKey import dagger.multibindings.IntoMap +import foundation.e.accountmanager.utils.AccountHelper import kotlinx.coroutines.runBlocking import javax.inject.Inject @@ -44,8 +44,7 @@ class AccountSettingsMigration18 @Inject constructor( db.serviceDao().getByAccountAndType(account.name, Service.TYPE_CARDDAV)?.let { service -> db.collectionDao().getByService(service.id).forEach { collection -> // Find associated address book account by collection ID (if it exists) - val addressBookAccount = accountManager - .getAccountsByType(context.getString(R.string.account_type_address_book)) + val addressBookAccount = AccountHelper.getAllAddressBooks(accountManager) .firstOrNull { accountManager.getUserData( it, diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/account/AccountsCleanupWorker.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/account/AccountsCleanupWorker.kt index 5278237e5..dab70f5c6 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/account/AccountsCleanupWorker.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/account/AccountsCleanupWorker.kt @@ -21,6 +21,7 @@ import at.bitfire.davdroid.resource.LocalAddressBook import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import foundation.e.accountmanager.utils.AccountHelper import java.time.Duration import java.util.concurrent.Semaphore import java.util.logging.Level @@ -77,7 +78,7 @@ class AccountsCleanupWorker @AssistedInject constructor( @VisibleForTesting internal fun cleanUpAddressBooks() { val accounts = accountRepository.getAll() - for (addressBookAccount in accountManager.getAccountsByType(context.getString(R.string.account_type_address_book))) { + for (addressBookAccount in AccountHelper.getAllAddressBooks(accountManager)) { val accountName = accountManager.getUserData(addressBookAccount, LocalAddressBook.USER_DATA_ACCOUNT_NAME) val accountType = accountManager.getUserData(addressBookAccount, LocalAddressBook.USER_DATA_ACCOUNT_TYPE) if (!accounts.any { it.name == accountName && it.type == accountType }) { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncAdapterImpl.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncAdapterImpl.kt index 9a5666029..2b956065d 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncAdapterImpl.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncAdapterImpl.kt @@ -31,6 +31,7 @@ import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent +import foundation.e.accountmanager.AccountTypes import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -80,14 +81,14 @@ class SyncAdapterImpl @Inject constructor( logger.info("Sync request via sync framework for $accountOrAddressBookAccount $authority (upload=$upload)") // If we should sync an address book account - find the account storing the settings - val account = if (accountOrAddressBookAccount.type == context.getString(R.string.account_type_address_book)) + val account = if (accountOrAddressBookAccount.type in AccountTypes.getAddressBookTypes()) AccountManager.get(context) .getUserData(accountOrAddressBookAccount, LocalAddressBook.USER_DATA_COLLECTION_ID) ?.toLongOrNull() ?.let { collectionId -> collectionRepository.get(collectionId)?.let { collection -> serviceRepository.getBlocking(collection.serviceId)?.let { service -> - Account(service.accountName, context.getString(R.string.account_type)) + Account(service.accountName, AccountTypes.getAccountTypeForAddressBook(accountOrAddressBookAccount.type)) } } } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/CollectionSelectedUseCase.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/CollectionSelectedUseCase.kt index 631ba4f52..7452b3bc8 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/CollectionSelectedUseCase.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/CollectionSelectedUseCase.kt @@ -13,6 +13,7 @@ import at.bitfire.davdroid.repository.DavCollectionRepository import at.bitfire.davdroid.repository.DavServiceRepository import at.bitfire.davdroid.sync.worker.SyncWorkerManager import at.bitfire.davdroid.ui.CollectionSelectedUseCase.Companion.DELAY_MS +import foundation.e.accountmanager.AccountTypes import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -49,7 +50,7 @@ class CollectionSelectedUseCase @Inject constructor( suspend fun handleWithDelay(collectionId: Long) { val collection = collectionRepository.getAsync(collectionId) ?: return val service = serviceRepository.get(collection.serviceId) ?: return - val account = accountRepository.fromName(service.accountName) + val account = accountRepository.fromName(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) // Atomically cancel, launch and remember delay coroutine of given account delayJobs.compute(account) { _, previousJob -> diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/DebugInfoGenerator.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/DebugInfoGenerator.kt index e3df6c3e9..a0313f050 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/DebugInfoGenerator.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/DebugInfoGenerator.kt @@ -33,7 +33,6 @@ import androidx.work.WorkManager import androidx.work.WorkQuery import at.bitfire.dav4jvm.exception.DavException import at.bitfire.davdroid.BuildConfig -import at.bitfire.davdroid.R import at.bitfire.davdroid.TextTable import at.bitfire.davdroid.db.AppDatabase import at.bitfire.davdroid.repository.AccountRepository @@ -47,6 +46,7 @@ import at.bitfire.davdroid.sync.worker.BaseSyncWorker import at.bitfire.ical4android.TaskProvider import at.techbee.jtx.JtxContract import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.utils.AccountHelper import org.dmfs.tasks.contract.TaskContract import java.io.PrintWriter import java.io.Writer @@ -314,7 +314,7 @@ class DebugInfoGenerator @Inject constructor( for (account in accounts) dumpAccount(account, writer) - val addressBookAccounts = accountManager.getAccountsByType(context.getString(R.string.account_type_address_book)).toMutableList() + val addressBookAccounts = AccountHelper.getAllAddressBooks(accountManager).toMutableList() if (addressBookAccounts.isNotEmpty()) { writer.append("ADDRESS BOOK ACCOUNTS\n\n") for (account in addressBookAccounts) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountActivity.kt index 2ba465926..d60f8bd5c 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountActivity.kt @@ -6,14 +6,15 @@ package at.bitfire.davdroid.ui.account import AccountScreen import android.accounts.Account +import android.accounts.AccountManager import android.content.Intent import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.core.content.IntentCompat -import at.bitfire.davdroid.R import at.bitfire.davdroid.ui.AccountsActivity import dagger.hilt.android.AndroidEntryPoint +import foundation.e.accountmanager.AccountTypes import java.util.logging.Logger import javax.inject.Inject @@ -28,7 +29,9 @@ class AccountActivity : AppCompatActivity() { val account = IntentCompat.getParcelableExtra(intent, EXTRA_ACCOUNT, Account::class.java) ?: - intent.getStringExtra(EXTRA_ACCOUNT)?.let { Account(it, getString(R.string.account_type)) } + intent.getStringExtra(EXTRA_ACCOUNT)?.let { + Account(it, AccountTypes.Default.accountType) + } // If account is not passed, log warning and redirect to accounts overview if (account == null) { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountScreenModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountScreenModel.kt index 9e1607aba..ac697c719 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountScreenModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountScreenModel.kt @@ -11,7 +11,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import at.bitfire.davdroid.R import at.bitfire.davdroid.db.Collection import at.bitfire.davdroid.repository.AccountRepository import at.bitfire.davdroid.repository.DavCollectionRepository @@ -162,7 +161,7 @@ class AccountScreenModel @AssistedInject constructor( /** Deletes the account from the system (won't touch collections on the server). */ fun deleteAccount() { viewModelScope.launch { - accountRepository.delete(account.name) + accountRepository.delete(account.name, account.type) } } @@ -183,10 +182,10 @@ class AccountScreenModel @AssistedInject constructor( fun renameAccount(newName: String) { viewModelScope.launch { try { - accountRepository.rename(account.name, newName) + accountRepository.rename(account.type, account.name, newName) // synchronize again - val newAccount = Account(newName, context.getString(R.string.account_type)) + val newAccount = Account(newName, account.type) syncWorkerManager.enqueueOneTimeAllAuthorities(newAccount, manual = true) } catch (e: Exception) { logger.log(Level.SEVERE, "Couldn't rename account", e) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/CollectionSelectedUseCase.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/CollectionSelectedUseCase.kt index 5ee54276c..247f173cb 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/CollectionSelectedUseCase.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/CollectionSelectedUseCase.kt @@ -12,6 +12,7 @@ import at.bitfire.davdroid.repository.DavCollectionRepository import at.bitfire.davdroid.repository.DavServiceRepository import at.bitfire.davdroid.sync.worker.SyncWorkerManager import at.bitfire.davdroid.ui.account.CollectionSelectedUseCase.Companion.DELAY_MS +import foundation.e.accountmanager.AccountTypes import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -53,7 +54,7 @@ class CollectionSelectedUseCase @Inject constructor( suspend fun handleWithDelay(collectionId: Long) { val collection = collectionRepository.getAsync(collectionId) ?: return val service = serviceRepository.get(collection.serviceId) ?: return - val account = accountRepository.fromName(service.accountName) + val account = accountRepository.fromName(service.accountName, AccountTypes.getAccountTypeFromPrincipal(service.principal)) // Atomically cancel, launch and remember delay coroutine of given account delayJobs.compute(account) { _, previousJob -> diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsPage.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsPage.kt index 3260b1ef9..bb2c319a1 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsPage.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/AccountDetailsPage.kt @@ -68,7 +68,7 @@ fun AccountDetailsPage( accountName = uiState.accountName, suggestedAccountNames = uiState.suggestedAccountNames, accountNameAlreadyExists = uiState.accountNameExists, - onUpdateAccountName = { model.updateAccountName(it) }, + onUpdateAccountName = { model.updateAccountName(it, uiState.accountType) }, showApostropheWarning = uiState.showApostropheWarning, groupMethod = uiState.groupMethod, groupMethodReadOnly = uiState.groupMethodReadOnly, diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginScreenModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginScreenModel.kt index 49ab78c6a..fc3a2690a 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginScreenModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginScreenModel.kt @@ -103,7 +103,8 @@ class LoginScreenModel @AssistedInject constructor( ?: loginInfo.credentials?.username ?: loginInfo.baseUri?.host ?: "" - updateAccountNameAndEmails(initialAccountName, emails) + val accountType = loginDetailsUiState.loginType.accountType + updateAccountNameAndEmails(initialAccountName, accountType, emails) updateGroupMethod(loginInfo.suggestedGroupMethod) page = Page.AccountDetails } @@ -223,6 +224,7 @@ class LoginScreenModel @AssistedInject constructor( data class AccountDetailsUiState( val accountName: String = "", + val accountType: String = "", val suggestedAccountNames: Set = emptySet(), val accountNameExists: Boolean = false, val groupMethod: GroupMethod = GroupMethod.GROUP_VCARDS, @@ -262,20 +264,22 @@ class LoginScreenModel @AssistedInject constructor( combinedState }.stateIn(viewModelScope, SharingStarted.Lazily, _accountDetailsUiState.value) - fun updateAccountName(accountName: String) { + fun updateAccountName(accountName: String, accountType: String) { _accountDetailsUiState.update { currentState -> currentState.copy( accountName = accountName, - accountNameExists = accountRepository.exists(accountName) + accountType = accountType, + accountNameExists = accountRepository.exists(accountName, accountType) ) } } - fun updateAccountNameAndEmails(accountName: String, emails: Set) { + fun updateAccountNameAndEmails(accountName: String, accountType: String, emails: Set) { _accountDetailsUiState.update { currentState -> currentState.copy( accountName = accountName, - accountNameExists = accountRepository.exists(accountName), + accountType = accountType, + accountNameExists = accountRepository.exists(accountName, accountType), suggestedAccountNames = emails ) } @@ -302,6 +306,7 @@ class LoginScreenModel @AssistedInject constructor( val account = withContext(Dispatchers.Default) { accountRepository.createBlocking( accountDetailsUiState.value.accountName, + accountDetailsUiState.value.accountType, loginInfo.credentials, foundConfig!!, accountDetailsUiState.value.groupMethod diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginType.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginType.kt index 88de99615..174df33aa 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginType.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/setup/LoginType.kt @@ -7,6 +7,7 @@ package at.bitfire.davdroid.ui.setup import android.net.Uri import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable +import foundation.e.accountmanager.AccountTypes interface LoginType { @@ -15,6 +16,9 @@ interface LoginType { /** Optional URL to a provider-specific help page. */ val helpUrl: Uri? + val accountType: String + get() = AccountTypes.Default.accountType + @Composable fun LoginScreen( snackbarHostState: SnackbarHostState, diff --git a/app/src/main/kotlin/foundation/e/accountmanager/AccountTypes.kt b/app/src/main/kotlin/foundation/e/accountmanager/AccountTypes.kt new file mode 100644 index 000000000..c75787ca4 --- /dev/null +++ b/app/src/main/kotlin/foundation/e/accountmanager/AccountTypes.kt @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2025 eFoundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package foundation.e.accountmanager + +import okhttp3.HttpUrl + +enum class AccountTypes( + val accountType: String, + val addressBookType: String, + val whitelistedDomains: List = emptyList() +) { + // Duplicated in res/values/strings.xml because we can’t access Context in enums. + // Update the values in res/values/strings.xml as well. + Default( + accountType = "bitfire.at.davdroid", // R.string.account_type + addressBookType = "at.bitfire.davdroid.address_book", // R.string.account_type_address_book + ); + + companion object { + fun getAccountTypes(): Set = + entries.map { it.accountType }.toSet() + + fun getAddressBookTypes(): Set = + entries.map { it.addressBookType }.toSet() + + fun getAccountTypeForAddressBook(addressBookType: String): String = + entries.find { + it.addressBookType == addressBookType + }?.accountType ?: Default.accountType + + fun getAddressBookForAccountType(accountType: String): String = + entries.find { + it.accountType == accountType + }?.addressBookType ?: Default.addressBookType + + fun getAccountTypeFromPrincipal(principal: HttpUrl?): String { + if (principal == null) { + return Default.accountType + } + + return AccountTypes.entries.firstOrNull { + it.whitelistedDomains.contains(principal.host) + }?.accountType ?: Default.accountType + } + + fun getAddressBookTypeFromPrincipal(principal: HttpUrl?): String { + val accountType = getAccountTypeFromPrincipal(principal) + return AccountTypes.getAddressBookForAccountType(accountType) + } + } +} diff --git a/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt b/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt new file mode 100644 index 000000000..ba108be59 --- /dev/null +++ b/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2025 eFoundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +package foundation.e.accountmanager.utils + +import android.accounts.Account +import android.accounts.AccountManager +import foundation.e.accountmanager.AccountTypes + +object AccountHelper { + fun getAllAccounts(accountManager: AccountManager): Array { + val allAccounts = mutableListOf() + for (type in AccountTypes.getAccountTypes()) { + allAccounts += accountManager.getAccountsByType(type).asList() + } + return allAccounts.toTypedArray() + } + + fun getAllAddressBooks(accountManager: AccountManager): Array { + val allAccounts = mutableListOf() + for (type in AccountTypes.getAddressBookTypes()) { + allAccounts += accountManager.getAccountsByType(type).asList() + } + return allAccounts.toTypedArray() + } +} diff --git a/app/src/main/kotlin/foundation/e/accountmanager/utils/SystemUtils.kt b/app/src/main/kotlin/foundation/e/accountmanager/utils/SystemUtils.kt index cf1b0a154..89e31b10c 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/utils/SystemUtils.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/utils/SystemUtils.kt @@ -1,7 +1,20 @@ /* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * Copyright (C) 2025 eFoundation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * */ - package foundation.e.accountmanager.utils import android.content.Context -- GitLab