Skip to content

Commit

Permalink
Make Collection room entity fully immutable
Browse files Browse the repository at this point in the history
  • Loading branch information
sunkup committed Jan 8, 2025
1 parent 0985d5a commit 1c35b17
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 61 deletions.
12 changes: 6 additions & 6 deletions app/src/main/kotlin/at/bitfire/davdroid/db/Collection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,18 @@ data class Collection(
* Service, which this collection belongs to. Services are unique, so a [Collection] is uniquely
* identifiable via its [serviceId] and [url].
*/
var serviceId: Long = 0,
val serviceId: Long = 0,

/**
* A home set this collection belongs to. Multiple homesets are not supported.
* If *null* the collection is considered homeless.
*/
var homeSetId: Long? = null,
val homeSetId: Long? = null,

/**
* Principal who is owner of this collection.
*/
var ownerId: Long? = null,
val ownerId: Long? = null,

/**
* Type of service. CalDAV or CardDAV
Expand All @@ -87,7 +87,7 @@ data class Collection(
* Whether the user has manually set the "force read-only" flag.
* Even if this flag is not set, there may still be other reasons why a collection is effectively read-only.
*/
var forceReadOnly: Boolean = false,
val forceReadOnly: Boolean = false,

/**
* Human-readable name of the collection
Expand All @@ -99,7 +99,7 @@ data class Collection(
val description: String? = null,

// CalDAV only
var color: Int? = null,
val color: Int? = null,

/** default timezone (only timezone ID, like `Europe/Vienna`) */
val timezoneId: String? = null,
Expand All @@ -117,7 +117,7 @@ data class Collection(
val source: HttpUrl? = null,

/** whether this collection has been selected for synchronization */
var sync: Boolean = false,
val sync: Boolean = false,

/** WebDAV-Push topic */
val pushTopic: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,8 @@ import at.bitfire.davdroid.resource.LocalTaskList
import at.bitfire.davdroid.servicedetection.DavResourceFinder
import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker
import at.bitfire.davdroid.settings.AccountSettings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.sync.AutomaticSyncManager
import at.bitfire.davdroid.sync.SyncDataType
import at.bitfire.davdroid.sync.TasksAppManager
import at.bitfire.davdroid.sync.account.AccountsCleanupWorker
import at.bitfire.davdroid.sync.account.SystemAccountUtils
import at.bitfire.davdroid.sync.worker.SyncWorkerManager
Expand Down Expand Up @@ -50,10 +48,8 @@ class AccountRepository @Inject constructor(
private val homeSetRepository: DavHomeSetRepository,
private val localAddressBookStore: Lazy<LocalAddressBookStore>,
private val logger: Logger,
private val settingsManager: SettingsManager,
private val serviceRepository: DavServiceRepository,
private val syncWorkerManager: SyncWorkerManager,
private val tasksAppManager: Lazy<TasksAppManager>
) {

private val accountType = context.getString(R.string.account_type)
Expand Down Expand Up @@ -251,8 +247,7 @@ class AccountRepository @Inject constructor(

// insert collections
for (collection in info.collections.values) {
collection.serviceId = serviceId
collectionRepository.insertOrUpdateByUrl(collection)
collectionRepository.insertOrUpdateByUrl(collection.copy(serviceId = serviceId))
}

return serviceId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,13 @@ class DavCollectionRepository @Inject constructor(
*/
fun insertOrUpdateByUrlAndRememberFlags(newCollection: Collection) {
// remember locally set flags
dao.getByServiceAndUrl(newCollection.serviceId, newCollection.url.toString())?.let { oldCollection ->
newCollection.sync = oldCollection.sync
newCollection.forceReadOnly = oldCollection.forceReadOnly
val oldCollection = dao.getByServiceAndUrl(newCollection.serviceId, newCollection.url.toString())
val newCollectionWithOldFlags = oldCollection?.let {
newCollection.copy(sync = it.sync, forceReadOnly = it.forceReadOnly)
}

// commit to database
insertOrUpdateByUrl(newCollection)
// commit new collection to database
insertOrUpdateByUrl(newCollectionWithOldFlags ?: newCollection)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ class LocalCalendarStore @Inject constructor(
val service = serviceRepository.get(fromCollection.serviceId) ?: throw IllegalArgumentException("Couldn't fetch DB service from collection")
val account = Account(service.accountName, context.getString(R.string.account_type))

// If the collection doesn't have a color, use a default color.
if (fromCollection.color != null)
fromCollection.color = Constants.DAVDROID_GREEN_RGBA

val values = valuesFromCollectionInfo(fromCollection, withColor = true)
val values = valuesFromCollectionInfo(
// If the collection doesn't have a color, use a default color.
info = fromCollection.copy(color = fromCollection.color ?: Constants.DAVDROID_GREEN_RGBA),
withColor = true
)

// ACCOUNT_NAME and ACCOUNT_TYPE are required (see docs)! If it's missing, other apps will crash.
values.put(Calendars.ACCOUNT_NAME, account.name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ class LocalJtxCollectionStore @Inject constructor(
private val serviceDao = db.serviceDao()

override fun create(provider: ContentProviderClient, fromCollection: Collection): LocalJtxCollection? {
// If the collection doesn't have a color, use a default color.
if (fromCollection.color != null)
fromCollection.color = Constants.DAVDROID_GREEN_RGBA

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 values = valuesFromCollection(fromCollection, account = account, withColor = true)
val values = valuesFromCollection(
// If the collection doesn't have a color, use a default color.
info = fromCollection.copy(color = fromCollection.color ?: Constants.DAVDROID_GREEN_RGBA),
account = account,
withColor = true
)

val uri = JtxCollection.create(account, provider, values)
return LocalJtxCollection(account, provider, ContentUris.parseId(uri))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ class LocalTaskListStore @AssistedInject constructor(
}

private fun create(account: Account, provider: ContentProviderClient, providerName: TaskProvider.ProviderName, info: Collection): Uri {
// If the collection doesn't have a color, use a default color.
if (info.color != null)
info.color = Constants.DAVDROID_GREEN_RGBA

val values = valuesFromCollectionInfo(info, withColor = true)
val values = valuesFromCollectionInfo(
// If the collection doesn't have a color, use a default color.
info = info.copy(color = info.color ?: Constants.DAVDROID_GREEN_RGBA),
withColor = true
)
values.put(TaskLists.OWNER, account.name)
values.put(TaskLists.SYNC_ENABLED, 1)
values.put(TaskLists.VISIBLE, 1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,28 +192,25 @@ class CollectionListRefresher @AssistedInject constructor(
return@propfind

if (relation == Response.HrefRelation.SELF) {
// this response is about the homeset itself
// this response is about the home set itself
homeSetRepository.insertOrUpdateByUrl(localHomeset.copy(
displayName = response[DisplayName::class.java]?.displayName,
privBind = response[CurrentUserPrivilegeSet::class.java]?.mayBind ?: true
))
}

// in any case, check whether the response is about a usable collection
val collection = Collection.fromDavResponse(response) ?: return@propfind

collection.serviceId = service.id
collection.homeSetId = localHomeset.id
collection.sync = shouldPreselect(collection, homesets.values)

// .. and save the principal url (collection owner)
response[Owner::class.java]?.href
?.let { response.href.resolve(it) }
?.let { principalUrl ->
val principal = Principal.fromServiceAndUrl(service, principalUrl)
val id = db.principalDao().insertOrUpdate(service.id, principal)
collection.ownerId = id
}
var collection = Collection.fromDavResponse(response)?: return@propfind

collection = collection.copy(
serviceId = service.id,
homeSetId = localHomeset.id,
sync = shouldPreselect(collection, homesets.values),
ownerId = response[Owner::class.java]?.href // save the principal id (collection owner)
?.let { response.href.resolve(it) }
?.let { principalUrl -> Principal.fromServiceAndUrl(service, principalUrl) }
?.let { principal -> db.principalDao().insertOrUpdate(service.id, principal) }
)

logger.log(Level.FINE, "Found collection", collection)

Expand All @@ -232,8 +229,9 @@ class CollectionListRefresher @AssistedInject constructor(

// Mark leftover (not rediscovered) collections from queue as homeless (remove association)
for ((_, homelessCollection) in localHomesetCollections) {
homelessCollection.homeSetId = null
collectionRepository.insertOrUpdateByUrlAndRememberFlags(homelessCollection)
collectionRepository.insertOrUpdateByUrlAndRememberFlags(
homelessCollection.copy(homeSetId = null)
)
}

}
Expand All @@ -257,18 +255,13 @@ class CollectionListRefresher @AssistedInject constructor(
Collection.fromDavResponse(response)?.let { collection ->
if (!isUsableCollection(collection))
return@let
collection.serviceId = localCollection.serviceId // use same service ID as previous entry

// .. and save the principal url (collection owner)
response[Owner::class.java]?.href
?.let { response.href.resolve(it) }
?.let { principalUrl ->
val principal = Principal.fromServiceAndUrl(service, principalUrl)
val principalId = db.principalDao().insertOrUpdate(service.id, principal)
collection.ownerId = principalId
}

collectionRepository.insertOrUpdateByUrlAndRememberFlags(collection)
collectionRepository.insertOrUpdateByUrlAndRememberFlags(collection.copy(
serviceId = localCollection.serviceId, // use same service ID as previous entry
ownerId = response[Owner::class.java]?.href // save the principal id (collection owner)
?.let { response.href.resolve(it) }
?.let { principalUrl -> Principal.fromServiceAndUrl(service, principalUrl) }
?.let { principal -> db.principalDao().insertOrUpdate(service.id, principal) }
))
} ?: collectionRepository.delete(localCollection)
}
} catch (e: HttpException) {
Expand Down

0 comments on commit 1c35b17

Please sign in to comment.