From bcf35c868d56b84bdd83ab8e5eb26ce27dcb13f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Wed, 21 Dec 2022 20:08:35 +0100 Subject: [PATCH 01/20] Introduce spaces skeleton (Model, Repository, DataSources..) --- owncloud-android-library | 2 +- .../LocalDataSourceModule.kt | 3 + .../RemoteDataSourceModule.kt | 8 +- .../dependecyinjection/RepositoryModule.kt | 3 + .../dependecyinjection/UseCaseModule.kt | 6 +- .../datasources/LocalSpacesDataSource.kt | 25 +++++ .../datasources/RemoteSpacesDataSource.kt | 25 +++++ .../implementation/OCLocalSpacesDataSource.kt | 30 ++++++ .../OCRemoteSpacesDataSource.kt | 93 +++++++++++++++++++ .../spaces/repository/OCSpacesRepository.kt | 34 +++++++ .../android/domain/spaces/SpacesRepository.kt | 23 +++++ .../android/domain/spaces/model/OCSpace.kt | 91 ++++++++++++++++++ .../RefreshSpacesFromServerAsyncUseCase.kt | 30 ++++++ 13 files changed, 370 insertions(+), 3 deletions(-) create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/LocalSpacesDataSource.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt diff --git a/owncloud-android-library b/owncloud-android-library index 8a89edb5bea..21969fa98c7 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit 8a89edb5bea43abc1228323ef74f99556153f20c +Subproject commit 21969fa98c72b1f9fa3336ffa2231aa018ba4961 diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt index 8654a6a49d0..457731ec2d1 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt @@ -38,6 +38,8 @@ import com.owncloud.android.data.preferences.datasources.SharedPreferencesProvid import com.owncloud.android.data.preferences.datasources.implementation.OCSharedPreferencesProvider import com.owncloud.android.data.sharing.shares.datasources.LocalShareDataSource import com.owncloud.android.data.sharing.shares.datasources.implementation.OCLocalShareDataSource +import com.owncloud.android.data.spaces.datasources.LocalSpacesDataSource +import com.owncloud.android.data.spaces.datasources.implementation.OCLocalSpacesDataSource import com.owncloud.android.data.storage.LocalStorageProvider import com.owncloud.android.data.storage.ScopedStorageProvider import com.owncloud.android.data.transfers.datasources.LocalTransferDataSource @@ -67,4 +69,5 @@ val localDataSourceModule = module { factory { OCLocalUserDataSource(get()) } factory { OCFolderBackupLocalDataSource(get()) } factory { OCLocalTransferDataSource(get()) } + factory { OCLocalSpacesDataSource() } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt index 00ef7477197..80337e63481 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RemoteDataSourceModule.kt @@ -21,7 +21,6 @@ package com.owncloud.android.dependecyinjection import com.owncloud.android.MainApp import com.owncloud.android.R -import com.owncloud.android.presentation.authentication.AccountUtils import com.owncloud.android.data.ClientManager import com.owncloud.android.data.authentication.datasources.RemoteAuthenticationDataSource import com.owncloud.android.data.authentication.datasources.implementation.OCRemoteAuthenticationDataSource @@ -40,6 +39,8 @@ import com.owncloud.android.data.sharing.sharees.datasources.mapper.RemoteSharee import com.owncloud.android.data.sharing.shares.datasources.RemoteShareDataSource import com.owncloud.android.data.sharing.shares.datasources.implementation.OCRemoteShareDataSource import com.owncloud.android.data.sharing.shares.datasources.mapper.RemoteShareMapper +import com.owncloud.android.data.spaces.datasources.RemoteSpacesDataSource +import com.owncloud.android.data.spaces.datasources.implementation.OCRemoteSpacesDataSource import com.owncloud.android.data.user.datasources.RemoteUserDataSource import com.owncloud.android.data.user.datasources.implementation.OCRemoteUserDataSource import com.owncloud.android.data.webfinger.datasources.WebfingerRemoteDatasource @@ -57,12 +58,15 @@ import com.owncloud.android.lib.resources.shares.services.ShareService import com.owncloud.android.lib.resources.shares.services.ShareeService import com.owncloud.android.lib.resources.shares.services.implementation.OCShareService import com.owncloud.android.lib.resources.shares.services.implementation.OCShareeService +import com.owncloud.android.lib.resources.spaces.services.OCSpacesService +import com.owncloud.android.lib.resources.spaces.services.SpacesService import com.owncloud.android.lib.resources.status.services.CapabilityService import com.owncloud.android.lib.resources.status.services.ServerInfoService import com.owncloud.android.lib.resources.status.services.implementation.OCCapabilityService import com.owncloud.android.lib.resources.status.services.implementation.OCServerInfoService import com.owncloud.android.lib.resources.webfinger.services.WebfingerService import com.owncloud.android.lib.resources.webfinger.services.implementation.OCWebfingerService +import com.owncloud.android.presentation.authentication.AccountUtils import org.koin.android.ext.koin.androidContext import org.koin.dsl.module @@ -81,6 +85,7 @@ val remoteDataSourceModule = module { single { OCOIDCService() } single { OCShareService(get()) } single { OCShareeService(get()) } + single { OCSpacesService(get()) } single { OCWebfingerService() } factory { OCRemoteAuthenticationDataSource(get()) } @@ -90,6 +95,7 @@ val remoteDataSourceModule = module { factory { OCRemoteServerInfoDataSource(get(), get()) } factory { OCRemoteShareDataSource(get(), get()) } factory { OCRemoteShareeDataSource(get(), get()) } + factory { OCRemoteSpacesDataSource(get()) } factory { OCRemoteUserDataSource(get(), androidContext().resources.getDimension(R.dimen.file_avatar_size).toInt()) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt index 73f251b80cd..96f7e630718 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt @@ -30,6 +30,7 @@ import com.owncloud.android.data.oauth.repository.OCOAuthRepository import com.owncloud.android.data.server.repository.OCServerInfoRepository import com.owncloud.android.data.sharing.sharees.repository.OCShareeRepository import com.owncloud.android.data.sharing.shares.repository.OCShareRepository +import com.owncloud.android.data.spaces.repository.OCSpacesRepository import com.owncloud.android.data.transfers.repository.OCTransferRepository import com.owncloud.android.data.user.repository.OCUserRepository import com.owncloud.android.data.webfinger.repository.OCWebfingerRepository @@ -41,6 +42,7 @@ import com.owncloud.android.domain.files.FileRepository import com.owncloud.android.domain.server.ServerInfoRepository import com.owncloud.android.domain.sharing.sharees.ShareeRepository import com.owncloud.android.domain.sharing.shares.ShareRepository +import com.owncloud.android.domain.spaces.SpacesRepository import com.owncloud.android.domain.transfers.TransferRepository import com.owncloud.android.domain.user.UserRepository import com.owncloud.android.domain.webfinger.WebfingerRepository @@ -53,6 +55,7 @@ val repositoryModule = module { factory { OCServerInfoRepository(get()) } factory { OCShareRepository(get(), get()) } factory { OCShareeRepository(get()) } + factory { OCSpacesRepository(get(), get()) } factory { OCUserRepository(get(), get()) } factory { OCOAuthRepository(get()) } factory { OCFolderBackupRepository(get()) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt index 6caf2ef91da..5a074c5c706 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -77,6 +77,7 @@ import com.owncloud.android.domain.sharing.shares.usecases.EditPublicShareAsyncU import com.owncloud.android.domain.sharing.shares.usecases.GetShareAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.GetSharesAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.RefreshSharesFromServerAsyncUseCase +import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase import com.owncloud.android.domain.transfers.usecases.ClearSuccessfulTransfersUseCase import com.owncloud.android.domain.transfers.usecases.GetAllTransfersAsLiveDataUseCase import com.owncloud.android.domain.transfers.usecases.GetAllTransfersUseCase @@ -91,10 +92,10 @@ import com.owncloud.android.usecases.accounts.RemoveAccountUseCase import com.owncloud.android.usecases.synchronization.SynchronizeFileUseCase import com.owncloud.android.usecases.synchronization.SynchronizeFolderUseCase import com.owncloud.android.usecases.transfers.downloads.CancelDownloadForFileUseCase +import com.owncloud.android.usecases.transfers.downloads.CancelDownloadsRecursivelyUseCase import com.owncloud.android.usecases.transfers.downloads.DownloadFileUseCase import com.owncloud.android.usecases.transfers.downloads.GetLiveDataForDownloadingFileUseCase import com.owncloud.android.usecases.transfers.downloads.GetLiveDataForFinishedDownloadsFromAccountUseCase -import com.owncloud.android.usecases.transfers.downloads.CancelDownloadsRecursivelyUseCase import com.owncloud.android.usecases.transfers.uploads.CancelTransfersFromAccountUseCase import com.owncloud.android.usecases.transfers.uploads.CancelUploadForFileUseCase import com.owncloud.android.usecases.transfers.uploads.CancelUploadUseCase @@ -172,6 +173,9 @@ val useCaseModule = module { factory { GetSharesAsLiveDataUseCase(get()) } factory { RefreshSharesFromServerAsyncUseCase(get()) } + // Spaces + factory { RefreshSpacesFromServerAsyncUseCase(get()) } + // Transfers factory { CancelDownloadForFileUseCase(get()) } factory { CancelDownloadsRecursivelyUseCase(get(), get()) } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/LocalSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/LocalSpacesDataSource.kt new file mode 100644 index 00000000000..ec34eb73b4c --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/LocalSpacesDataSource.kt @@ -0,0 +1,25 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.data.spaces.datasources + +import com.owncloud.android.domain.spaces.model.OCSpace + +interface LocalSpacesDataSource { + fun saveSpacesForAccount(listOfSpaces: List) +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt new file mode 100644 index 00000000000..5152b53c5b7 --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt @@ -0,0 +1,25 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.data.spaces.datasources + +import com.owncloud.android.domain.spaces.model.OCSpace + +interface RemoteSpacesDataSource { + fun refreshSpacesForAccount(): List +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt new file mode 100644 index 00000000000..7e432419b73 --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt @@ -0,0 +1,30 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.data.spaces.datasources.implementation + +import com.owncloud.android.data.spaces.datasources.LocalSpacesDataSource +import com.owncloud.android.domain.spaces.model.OCSpace + +class OCLocalSpacesDataSource( + +): LocalSpacesDataSource { + override fun saveSpacesForAccount(listOfSpaces: List) { + TODO("Not yet implemented") + } +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt new file mode 100644 index 00000000000..20e473b9268 --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt @@ -0,0 +1,93 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.data.spaces.datasources.implementation + +import com.owncloud.android.data.executeRemoteOperation +import com.owncloud.android.data.spaces.datasources.RemoteSpacesDataSource +import com.owncloud.android.domain.spaces.model.File +import com.owncloud.android.domain.spaces.model.OCSpace +import com.owncloud.android.domain.spaces.model.SpaceGrantedTo +import com.owncloud.android.domain.spaces.model.SpaceOwner +import com.owncloud.android.domain.spaces.model.SpacePermission +import com.owncloud.android.domain.spaces.model.SpaceQuota +import com.owncloud.android.domain.spaces.model.SpaceRoot +import com.owncloud.android.domain.spaces.model.SpaceSpecial +import com.owncloud.android.domain.spaces.model.SpaceSpecialFolder +import com.owncloud.android.domain.spaces.model.SpaceUser +import com.owncloud.android.lib.resources.spaces.responses.SpaceResponse +import com.owncloud.android.lib.resources.spaces.services.SpacesService + +class OCRemoteSpacesDataSource( + private val spacesService: SpacesService +) : RemoteSpacesDataSource { + override fun refreshSpacesForAccount(): List { + val spacesResponse = executeRemoteOperation { + spacesService.getSpaces() + } + + return spacesResponse.map { it.toModel() } + } + + private fun SpaceResponse.toModel(): OCSpace = + OCSpace( + driveAlias = driveAlias, + driveType = driveType, + id = id, + lastModifiedDateTime = lastModifiedDateTime, + name = name, + owner = SpaceOwner( + user = SpaceUser( + id = owner.user.id + ) + ), + quota = SpaceQuota( + remaining = quota.remaining, + state = quota.state, + total = quota.total, + used = quota.used + ), + root = SpaceRoot( + eTag = root.eTag, + id = root.id, + permissions = root.permissions?.map { permissionsResponse -> + SpacePermission( + grantedTo = permissionsResponse.grantedTo.map { grantedToResponse -> + SpaceGrantedTo(SpaceUser(grantedToResponse.user.id)) + }, + roles = permissionsResponse.roles, + ) + }, + webDavUrl = root.webDavUrl + ), + webUrl = webUrl, + description = description, + special = special?.map { specialResponse -> + SpaceSpecial( + eTag = specialResponse.eTag, + file = File(mimeType = specialResponse.file.mimeType), + id = specialResponse.id, + lastModifiedDateTime = specialResponse.lastModifiedDateTime, + name = specialResponse.name, + size = specialResponse.size, + specialFolder = SpaceSpecialFolder(name = specialResponse.specialFolder.name), + webDavUrl = specialResponse.webDavUrl + ) + } + ) +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt new file mode 100644 index 00000000000..cfe837932ea --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt @@ -0,0 +1,34 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.data.spaces.repository + +import com.owncloud.android.data.spaces.datasources.LocalSpacesDataSource +import com.owncloud.android.data.spaces.datasources.RemoteSpacesDataSource +import com.owncloud.android.domain.spaces.SpacesRepository + +class OCSpacesRepository( + private val localSpacesDataSource: LocalSpacesDataSource, + private val remoteSpacesDataSource: RemoteSpacesDataSource, +) : SpacesRepository { + override fun refreshSpacesForAccount() { + remoteSpacesDataSource.refreshSpacesForAccount().also { listOfSpaces -> + localSpacesDataSource.saveSpacesForAccount(listOfSpaces) + } + } +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt new file mode 100644 index 00000000000..779d7a815ac --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt @@ -0,0 +1,23 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.domain.spaces + +interface SpacesRepository { + fun refreshSpacesForAccount() +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt new file mode 100644 index 00000000000..99f8190269d --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt @@ -0,0 +1,91 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.domain.spaces.model + +data class OCSpace( + val driveAlias: String, + val driveType: String, + val id: String, + val lastModifiedDateTime: String, + val name: String, + val owner: SpaceOwner, + val quota: SpaceQuota, + val root: SpaceRoot, + val webUrl: String, + val description: String?, + val special: List?, +) { + fun isPersonal() = driveType == DRIVE_TYPE_PERSONAL + fun isProject() = driveType == DRIVE_TYPE_PROJECT + + companion object { + const val DRIVE_TYPE_PERSONAL = "personal" + const val DRIVE_TYPE_PROJECT = "project" + } +} + +data class SpaceOwner( + val user: SpaceUser +) + +data class SpaceQuota( + val remaining: Long, + val state: String, + val total: Int, + val used: Int +) + +data class SpaceRoot( + val eTag: String, + val id: String, + val permissions: List?, + val webDavUrl: String +) + +data class SpaceSpecial( + val eTag: String, + val file: File, + val id: String, + val lastModifiedDateTime: String, + val name: String, + val size: Int, + val specialFolder: SpaceSpecialFolder, + val webDavUrl: String +) + +data class SpaceUser( + val id: String +) + +data class File( + val mimeType: String +) + +data class SpaceGrantedTo( + val user: SpaceUser +) + +data class SpacePermission( + val grantedTo: List, + val roles: List +) + +data class SpaceSpecialFolder( + val name: String +) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt new file mode 100644 index 00000000000..c37d1b7334d --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt @@ -0,0 +1,30 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.domain.spaces.usecases + +import com.owncloud.android.domain.BaseUseCaseWithResult +import com.owncloud.android.domain.spaces.SpacesRepository + +class RefreshSpacesFromServerAsyncUseCase( + private val spacesRepository: SpacesRepository +) : BaseUseCaseWithResult() { + + override fun run(params: Unit) = + spacesRepository.refreshSpacesForAccount() +} From 242c03713733268b3b02ddb95e915eeb3abcbe96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Thu, 22 Dec 2022 08:34:11 +0100 Subject: [PATCH 02/20] Order provider meta alphabetically --- .../owncloud/android/data/ProviderMeta.java | 117 +++++++++--------- .../android/domain/spaces/model/OCSpace.kt | 8 +- 2 files changed, 60 insertions(+), 65 deletions(-) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java index fb491176c90..de0b2d10afa 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java +++ b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java @@ -37,105 +37,100 @@ private ProviderMeta() { } static public class ProviderTableMeta implements BaseColumns { - public static final String OCSHARES_TABLE_NAME = "ocshares"; public static final String CAPABILITIES_TABLE_NAME = "capabilities"; + public static final String FILES_SYNC_TABLE_NAME = "files_sync"; public static final String FILES_TABLE_NAME = "files"; - public static final String USER_QUOTAS_TABLE_NAME = "user_quotas"; public static final String FOLDER_BACKUP_TABLE_NAME = "folder_backup"; + public static final String OCSHARES_TABLE_NAME = "ocshares"; public static final String TRANSFERS_TABLE_NAME = "transfers"; - public static final String FILES_SYNC_TABLE_NAME = "files_sync"; + public static final String USER_QUOTAS_TABLE_NAME = "user_quotas"; // Columns of ocshares table - public static final String OCSHARES_SHARE_TYPE = "share_type"; - public static final String OCSHARES_SHARE_WITH = "share_with"; + public static final String OCSHARES_ACCOUNT_OWNER = "owner_share"; + public static final String OCSHARES_EXPIRATION_DATE = "expiration_date"; + public static final String OCSHARES_ID_REMOTE_SHARED = "id_remote_shared"; + public static final String OCSHARES_IS_DIRECTORY = "is_directory"; + public static final String OCSHARES_NAME = "name"; public static final String OCSHARES_PATH = "path"; public static final String OCSHARES_PERMISSIONS = "permissions"; public static final String OCSHARES_SHARED_DATE = "shared_date"; - public static final String OCSHARES_EXPIRATION_DATE = "expiration_date"; - public static final String OCSHARES_TOKEN = "token"; - public static final String OCSHARES_SHARE_WITH_DISPLAY_NAME = "shared_with_display_name"; + public static final String OCSHARES_SHARE_TYPE = "share_type"; + public static final String OCSHARES_SHARE_WITH = "share_with"; public static final String OCSHARES_SHARE_WITH_ADDITIONAL_INFO = "share_with_additional_info"; - public static final String OCSHARES_IS_DIRECTORY = "is_directory"; - public static final String OCSHARES_ID_REMOTE_SHARED = "id_remote_shared"; - public static final String OCSHARES_ACCOUNT_OWNER = "owner_share"; - public static final String OCSHARES_NAME = "name"; + public static final String OCSHARES_SHARE_WITH_DISPLAY_NAME = "shared_with_display_name"; + public static final String OCSHARES_TOKEN = "token"; public static final String OCSHARES_URL = "url"; // Columns of capabilities table public static final String CAPABILITIES_ACCOUNT_NAME = "account"; - public static final String LEGACY_CAPABILITIES_VERSION_MAYOR = "version_mayor"; - public static final String CAPABILITIES_VERSION_MAJOR = "version_major"; - public static final String CAPABILITIES_VERSION_MINOR = "version_minor"; - public static final String CAPABILITIES_VERSION_MICRO = "version_micro"; - public static final String CAPABILITIES_VERSION_STRING = "version_string"; - public static final String CAPABILITIES_VERSION_EDITION = "version_edition"; + public static final String CAPABILITIES_APP_PROVIDERS_PREFIX = "app_providers_"; public static final String CAPABILITIES_CORE_POLLINTERVAL = "core_pollinterval"; public static final String CAPABILITIES_DAV_CHUNKING_VERSION = "dav_chunking_version"; + public static final String CAPABILITIES_FILES_APP_PROVIDERS = "files_apps_providers"; + public static final String CAPABILITIES_FILES_BIGFILECHUNKING = "files_bigfilechunking"; + public static final String CAPABILITIES_FILES_PRIVATE_LINKS = "files_private_links"; + public static final String CAPABILITIES_FILES_UNDELETE = "files_undelete"; + public static final String CAPABILITIES_FILES_VERSIONING = "files_versioning"; public static final String CAPABILITIES_SHARING_API_ENABLED = "sharing_api_enabled"; + public static final String CAPABILITIES_SHARING_FEDERATION_INCOMING = "sharing_federation_incoming"; + public static final String CAPABILITIES_SHARING_FEDERATION_OUTGOING = "sharing_federation_outgoing"; public static final String CAPABILITIES_SHARING_PUBLIC_ENABLED = "sharing_public_enabled"; - public static final String CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED = "sharing_public_password_enforced"; - public static final String CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED_READ_ONLY = - "sharing_public_password_enforced_read_only"; - public static final String CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED_READ_WRITE = - "sharing_public_password_enforced_read_write"; - public static final String CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED_UPLOAD_ONLY = - "sharing_public_password_enforced_public_only"; - public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENABLED = - "sharing_public_expire_date_enabled"; - public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_DAYS = - "sharing_public_expire_date_days"; - public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENFORCED = - "sharing_public_expire_date_enforced"; - public static final String CAPABILITIES_SHARING_PUBLIC_UPLOAD = "sharing_public_upload"; + public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_DAYS = "sharing_public_expire_date_days"; + public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENABLED = "sharing_public_expire_date_enabled"; + public static final String CAPABILITIES_SHARING_PUBLIC_EXPIRE_DATE_ENFORCED = "sharing_public_expire_date_enforced"; public static final String CAPABILITIES_SHARING_PUBLIC_MULTIPLE = "sharing_public_multiple"; + public static final String CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED = "sharing_public_password_enforced"; + public static final String CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED_READ_ONLY = "sharing_public_password_enforced_read_only"; + public static final String CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED_READ_WRITE = "sharing_public_password_enforced_read_write"; + public static final String CAPABILITIES_SHARING_PUBLIC_PASSWORD_ENFORCED_UPLOAD_ONLY = "sharing_public_password_enforced_public_only"; public static final String CAPABILITIES_SHARING_PUBLIC_SUPPORTS_UPLOAD_ONLY = "supports_upload_only"; + public static final String CAPABILITIES_SHARING_PUBLIC_UPLOAD = "sharing_public_upload"; public static final String CAPABILITIES_SHARING_RESHARING = "sharing_resharing"; - public static final String CAPABILITIES_SHARING_FEDERATION_OUTGOING = "sharing_federation_outgoing"; - public static final String CAPABILITIES_SHARING_FEDERATION_INCOMING = "sharing_federation_incoming"; public static final String CAPABILITIES_SHARING_USER_PROFILE_PICTURE = "sharing_user_profile_picture"; - public static final String CAPABILITIES_FILES_BIGFILECHUNKING = "files_bigfilechunking"; - public static final String CAPABILITIES_FILES_UNDELETE = "files_undelete"; - public static final String CAPABILITIES_FILES_VERSIONING = "files_versioning"; - public static final String CAPABILITIES_FILES_PRIVATE_LINKS = "files_private_links"; - public static final String CAPABILITIES_APP_PROVIDERS_PREFIX = "app_providers_"; public static final String CAPABILITIES_SPACES_PREFIX = "spaces_"; + public static final String CAPABILITIES_VERSION_EDITION = "version_edition"; + public static final String CAPABILITIES_VERSION_MAJOR = "version_major"; + public static final String CAPABILITIES_VERSION_MICRO = "version_micro"; + public static final String CAPABILITIES_VERSION_MINOR = "version_minor"; + public static final String CAPABILITIES_VERSION_STRING = "version_string"; + public static final String LEGACY_CAPABILITIES_VERSION_MAYOR = "version_mayor"; // Columns of filelist table - public static final String FILE_PARENT = "parent"; - public static final String FILE_NAME = "filename"; - public static final String FILE_CREATION = "created"; - public static final String FILE_MODIFIED = "modified"; - public static final String FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA = "modified_at_last_sync_for_data"; + public static final String FILE_ACCOUNT_OWNER = "file_owner"; public static final String FILE_CONTENT_LENGTH = "content_length"; public static final String FILE_CONTENT_TYPE = "content_type"; - public static final String FILE_STORAGE_PATH = "media_path"; - public static final String FILE_PATH = "path"; - public static final String FILE_ACCOUNT_OWNER = "file_owner"; + public static final String FILE_CREATION = "created"; + public static final String FILE_ETAG = "etag"; + public static final String FILE_ETAG_IN_CONFLICT = "etag_in_conflict"; + public static final String FILE_IS_DOWNLOADING = "is_downloading"; + public static final String FILE_KEEP_IN_SYNC = "keep_in_sync"; public static final String FILE_LAST_SYNC_DATE = "last_sync_date";// _for_properties, but let's keep it as it is public static final String FILE_LAST_SYNC_DATE_FOR_DATA = "last_sync_date_for_data"; - public static final String FILE_KEEP_IN_SYNC = "keep_in_sync"; - public static final String FILE_ETAG = "etag"; - public static final String FILE_TREE_ETAG = "tree_etag"; - public static final String FILE_SHARED_VIA_LINK = "share_by_link"; - public static final String FILE_SHARED_WITH_SHAREE = "shared_via_users"; + public static final String FILE_MODIFIED = "modified"; + public static final String FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA = "modified_at_last_sync_for_data"; + public static final String FILE_NAME = "filename"; + public static final String FILE_PARENT = "parent"; + public static final String FILE_PATH = "path"; public static final String FILE_PERMISSIONS = "permissions"; + public static final String FILE_PRIVATE_LINK = "private_link"; public static final String FILE_REMOTE_ID = "remote_id"; + public static final String FILE_SHARED_VIA_LINK = "share_by_link"; + public static final String FILE_SHARED_WITH_SHAREE = "shared_via_users"; + public static final String FILE_STORAGE_PATH = "media_path"; + public static final String FILE_TREE_ETAG = "tree_etag"; public static final String FILE_UPDATE_THUMBNAIL = "update_thumbnail"; - public static final String FILE_IS_DOWNLOADING = "is_downloading"; - public static final String FILE_ETAG_IN_CONFLICT = "etag_in_conflict"; - public static final String FILE_PRIVATE_LINK = "private_link"; // Columns of list_of_uploads table - public static final String UPLOAD_LOCAL_PATH = "local_path"; - public static final String UPLOAD_REMOTE_PATH = "remote_path"; public static final String UPLOAD_ACCOUNT_NAME = "account_name"; + public static final String UPLOAD_CREATED_BY = "created_by"; public static final String UPLOAD_FILE_SIZE = "file_size"; - public static final String UPLOAD_STATUS = "status"; - public static final String UPLOAD_LOCAL_BEHAVIOUR = "local_behaviour"; public static final String UPLOAD_FORCE_OVERWRITE = "force_overwrite"; - public static final String UPLOAD_UPLOAD_END_TIMESTAMP = "upload_end_timestamp"; public static final String UPLOAD_LAST_RESULT = "last_result"; - public static final String UPLOAD_CREATED_BY = "created_by"; + public static final String UPLOAD_LOCAL_BEHAVIOUR = "local_behaviour"; + public static final String UPLOAD_LOCAL_PATH = "local_path"; + public static final String UPLOAD_REMOTE_PATH = "remote_path"; + public static final String UPLOAD_STATUS = "status"; public static final String UPLOAD_TRANSFER_ID = "transfer_id"; + public static final String UPLOAD_UPLOAD_END_TIMESTAMP = "upload_end_timestamp"; } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt index 99f8190269d..345cdc82dee 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt @@ -45,10 +45,10 @@ data class SpaceOwner( ) data class SpaceQuota( - val remaining: Long, - val state: String, - val total: Int, - val used: Int + val remaining: Long?, + val state: String?, + val total: Long, + val used: Long?, ) data class SpaceRoot( From a615d87f4de2085456f48c795c8e3a581298a894 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Fri, 23 Dec 2022 11:51:01 +0100 Subject: [PATCH 03/20] Save spaces into database. --- .../LocalDataSourceModule.kt | 3 +- .../dependecyinjection/ViewModelModule.kt | 22 +- .../40.json | 272 +++++++++++++++--- .../owncloud/android/data/OwncloudDatabase.kt | 10 +- .../owncloud/android/data/ProviderMeta.java | 2 + .../implementation/OCLocalSpacesDataSource.kt | 52 +++- .../OCRemoteSpacesDataSource.kt | 14 +- .../data/spaces/db/SpaceSpecialEntity.kt | 57 ++++ .../android/data/spaces/db/SpacesDao.kt | 33 +++ .../android/data/spaces/db/SpacesEntity.kt | 78 +++++ .../android/domain/spaces/model/OCSpace.kt | 8 +- 11 files changed, 481 insertions(+), 70 deletions(-) create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt index 457731ec2d1..dabbc59ea40 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/LocalDataSourceModule.kt @@ -58,6 +58,7 @@ val localDataSourceModule = module { single { OwncloudDatabase.getDatabase(androidContext()).userDao() } single { OwncloudDatabase.getDatabase(androidContext()).folderBackUpDao() } single { OwncloudDatabase.getDatabase(androidContext()).transferDao() } + single { OwncloudDatabase.getDatabase(androidContext()).spacesDao() } single { OCSharedPreferencesProvider(get()) } single { ScopedStorageProvider(dataFolder, androidContext()) } @@ -69,5 +70,5 @@ val localDataSourceModule = module { factory { OCLocalUserDataSource(get()) } factory { OCFolderBackupLocalDataSource(get()) } factory { OCLocalTransferDataSource(get()) } - factory { OCLocalSpacesDataSource() } + factory { OCLocalSpacesDataSource(get()) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt index 4c6fef9ab4f..fadeb2d4846 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -26,33 +26,33 @@ package com.owncloud.android.dependecyinjection import com.owncloud.android.MainApp import com.owncloud.android.domain.files.model.FileListOption import com.owncloud.android.domain.files.model.OCFile -import com.owncloud.android.presentation.files.details.FileDetailsViewModel -import com.owncloud.android.presentation.files.filelist.MainFileListViewModel -import com.owncloud.android.presentation.files.operations.FileOperationsViewModel -import com.owncloud.android.presentation.security.passcode.PasscodeAction +import com.owncloud.android.presentation.accounts.AccountsManagementViewModel +import com.owncloud.android.presentation.accounts.RemoveAccountDialogViewModel import com.owncloud.android.presentation.authentication.AuthenticationViewModel +import com.owncloud.android.presentation.authentication.oauth.OAuthViewModel import com.owncloud.android.presentation.capabilities.CapabilityViewModel -import com.owncloud.android.presentation.conflicts.ConflictsResolveViewModel import com.owncloud.android.presentation.common.DrawerViewModel +import com.owncloud.android.presentation.conflicts.ConflictsResolveViewModel +import com.owncloud.android.presentation.files.details.FileDetailsViewModel +import com.owncloud.android.presentation.files.filelist.MainFileListViewModel +import com.owncloud.android.presentation.files.operations.FileOperationsViewModel import com.owncloud.android.presentation.logging.LogListViewModel import com.owncloud.android.presentation.migration.MigrationViewModel -import com.owncloud.android.presentation.authentication.oauth.OAuthViewModel import com.owncloud.android.presentation.releasenotes.ReleaseNotesViewModel import com.owncloud.android.presentation.security.biometric.BiometricViewModel import com.owncloud.android.presentation.security.passcode.PassCodeViewModel +import com.owncloud.android.presentation.security.passcode.PasscodeAction import com.owncloud.android.presentation.security.pattern.PatternViewModel +import com.owncloud.android.presentation.settings.SettingsViewModel import com.owncloud.android.presentation.settings.advanced.SettingsAdvancedViewModel +import com.owncloud.android.presentation.settings.autouploads.SettingsPictureUploadsViewModel +import com.owncloud.android.presentation.settings.autouploads.SettingsVideoUploadsViewModel import com.owncloud.android.presentation.settings.logging.SettingsLogsViewModel import com.owncloud.android.presentation.settings.more.SettingsMoreViewModel -import com.owncloud.android.presentation.settings.autouploads.SettingsPictureUploadsViewModel import com.owncloud.android.presentation.settings.security.SettingsSecurityViewModel -import com.owncloud.android.presentation.settings.autouploads.SettingsVideoUploadsViewModel -import com.owncloud.android.presentation.settings.SettingsViewModel import com.owncloud.android.presentation.sharing.ShareViewModel import com.owncloud.android.presentation.transfers.TransfersViewModel -import com.owncloud.android.presentation.accounts.AccountsManagementViewModel import com.owncloud.android.ui.ReceiveExternalFilesViewModel -import com.owncloud.android.presentation.accounts.RemoveAccountDialogViewModel import com.owncloud.android.ui.preview.PreviewImageViewModel import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.module diff --git a/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json index 10a6b97f5d4..62727fe2464 100644 --- a/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json +++ b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 40, - "identityHash": "be3530216034282e4a6680bf121e3096", + "identityHash": "91afe77785b47d2d5959b2a98ce4f6fc", "entities": [ { "tableName": "folder_backup", @@ -74,7 +74,7 @@ }, { "tableName": "capabilities", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account` TEXT, `version_major` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `dav_chunking_version` TEXT NOT NULL, `sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `sharing_user_profile_picture` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1, `files_private_links` INTEGER NOT NULL DEFAULT -1, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `app_providers_enabled` INTEGER, `app_providers_version` TEXT, `app_providers_appsUrl` TEXT, `app_providers_openUrl` TEXT, `app_providers_openWebUrl` TEXT, `app_providers_newUrl` TEXT, `spaces_enabled` INTEGER, `spaces_projects` INTEGER, `spaces_shareJail` INTEGER)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account` TEXT, `version_major` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `dav_chunking_version` TEXT NOT NULL, `sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `sharing_user_profile_picture` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1, `files_private_links` INTEGER NOT NULL DEFAULT -1, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `enabled` INTEGER, `version` TEXT, `appsUrl` TEXT, `openUrl` TEXT, `openWebUrl` TEXT, `newUrl` TEXT, `enabled` INTEGER, `projects` INTEGER, `shareJail` INTEGER)", "fields": [ { "fieldPath": "accountName", @@ -271,55 +271,55 @@ }, { "fieldPath": "appProviders.enabled", - "columnName": "app_providers_enabled", + "columnName": "enabled", "affinity": "INTEGER", "notNull": false }, { "fieldPath": "appProviders.version", - "columnName": "app_providers_version", + "columnName": "version", "affinity": "TEXT", "notNull": false }, { "fieldPath": "appProviders.appsUrl", - "columnName": "app_providers_appsUrl", + "columnName": "appsUrl", "affinity": "TEXT", "notNull": false }, { "fieldPath": "appProviders.openUrl", - "columnName": "app_providers_openUrl", + "columnName": "openUrl", "affinity": "TEXT", "notNull": false }, { "fieldPath": "appProviders.openWebUrl", - "columnName": "app_providers_openWebUrl", + "columnName": "openWebUrl", "affinity": "TEXT", "notNull": false }, { "fieldPath": "appProviders.newUrl", - "columnName": "app_providers_newUrl", + "columnName": "newUrl", "affinity": "TEXT", "notNull": false }, { "fieldPath": "spaces.enabled", - "columnName": "spaces_enabled", + "columnName": "enabled", "affinity": "INTEGER", "notNull": false }, { "fieldPath": "spaces.projects", - "columnName": "spaces_projects", + "columnName": "projects", "affinity": "INTEGER", "notNull": false }, { "fieldPath": "spaces.shareJail", - "columnName": "spaces_shareJail", + "columnName": "shareJail", "affinity": "INTEGER", "notNull": false } @@ -651,38 +651,6 @@ "indices": [], "foreignKeys": [] }, - { - "tableName": "user_quotas", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountName` TEXT NOT NULL, `used` INTEGER NOT NULL, `available` INTEGER NOT NULL, PRIMARY KEY(`accountName`))", - "fields": [ - { - "fieldPath": "accountName", - "columnName": "accountName", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "used", - "columnName": "used", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "available", - "columnName": "available", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "accountName" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, { "tableName": "transfers", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`localPath` TEXT NOT NULL, `remotePath` TEXT NOT NULL, `accountName` TEXT NOT NULL, `fileSize` INTEGER NOT NULL, `status` INTEGER NOT NULL, `localBehaviour` INTEGER NOT NULL, `forceOverwrite` INTEGER NOT NULL, `transferEndTimestamp` INTEGER, `lastResult` INTEGER, `createdBy` INTEGER NOT NULL, `transferId` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", @@ -768,12 +736,228 @@ }, "indices": [], "foreignKeys": [] + }, + { + "tableName": "spaces", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`drive_alias` TEXT NOT NULL, `drive_type` TEXT NOT NULL, `space_id` TEXT NOT NULL, `last_modified_date_time` TEXT NOT NULL, `name` TEXT NOT NULL, `owner_id` TEXT NOT NULL, `rootETag` TEXT NOT NULL, `rootId` TEXT NOT NULL, `rootWebDavUrl` TEXT NOT NULL, `web_url` TEXT NOT NULL, `description` TEXT, `remaining` INTEGER, `state` TEXT, `total` INTEGER, `used` INTEGER, PRIMARY KEY(`space_id`))", + "fields": [ + { + "fieldPath": "driveAlias", + "columnName": "drive_alias", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "driveType", + "columnName": "drive_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "space_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastModifiedDateTime", + "columnName": "last_modified_date_time", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "owner_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rootETag", + "columnName": "rootETag", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rootId", + "columnName": "rootId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rootWebDavUrl", + "columnName": "rootWebDavUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "webUrl", + "columnName": "web_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "quota.remaining", + "columnName": "remaining", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quota.state", + "columnName": "state", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "quota.total", + "columnName": "total", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "quota.used", + "columnName": "used", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "space_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "spaces_special", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`spaces_permission_space_id` TEXT NOT NULL, `eTag` TEXT NOT NULL, `file_mime_type` TEXT NOT NULL, `special_id` TEXT NOT NULL, `last_modified_date_time` TEXT NOT NULL, `name` TEXT NOT NULL, `size` INTEGER NOT NULL, `specialFolderName` TEXT NOT NULL, `webDavUrl` TEXT NOT NULL, PRIMARY KEY(`special_id`), FOREIGN KEY(`spaces_permission_space_id`) REFERENCES `spaces`(`space_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "spaceId", + "columnName": "spaces_permission_space_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "eTag", + "columnName": "eTag", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "fileMymeType", + "columnName": "file_mime_type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "special_id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "lastModifiedDateTime", + "columnName": "last_modified_date_time", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "specialFolderName", + "columnName": "specialFolderName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "webDavUrl", + "columnName": "webDavUrl", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "special_id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [ + { + "table": "spaces", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "spaces_permission_space_id" + ], + "referencedColumns": [ + "space_id" + ] + } + ] + }, + { + "tableName": "user_quotas", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountName` TEXT NOT NULL, `used` INTEGER NOT NULL, `available` INTEGER NOT NULL, PRIMARY KEY(`accountName`))", + "fields": [ + { + "fieldPath": "accountName", + "columnName": "accountName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "used", + "columnName": "used", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "available", + "columnName": "available", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "accountName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] } ], "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'be3530216034282e4a6680bf121e3096')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '91afe77785b47d2d5959b2a98ce4f6fc')" ] } } \ No newline at end of file diff --git a/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt b/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt index 8ab36962772..b2a088d9c6d 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/OwncloudDatabase.kt @@ -48,6 +48,9 @@ import com.owncloud.android.data.migrations.MIGRATION_35_36 import com.owncloud.android.data.migrations.MIGRATION_37_38 import com.owncloud.android.data.sharing.shares.db.OCShareDao import com.owncloud.android.data.sharing.shares.db.OCShareEntity +import com.owncloud.android.data.spaces.db.SpaceSpecialEntity +import com.owncloud.android.data.spaces.db.SpacesDao +import com.owncloud.android.data.spaces.db.SpacesEntity import com.owncloud.android.data.transfers.db.OCTransferEntity import com.owncloud.android.data.transfers.db.TransferDao import com.owncloud.android.data.user.db.UserDao @@ -60,8 +63,10 @@ import com.owncloud.android.data.user.db.UserQuotaEntity OCFileEntity::class, OCFileSyncEntity::class, OCShareEntity::class, - UserQuotaEntity::class, OCTransferEntity::class, + SpacesEntity::class, + SpaceSpecialEntity::class, + UserQuotaEntity::class, ], autoMigrations = [ AutoMigration(from = 36, to = 37), @@ -76,8 +81,9 @@ abstract class OwncloudDatabase : RoomDatabase() { abstract fun fileDao(): FileDao abstract fun folderBackUpDao(): FolderBackupDao abstract fun shareDao(): OCShareDao - abstract fun userDao(): UserDao + abstract fun spacesDao(): SpacesDao abstract fun transferDao(): TransferDao + abstract fun userDao(): UserDao companion object { @Volatile diff --git a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java index de0b2d10afa..525983566d1 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java +++ b/owncloudData/src/main/java/com/owncloud/android/data/ProviderMeta.java @@ -42,6 +42,8 @@ static public class ProviderTableMeta implements BaseColumns { public static final String FILES_TABLE_NAME = "files"; public static final String FOLDER_BACKUP_TABLE_NAME = "folder_backup"; public static final String OCSHARES_TABLE_NAME = "ocshares"; + public static final String SPACES_TABLE_NAME = "spaces"; + public static final String SPACES_SPECIAL_TABLE_NAME = "spaces_special"; public static final String TRANSFERS_TABLE_NAME = "transfers"; public static final String USER_QUOTAS_TABLE_NAME = "user_quotas"; diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt index 7e432419b73..2582f18a4c0 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt @@ -19,12 +19,60 @@ package com.owncloud.android.data.spaces.datasources.implementation import com.owncloud.android.data.spaces.datasources.LocalSpacesDataSource +import com.owncloud.android.data.spaces.db.SpaceQuotaEntity +import com.owncloud.android.data.spaces.db.SpaceSpecialEntity +import com.owncloud.android.data.spaces.db.SpacesDao +import com.owncloud.android.data.spaces.db.SpacesEntity import com.owncloud.android.domain.spaces.model.OCSpace +import com.owncloud.android.domain.spaces.model.SpaceSpecial class OCLocalSpacesDataSource( + private val spacesDao: SpacesDao, +) : LocalSpacesDataSource { -): LocalSpacesDataSource { override fun saveSpacesForAccount(listOfSpaces: List) { - TODO("Not yet implemented") + val spaceEntities = mutableListOf() + val spaceSpecialEntities = mutableListOf() + + listOfSpaces.forEach { spaceModel -> + spaceEntities.add(spaceModel.toEntity()) + spaceModel.special?.let { listOfSpacesSpecials -> + spaceSpecialEntities.addAll(listOfSpacesSpecials.map { it.toEntity(spaceModel.id) }) + } + } + + spacesDao.insertOrReplaceSpaces(spaceEntities) + spacesDao.insertOrReplaceSpecials(spaceSpecialEntities) } + + private fun OCSpace.toEntity() = + SpacesEntity( + driveAlias = driveAlias, + driveType = driveType, + id = id, + lastModifiedDateTime = lastModifiedDateTime, + name = name, + ownerId = owner.user.id, + quota = quota?.let { quotaModel -> + SpaceQuotaEntity(remaining = quotaModel.remaining, state = quotaModel.state, total = quotaModel.total, used = quotaModel.used) + }, + rootETag = root.eTag, + rootId = root.id, + rootWebDavUrl = root.webDavUrl, + webUrl = webUrl, + description = description, + ) + + private fun SpaceSpecial.toEntity(spaceId: String): SpaceSpecialEntity = + SpaceSpecialEntity( + spaceId = spaceId, + eTag = eTag, + fileMymeType = file.mimeType, + id = id, + lastModifiedDateTime = lastModifiedDateTime, + name = name, + size = size, + specialFolderName = specialFolder.name, + webDavUrl = webDavUrl + ) } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt index 20e473b9268..a56a2c8a1b9 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt @@ -56,12 +56,14 @@ class OCRemoteSpacesDataSource( id = owner.user.id ) ), - quota = SpaceQuota( - remaining = quota.remaining, - state = quota.state, - total = quota.total, - used = quota.used - ), + quota = quota?.let { quotaResponse -> + SpaceQuota( + remaining = quotaResponse.remaining, + state = quotaResponse.state, + total = quotaResponse.total, + used = quotaResponse.used, + ) + }, root = SpaceRoot( eTag = root.eTag, id = root.id, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt new file mode 100644 index 00000000000..61238c57207 --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt @@ -0,0 +1,57 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.data.spaces.db + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import androidx.room.PrimaryKey +import com.owncloud.android.data.ProviderMeta +import com.owncloud.android.data.spaces.db.SpaceSpecialEntity.Companion.SPACES_SPECIAL_SPACE_ID + +@Entity( + tableName = ProviderMeta.ProviderTableMeta.SPACES_SPECIAL_TABLE_NAME, + foreignKeys = [ForeignKey( + entity = SpacesEntity::class, + parentColumns = arrayOf(SpacesEntity.SPACES_ID), + childColumns = arrayOf(SPACES_SPECIAL_SPACE_ID), + onDelete = ForeignKey.CASCADE + )] +) +data class SpaceSpecialEntity( + @ColumnInfo(name = SPACES_SPECIAL_SPACE_ID) + val spaceId: String, + val eTag: String, + @ColumnInfo(name = SPACES_SPECIAL_FILE_MIME_TYPE) + val fileMymeType: String, + @ColumnInfo(name = SPACES_SPECIAL_ID) @PrimaryKey + val id: String, + @ColumnInfo(name = SpacesEntity.SPACES_LAST_MODIFIED_DATE_TIME) + val lastModifiedDateTime: String, + val name: String, + val size: Int, + val specialFolderName: String, + val webDavUrl: String +) { + companion object { + const val SPACES_SPECIAL_SPACE_ID = "spaces_permission_space_id" + const val SPACES_SPECIAL_FILE_MIME_TYPE = "file_mime_type" + const val SPACES_SPECIAL_ID = "special_id" + } +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt new file mode 100644 index 00000000000..c88a831f4a8 --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt @@ -0,0 +1,33 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.data.spaces.db + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy + +@Dao +interface SpacesDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertOrReplaceSpaces(listOfSpacesEntities: List): List + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertOrReplaceSpecials(listOfSpecialEntities: List): List + +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt new file mode 100644 index 00000000000..c45bc741cf1 --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt @@ -0,0 +1,78 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.data.spaces.db + +import androidx.room.ColumnInfo +import androidx.room.Embedded +import androidx.room.Entity +import androidx.room.PrimaryKey +import androidx.room.Relation +import com.owncloud.android.data.ProviderMeta +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ID + +data class SpacesWithSpecials( + @Embedded val space: SpacesEntity, + @Relation( + parentColumn = SPACES_ID, + entityColumn = SpaceSpecialEntity.SPACES_SPECIAL_SPACE_ID, + ) + val specials: List +) + +@Entity(tableName = ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME) +data class SpacesEntity( + @ColumnInfo(name = SPACES_DRIVE_ALIAS) + val driveAlias: String, + @ColumnInfo(name = SPACES_DRIVE_TYPE) + val driveType: String, + @PrimaryKey @ColumnInfo(name = SPACES_ID) + val id: String, + @ColumnInfo(name = SPACES_LAST_MODIFIED_DATE_TIME) + val lastModifiedDateTime: String, + val name: String, + @ColumnInfo(name = SPACES_OWNER_ID) + val ownerId: String, + @Embedded + val quota: SpaceQuotaEntity?, + val rootETag: String, + val rootId: String, + val rootWebDavUrl: String, + @ColumnInfo(name = SPACES_WEB_URL) + val webUrl: String, + val description: String?, +) { + + companion object { + const val DRIVE_TYPE_PERSONAL = "personal" + const val DRIVE_TYPE_PROJECT = "project" + const val SPACES_ID = "space_id" + const val SPACES_DRIVE_ALIAS = "drive_alias" + const val SPACES_DRIVE_TYPE = "drive_type" + const val SPACES_LAST_MODIFIED_DATE_TIME = "last_modified_date_time" + const val SPACES_WEB_URL = "web_url" + const val SPACES_OWNER_ID = "owner_id" + } +} + +data class SpaceQuotaEntity( + val remaining: Long, + val state: String, + val total: Int, + val used: Int +) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt index 345cdc82dee..ccb4e29fa83 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt @@ -25,7 +25,7 @@ data class OCSpace( val lastModifiedDateTime: String, val name: String, val owner: SpaceOwner, - val quota: SpaceQuota, + val quota: SpaceQuota?, val root: SpaceRoot, val webUrl: String, val description: String?, @@ -35,8 +35,8 @@ data class OCSpace( fun isProject() = driveType == DRIVE_TYPE_PROJECT companion object { - const val DRIVE_TYPE_PERSONAL = "personal" - const val DRIVE_TYPE_PROJECT = "project" + private const val DRIVE_TYPE_PERSONAL = "personal" + private const val DRIVE_TYPE_PROJECT = "project" } } @@ -78,7 +78,7 @@ data class File( ) data class SpaceGrantedTo( - val user: SpaceUser + val user: SpaceUser? ) data class SpacePermission( From 69b69cae1ebb64713f02850c6e237395ca28e81b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Fri, 23 Dec 2022 20:57:27 +0100 Subject: [PATCH 04/20] Add account name to the spaces and save space specials on a different table --- .../40.json | 30 +++++-- .../datasources/RemoteSpacesDataSource.kt | 2 +- .../implementation/OCLocalSpacesDataSource.kt | 9 +- .../OCRemoteSpacesDataSource.kt | 7 +- .../data/spaces/db/SpaceSpecialEntity.kt | 15 ++-- .../android/data/spaces/db/SpacesDao.kt | 84 +++++++++++++++++++ .../android/data/spaces/db/SpacesEntity.kt | 13 ++- .../spaces/repository/OCSpacesRepository.kt | 4 +- .../android/domain/spaces/SpacesRepository.kt | 2 +- .../android/domain/spaces/model/OCSpace.kt | 1 + .../RefreshSpacesFromServerAsyncUseCase.kt | 10 ++- 11 files changed, 150 insertions(+), 27 deletions(-) diff --git a/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json index 62727fe2464..f16ece5abc1 100644 --- a/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json +++ b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json @@ -2,7 +2,11 @@ "formatVersion": 1, "database": { "version": 40, +<<<<<<< HEAD "identityHash": "91afe77785b47d2d5959b2a98ce4f6fc", +======= + "identityHash": "3e2b989b89b3c14a02a8c4e259a8c022", +>>>>>>> Add account name to the spaces and save space specials on a different table "entities": [ { "tableName": "folder_backup", @@ -739,8 +743,14 @@ }, { "tableName": "spaces", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`drive_alias` TEXT NOT NULL, `drive_type` TEXT NOT NULL, `space_id` TEXT NOT NULL, `last_modified_date_time` TEXT NOT NULL, `name` TEXT NOT NULL, `owner_id` TEXT NOT NULL, `rootETag` TEXT NOT NULL, `rootId` TEXT NOT NULL, `rootWebDavUrl` TEXT NOT NULL, `web_url` TEXT NOT NULL, `description` TEXT, `remaining` INTEGER, `state` TEXT, `total` INTEGER, `used` INTEGER, PRIMARY KEY(`space_id`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account_name` TEXT NOT NULL, `drive_alias` TEXT NOT NULL, `drive_type` TEXT NOT NULL, `space_id` TEXT NOT NULL, `last_modified_date_time` TEXT NOT NULL, `name` TEXT NOT NULL, `owner_id` TEXT NOT NULL, `rootETag` TEXT NOT NULL, `rootId` TEXT NOT NULL, `rootWebDavUrl` TEXT NOT NULL, `web_url` TEXT NOT NULL, `description` TEXT, `remaining` INTEGER, `state` TEXT, `total` INTEGER, `used` INTEGER, PRIMARY KEY(`account_name`, `space_id`))", "fields": [ + { + "fieldPath": "accountName", + "columnName": "account_name", + "affinity": "TEXT", + "notNull": true + }, { "fieldPath": "driveAlias", "columnName": "drive_alias", @@ -834,6 +844,7 @@ ], "primaryKey": { "columnNames": [ + "account_name", "space_id" ], "autoGenerate": false @@ -843,11 +854,17 @@ }, { "tableName": "spaces_special", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`spaces_permission_space_id` TEXT NOT NULL, `eTag` TEXT NOT NULL, `file_mime_type` TEXT NOT NULL, `special_id` TEXT NOT NULL, `last_modified_date_time` TEXT NOT NULL, `name` TEXT NOT NULL, `size` INTEGER NOT NULL, `specialFolderName` TEXT NOT NULL, `webDavUrl` TEXT NOT NULL, PRIMARY KEY(`special_id`), FOREIGN KEY(`spaces_permission_space_id`) REFERENCES `spaces`(`space_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`spaces_special_account_name` TEXT NOT NULL, `spaces_special_space_id` TEXT NOT NULL, `eTag` TEXT NOT NULL, `file_mime_type` TEXT NOT NULL, `special_id` TEXT NOT NULL, `last_modified_date_time` TEXT NOT NULL, `name` TEXT NOT NULL, `size` INTEGER NOT NULL, `specialFolderName` TEXT NOT NULL, `webDavUrl` TEXT NOT NULL, PRIMARY KEY(`spaces_special_space_id`, `special_id`), FOREIGN KEY(`spaces_special_account_name`, `spaces_special_space_id`) REFERENCES `spaces`(`account_name`, `space_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ + { + "fieldPath": "accountName", + "columnName": "spaces_special_account_name", + "affinity": "TEXT", + "notNull": true + }, { "fieldPath": "spaceId", - "columnName": "spaces_permission_space_id", + "columnName": "spaces_special_space_id", "affinity": "TEXT", "notNull": true }, @@ -902,6 +919,7 @@ ], "primaryKey": { "columnNames": [ + "spaces_special_space_id", "special_id" ], "autoGenerate": false @@ -913,9 +931,11 @@ "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ - "spaces_permission_space_id" + "spaces_special_account_name", + "spaces_special_space_id" ], "referencedColumns": [ + "account_name", "space_id" ] } @@ -960,4 +980,4 @@ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '91afe77785b47d2d5959b2a98ce4f6fc')" ] } -} \ No newline at end of file +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt index 5152b53c5b7..1f06f414551 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/RemoteSpacesDataSource.kt @@ -21,5 +21,5 @@ package com.owncloud.android.data.spaces.datasources import com.owncloud.android.domain.spaces.model.OCSpace interface RemoteSpacesDataSource { - fun refreshSpacesForAccount(): List + fun refreshSpacesForAccount(accountName: String): List } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt index 2582f18a4c0..cbce0417bf5 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt @@ -37,16 +37,16 @@ class OCLocalSpacesDataSource( listOfSpaces.forEach { spaceModel -> spaceEntities.add(spaceModel.toEntity()) spaceModel.special?.let { listOfSpacesSpecials -> - spaceSpecialEntities.addAll(listOfSpacesSpecials.map { it.toEntity(spaceModel.id) }) + spaceSpecialEntities.addAll(listOfSpacesSpecials.map { it.toEntity(spaceModel.accountName, spaceModel.id) }) } } - spacesDao.insertOrReplaceSpaces(spaceEntities) - spacesDao.insertOrReplaceSpecials(spaceSpecialEntities) + spacesDao.upsertOrDeleteSpaces(spaceEntities, spaceSpecialEntities) } private fun OCSpace.toEntity() = SpacesEntity( + accountName = accountName, driveAlias = driveAlias, driveType = driveType, id = id, @@ -63,8 +63,9 @@ class OCLocalSpacesDataSource( description = description, ) - private fun SpaceSpecial.toEntity(spaceId: String): SpaceSpecialEntity = + private fun SpaceSpecial.toEntity(accountName: String, spaceId: String): SpaceSpecialEntity = SpaceSpecialEntity( + accountName = accountName, spaceId = spaceId, eTag = eTag, fileMymeType = file.mimeType, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt index a56a2c8a1b9..f5e31d62153 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt @@ -36,16 +36,17 @@ import com.owncloud.android.lib.resources.spaces.services.SpacesService class OCRemoteSpacesDataSource( private val spacesService: SpacesService ) : RemoteSpacesDataSource { - override fun refreshSpacesForAccount(): List { + override fun refreshSpacesForAccount(accountName: String): List { val spacesResponse = executeRemoteOperation { spacesService.getSpaces() } - return spacesResponse.map { it.toModel() } + return spacesResponse.map { it.toModel(accountName) } } - private fun SpaceResponse.toModel(): OCSpace = + private fun SpaceResponse.toModel(accountName: String): OCSpace = OCSpace( + accountName = accountName, driveAlias = driveAlias, driveType = driveType, id = id, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt index 61238c57207..38349f5c656 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt @@ -21,26 +21,30 @@ package com.owncloud.android.data.spaces.db import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.ForeignKey -import androidx.room.PrimaryKey import com.owncloud.android.data.ProviderMeta +import com.owncloud.android.data.spaces.db.SpaceSpecialEntity.Companion.SPACES_SPECIAL_ACCOUNT_NAME +import com.owncloud.android.data.spaces.db.SpaceSpecialEntity.Companion.SPACES_SPECIAL_ID import com.owncloud.android.data.spaces.db.SpaceSpecialEntity.Companion.SPACES_SPECIAL_SPACE_ID @Entity( tableName = ProviderMeta.ProviderTableMeta.SPACES_SPECIAL_TABLE_NAME, + primaryKeys = [SPACES_SPECIAL_SPACE_ID, SPACES_SPECIAL_ID], foreignKeys = [ForeignKey( entity = SpacesEntity::class, - parentColumns = arrayOf(SpacesEntity.SPACES_ID), - childColumns = arrayOf(SPACES_SPECIAL_SPACE_ID), + parentColumns = arrayOf(SpacesEntity.SPACES_ACCOUNT_NAME, SpacesEntity.SPACES_ID), + childColumns = arrayOf(SPACES_SPECIAL_ACCOUNT_NAME, SPACES_SPECIAL_SPACE_ID), onDelete = ForeignKey.CASCADE )] ) data class SpaceSpecialEntity( + @ColumnInfo(name = SPACES_SPECIAL_ACCOUNT_NAME) + val accountName: String, @ColumnInfo(name = SPACES_SPECIAL_SPACE_ID) val spaceId: String, val eTag: String, @ColumnInfo(name = SPACES_SPECIAL_FILE_MIME_TYPE) val fileMymeType: String, - @ColumnInfo(name = SPACES_SPECIAL_ID) @PrimaryKey + @ColumnInfo(name = SPACES_SPECIAL_ID) val id: String, @ColumnInfo(name = SpacesEntity.SPACES_LAST_MODIFIED_DATE_TIME) val lastModifiedDateTime: String, @@ -50,7 +54,8 @@ data class SpaceSpecialEntity( val webDavUrl: String ) { companion object { - const val SPACES_SPECIAL_SPACE_ID = "spaces_permission_space_id" + const val SPACES_SPECIAL_ACCOUNT_NAME = "spaces_special_account_name" + const val SPACES_SPECIAL_SPACE_ID = "spaces_special_space_id" const val SPACES_SPECIAL_FILE_MIME_TYPE = "file_mime_type" const val SPACES_SPECIAL_ID = "special_id" } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt index c88a831f4a8..08f9beab3b4 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt @@ -21,13 +21,97 @@ package com.owncloud.android.data.spaces.db import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction +import com.owncloud.android.data.ProviderMeta +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.DRIVE_TYPE_PERSONAL +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.DRIVE_TYPE_PROJECT +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ACCOUNT_NAME +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_DRIVE_TYPE +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ID @Dao interface SpacesDao { + @Transaction + fun upsertOrDeleteSpaces( + listOfSpacesEntities: List, + listOfSpecialEntities: List, + ) { + val currentAccountName = listOfSpacesEntities.first().accountName + val currentSpaces = getAllSpacesForAccount(currentAccountName) + + // Delete spaces that are not attached to the current account anymore + val spacesToDelete = currentSpaces.filterNot { oldSpace -> + listOfSpacesEntities.any { it.id == oldSpace.id } + } + + spacesToDelete.forEach { spaceToDelete -> + deleteSpaceForAccountById(accountName = spaceToDelete.accountName, spaceId = spaceToDelete.id) + } + + // Upsert new spaces + insertOrReplaceSpaces(listOfSpacesEntities) + insertOrReplaceSpecials(listOfSpecialEntities) + } + + // TODO: Use upsert instead of insert and replace @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertOrReplaceSpaces(listOfSpacesEntities: List): List + // TODO: Use upsert instead of insert and replace @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertOrReplaceSpecials(listOfSpecialEntities: List): List + @Query(SELECT_ALL_SPACES) + fun getAllSpacesForAccount( + accountName: String, + ): List + + @Query(SELECT_PERSONAL_SPACE) + fun getPersonalSpacesForAccount( + accountName: String, + ): List + + @Query(SELECT_PROJECT_SPACES) + fun getProjectSpacesForAccount( + accountName: String, + ): List + + @Query(DELETE_ALL_SPACES_FOR_ACCOUNT) + fun deleteSpacesForAccount(accountName: String) + + @Query(DELETE_SPACE_FOR_ACCOUNT_BY_ID) + fun deleteSpaceForAccountById(accountName: String, spaceId: String) + + companion object { + private const val SELECT_ALL_SPACES = """ + SELECT * + FROM ${ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME} + WHERE $SPACES_ACCOUNT_NAME = :accountName + """ + + private const val SELECT_PERSONAL_SPACE = """ + SELECT * + FROM ${ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME} + WHERE $SPACES_ACCOUNT_NAME = :accountName AND $SPACES_DRIVE_TYPE LIKE '$DRIVE_TYPE_PERSONAL' + """ + + private const val SELECT_PROJECT_SPACES = """ + SELECT * + FROM ${ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME} + WHERE $SPACES_ACCOUNT_NAME = :accountName AND $SPACES_DRIVE_TYPE LIKE '$DRIVE_TYPE_PROJECT' + """ + + private const val DELETE_ALL_SPACES_FOR_ACCOUNT = """ + DELETE + FROM ${ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME} + WHERE $SPACES_ACCOUNT_NAME = :accountName + """ + + private const val DELETE_SPACE_FOR_ACCOUNT_BY_ID = """ + DELETE + FROM ${ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME} + WHERE $SPACES_ACCOUNT_NAME = :accountName AND $SPACES_ID LIKE :spaceId + """ + } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt index c45bc741cf1..0952e1c0590 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt @@ -21,9 +21,10 @@ package com.owncloud.android.data.spaces.db import androidx.room.ColumnInfo import androidx.room.Embedded import androidx.room.Entity -import androidx.room.PrimaryKey +import androidx.room.Index import androidx.room.Relation import com.owncloud.android.data.ProviderMeta +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ACCOUNT_NAME import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ID data class SpacesWithSpecials( @@ -35,13 +36,18 @@ data class SpacesWithSpecials( val specials: List ) -@Entity(tableName = ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME) +@Entity( + tableName = ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME, + primaryKeys = [SPACES_ACCOUNT_NAME, SPACES_ID], +) data class SpacesEntity( + @ColumnInfo(name = SPACES_ACCOUNT_NAME) + val accountName: String, @ColumnInfo(name = SPACES_DRIVE_ALIAS) val driveAlias: String, @ColumnInfo(name = SPACES_DRIVE_TYPE) val driveType: String, - @PrimaryKey @ColumnInfo(name = SPACES_ID) + @ColumnInfo(name = SPACES_ID) val id: String, @ColumnInfo(name = SPACES_LAST_MODIFIED_DATE_TIME) val lastModifiedDateTime: String, @@ -61,6 +67,7 @@ data class SpacesEntity( companion object { const val DRIVE_TYPE_PERSONAL = "personal" const val DRIVE_TYPE_PROJECT = "project" + const val SPACES_ACCOUNT_NAME = "account_name" const val SPACES_ID = "space_id" const val SPACES_DRIVE_ALIAS = "drive_alias" const val SPACES_DRIVE_TYPE = "drive_type" diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt index cfe837932ea..a444e00ead7 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt @@ -26,8 +26,8 @@ class OCSpacesRepository( private val localSpacesDataSource: LocalSpacesDataSource, private val remoteSpacesDataSource: RemoteSpacesDataSource, ) : SpacesRepository { - override fun refreshSpacesForAccount() { - remoteSpacesDataSource.refreshSpacesForAccount().also { listOfSpaces -> + override fun refreshSpacesForAccount(accountName: String) { + remoteSpacesDataSource.refreshSpacesForAccount(accountName).also { listOfSpaces -> localSpacesDataSource.saveSpacesForAccount(listOfSpaces) } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt index 779d7a815ac..e10e1c1b43d 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt @@ -19,5 +19,5 @@ package com.owncloud.android.domain.spaces interface SpacesRepository { - fun refreshSpacesForAccount() + fun refreshSpacesForAccount(accountName: String) } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt index ccb4e29fa83..6479368c569 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt @@ -19,6 +19,7 @@ package com.owncloud.android.domain.spaces.model data class OCSpace( + val accountName: String, val driveAlias: String, val driveType: String, val id: String, diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt index c37d1b7334d..87b666d4569 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/RefreshSpacesFromServerAsyncUseCase.kt @@ -23,8 +23,12 @@ import com.owncloud.android.domain.spaces.SpacesRepository class RefreshSpacesFromServerAsyncUseCase( private val spacesRepository: SpacesRepository -) : BaseUseCaseWithResult() { +) : BaseUseCaseWithResult() { - override fun run(params: Unit) = - spacesRepository.refreshSpacesForAccount() + override fun run(params: Params) = + spacesRepository.refreshSpacesForAccount(accountName = params.accountName) + + data class Params( + val accountName: String, + ) } From a4f30e8b670dda2303a7b5d848c427ce91eb3901 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Tue, 27 Dec 2022 16:55:48 +0100 Subject: [PATCH 05/20] Some type changes and renamings --- .../40.json | 84 +++++++++---------- .../implementation/OCLocalSpacesDataSource.kt | 7 +- .../OCRemoteSpacesDataSource.kt | 4 +- .../data/spaces/db/SpaceSpecialEntity.kt | 16 +++- .../android/data/spaces/db/SpacesEntity.kt | 58 +++++++++---- .../android/domain/spaces/model/OCSpace.kt | 12 +-- 6 files changed, 109 insertions(+), 72 deletions(-) diff --git a/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json index f16ece5abc1..a33064a4695 100644 --- a/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json +++ b/owncloudData/schemas/com.owncloud.android.data.OwncloudDatabase/40.json @@ -2,11 +2,7 @@ "formatVersion": 1, "database": { "version": 40, -<<<<<<< HEAD - "identityHash": "91afe77785b47d2d5959b2a98ce4f6fc", -======= - "identityHash": "3e2b989b89b3c14a02a8c4e259a8c022", ->>>>>>> Add account name to the spaces and save space specials on a different table + "identityHash": "69f96a3d4d578df91be813a866bfbed1", "entities": [ { "tableName": "folder_backup", @@ -78,7 +74,7 @@ }, { "tableName": "capabilities", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account` TEXT, `version_major` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `dav_chunking_version` TEXT NOT NULL, `sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `sharing_user_profile_picture` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1, `files_private_links` INTEGER NOT NULL DEFAULT -1, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `enabled` INTEGER, `version` TEXT, `appsUrl` TEXT, `openUrl` TEXT, `openWebUrl` TEXT, `newUrl` TEXT, `enabled` INTEGER, `projects` INTEGER, `shareJail` INTEGER)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account` TEXT, `version_major` INTEGER NOT NULL, `version_minor` INTEGER NOT NULL, `version_micro` INTEGER NOT NULL, `version_string` TEXT, `version_edition` TEXT, `core_pollinterval` INTEGER NOT NULL, `dav_chunking_version` TEXT NOT NULL, `sharing_api_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_read_write` INTEGER NOT NULL DEFAULT -1, `sharing_public_password_enforced_public_only` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_enabled` INTEGER NOT NULL DEFAULT -1, `sharing_public_expire_date_days` INTEGER NOT NULL, `sharing_public_expire_date_enforced` INTEGER NOT NULL DEFAULT -1, `sharing_public_upload` INTEGER NOT NULL DEFAULT -1, `sharing_public_multiple` INTEGER NOT NULL DEFAULT -1, `supports_upload_only` INTEGER NOT NULL DEFAULT -1, `sharing_resharing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_outgoing` INTEGER NOT NULL DEFAULT -1, `sharing_federation_incoming` INTEGER NOT NULL DEFAULT -1, `sharing_user_profile_picture` INTEGER NOT NULL DEFAULT -1, `files_bigfilechunking` INTEGER NOT NULL DEFAULT -1, `files_undelete` INTEGER NOT NULL DEFAULT -1, `files_versioning` INTEGER NOT NULL DEFAULT -1, `files_private_links` INTEGER NOT NULL DEFAULT -1, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `app_providers_enabled` INTEGER, `app_providers_version` TEXT, `app_providers_appsUrl` TEXT, `app_providers_openUrl` TEXT, `app_providers_openWebUrl` TEXT, `app_providers_newUrl` TEXT, `spaces_enabled` INTEGER, `spaces_projects` INTEGER, `spaces_shareJail` INTEGER)", "fields": [ { "fieldPath": "accountName", @@ -275,55 +271,55 @@ }, { "fieldPath": "appProviders.enabled", - "columnName": "enabled", + "columnName": "app_providers_enabled", "affinity": "INTEGER", "notNull": false }, { "fieldPath": "appProviders.version", - "columnName": "version", + "columnName": "app_providers_version", "affinity": "TEXT", "notNull": false }, { "fieldPath": "appProviders.appsUrl", - "columnName": "appsUrl", + "columnName": "app_providers_appsUrl", "affinity": "TEXT", "notNull": false }, { "fieldPath": "appProviders.openUrl", - "columnName": "openUrl", + "columnName": "app_providers_openUrl", "affinity": "TEXT", "notNull": false }, { "fieldPath": "appProviders.openWebUrl", - "columnName": "openWebUrl", + "columnName": "app_providers_openWebUrl", "affinity": "TEXT", "notNull": false }, { "fieldPath": "appProviders.newUrl", - "columnName": "newUrl", + "columnName": "app_providers_newUrl", "affinity": "TEXT", "notNull": false }, { "fieldPath": "spaces.enabled", - "columnName": "enabled", + "columnName": "spaces_enabled", "affinity": "INTEGER", "notNull": false }, { "fieldPath": "spaces.projects", - "columnName": "projects", + "columnName": "spaces_projects", "affinity": "INTEGER", "notNull": false }, { "fieldPath": "spaces.shareJail", - "columnName": "shareJail", + "columnName": "spaces_shareJail", "affinity": "INTEGER", "notNull": false } @@ -743,7 +739,7 @@ }, { "tableName": "spaces", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account_name` TEXT NOT NULL, `drive_alias` TEXT NOT NULL, `drive_type` TEXT NOT NULL, `space_id` TEXT NOT NULL, `last_modified_date_time` TEXT NOT NULL, `name` TEXT NOT NULL, `owner_id` TEXT NOT NULL, `rootETag` TEXT NOT NULL, `rootId` TEXT NOT NULL, `rootWebDavUrl` TEXT NOT NULL, `web_url` TEXT NOT NULL, `description` TEXT, `remaining` INTEGER, `state` TEXT, `total` INTEGER, `used` INTEGER, PRIMARY KEY(`account_name`, `space_id`))", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`account_name` TEXT NOT NULL, `drive_alias` TEXT NOT NULL, `drive_type` TEXT NOT NULL, `space_id` TEXT NOT NULL, `last_modified_date_time` TEXT NOT NULL, `name` TEXT NOT NULL, `owner_id` TEXT NOT NULL, `web_url` TEXT NOT NULL, `description` TEXT, `quota_remaining` INTEGER, `quota_state` TEXT, `quota_total` INTEGER, `quota_used` INTEGER, `root_etag` TEXT, `root_id` TEXT, `root_web_dav_url` TEXT, PRIMARY KEY(`account_name`, `space_id`))", "fields": [ { "fieldPath": "accountName", @@ -787,24 +783,6 @@ "affinity": "TEXT", "notNull": true }, - { - "fieldPath": "rootETag", - "columnName": "rootETag", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "rootId", - "columnName": "rootId", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "rootWebDavUrl", - "columnName": "rootWebDavUrl", - "affinity": "TEXT", - "notNull": true - }, { "fieldPath": "webUrl", "columnName": "web_url", @@ -819,27 +797,45 @@ }, { "fieldPath": "quota.remaining", - "columnName": "remaining", + "columnName": "quota_remaining", "affinity": "INTEGER", "notNull": false }, { "fieldPath": "quota.state", - "columnName": "state", + "columnName": "quota_state", "affinity": "TEXT", "notNull": false }, { "fieldPath": "quota.total", - "columnName": "total", + "columnName": "quota_total", "affinity": "INTEGER", "notNull": false }, { "fieldPath": "quota.used", - "columnName": "used", + "columnName": "quota_used", "affinity": "INTEGER", "notNull": false + }, + { + "fieldPath": "root.eTag", + "columnName": "root_etag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "root.id", + "columnName": "root_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "root.webDavUrl", + "columnName": "root_web_dav_url", + "affinity": "TEXT", + "notNull": false } ], "primaryKey": { @@ -854,7 +850,7 @@ }, { "tableName": "spaces_special", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`spaces_special_account_name` TEXT NOT NULL, `spaces_special_space_id` TEXT NOT NULL, `eTag` TEXT NOT NULL, `file_mime_type` TEXT NOT NULL, `special_id` TEXT NOT NULL, `last_modified_date_time` TEXT NOT NULL, `name` TEXT NOT NULL, `size` INTEGER NOT NULL, `specialFolderName` TEXT NOT NULL, `webDavUrl` TEXT NOT NULL, PRIMARY KEY(`spaces_special_space_id`, `special_id`), FOREIGN KEY(`spaces_special_account_name`, `spaces_special_space_id`) REFERENCES `spaces`(`account_name`, `space_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`spaces_special_account_name` TEXT NOT NULL, `spaces_special_space_id` TEXT NOT NULL, `spaces_special_etag` TEXT NOT NULL, `file_mime_type` TEXT NOT NULL, `special_id` TEXT NOT NULL, `last_modified_date_time` TEXT NOT NULL, `name` TEXT NOT NULL, `size` INTEGER NOT NULL, `special_folder_name` TEXT NOT NULL, `special_web_dav_url` TEXT NOT NULL, PRIMARY KEY(`spaces_special_space_id`, `special_id`), FOREIGN KEY(`spaces_special_account_name`, `spaces_special_space_id`) REFERENCES `spaces`(`account_name`, `space_id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { "fieldPath": "accountName", @@ -870,7 +866,7 @@ }, { "fieldPath": "eTag", - "columnName": "eTag", + "columnName": "spaces_special_etag", "affinity": "TEXT", "notNull": true }, @@ -906,13 +902,13 @@ }, { "fieldPath": "specialFolderName", - "columnName": "specialFolderName", + "columnName": "special_folder_name", "affinity": "TEXT", "notNull": true }, { "fieldPath": "webDavUrl", - "columnName": "webDavUrl", + "columnName": "special_web_dav_url", "affinity": "TEXT", "notNull": true } @@ -977,7 +973,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '91afe77785b47d2d5959b2a98ce4f6fc')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '69f96a3d4d578df91be813a866bfbed1')" ] } -} +} \ No newline at end of file diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt index cbce0417bf5..7473af56739 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt @@ -20,6 +20,7 @@ package com.owncloud.android.data.spaces.datasources.implementation import com.owncloud.android.data.spaces.datasources.LocalSpacesDataSource import com.owncloud.android.data.spaces.db.SpaceQuotaEntity +import com.owncloud.android.data.spaces.db.SpaceRootEntity import com.owncloud.android.data.spaces.db.SpaceSpecialEntity import com.owncloud.android.data.spaces.db.SpacesDao import com.owncloud.android.data.spaces.db.SpacesEntity @@ -56,9 +57,9 @@ class OCLocalSpacesDataSource( quota = quota?.let { quotaModel -> SpaceQuotaEntity(remaining = quotaModel.remaining, state = quotaModel.state, total = quotaModel.total, used = quotaModel.used) }, - rootETag = root.eTag, - rootId = root.id, - rootWebDavUrl = root.webDavUrl, + root = root.let { rootModel -> + SpaceRootEntity(eTag = rootModel.eTag, id = rootModel.id, webDavUrl = rootModel.webDavUrl) + }, webUrl = webUrl, description = description, ) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt index f5e31d62153..6a46ae82a9c 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCRemoteSpacesDataSource.kt @@ -20,7 +20,7 @@ package com.owncloud.android.data.spaces.datasources.implementation import com.owncloud.android.data.executeRemoteOperation import com.owncloud.android.data.spaces.datasources.RemoteSpacesDataSource -import com.owncloud.android.domain.spaces.model.File +import com.owncloud.android.domain.spaces.model.SpaceFile import com.owncloud.android.domain.spaces.model.OCSpace import com.owncloud.android.domain.spaces.model.SpaceGrantedTo import com.owncloud.android.domain.spaces.model.SpaceOwner @@ -83,7 +83,7 @@ class OCRemoteSpacesDataSource( special = special?.map { specialResponse -> SpaceSpecial( eTag = specialResponse.eTag, - file = File(mimeType = specialResponse.file.mimeType), + file = SpaceFile(mimeType = specialResponse.file.mimeType), id = specialResponse.id, lastModifiedDateTime = specialResponse.lastModifiedDateTime, name = specialResponse.name, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt index 38349f5c656..1b154bcac1b 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt @@ -2,6 +2,8 @@ * ownCloud Android client application * * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón + * * Copyright (C) 2022 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify @@ -16,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.data.spaces.db import androidx.room.ColumnInfo @@ -25,13 +28,16 @@ import com.owncloud.android.data.ProviderMeta import com.owncloud.android.data.spaces.db.SpaceSpecialEntity.Companion.SPACES_SPECIAL_ACCOUNT_NAME import com.owncloud.android.data.spaces.db.SpaceSpecialEntity.Companion.SPACES_SPECIAL_ID import com.owncloud.android.data.spaces.db.SpaceSpecialEntity.Companion.SPACES_SPECIAL_SPACE_ID +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ACCOUNT_NAME +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ID +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_LAST_MODIFIED_DATE_TIME @Entity( tableName = ProviderMeta.ProviderTableMeta.SPACES_SPECIAL_TABLE_NAME, primaryKeys = [SPACES_SPECIAL_SPACE_ID, SPACES_SPECIAL_ID], foreignKeys = [ForeignKey( entity = SpacesEntity::class, - parentColumns = arrayOf(SpacesEntity.SPACES_ACCOUNT_NAME, SpacesEntity.SPACES_ID), + parentColumns = arrayOf(SPACES_ACCOUNT_NAME, SPACES_ID), childColumns = arrayOf(SPACES_SPECIAL_ACCOUNT_NAME, SPACES_SPECIAL_SPACE_ID), onDelete = ForeignKey.CASCADE )] @@ -41,22 +47,28 @@ data class SpaceSpecialEntity( val accountName: String, @ColumnInfo(name = SPACES_SPECIAL_SPACE_ID) val spaceId: String, + @ColumnInfo(name = SPACES_SPECIAL_ETAG) val eTag: String, @ColumnInfo(name = SPACES_SPECIAL_FILE_MIME_TYPE) val fileMymeType: String, @ColumnInfo(name = SPACES_SPECIAL_ID) val id: String, - @ColumnInfo(name = SpacesEntity.SPACES_LAST_MODIFIED_DATE_TIME) + @ColumnInfo(name = SPACES_LAST_MODIFIED_DATE_TIME) val lastModifiedDateTime: String, val name: String, val size: Int, + @ColumnInfo(name = SPACES_SPECIAL_FOLDER_NAME) val specialFolderName: String, + @ColumnInfo(name = SPACES_SPECIAL_WEB_DAV_URL) val webDavUrl: String ) { companion object { const val SPACES_SPECIAL_ACCOUNT_NAME = "spaces_special_account_name" const val SPACES_SPECIAL_SPACE_ID = "spaces_special_space_id" + const val SPACES_SPECIAL_ETAG = "spaces_special_etag" const val SPACES_SPECIAL_FILE_MIME_TYPE = "file_mime_type" const val SPACES_SPECIAL_ID = "special_id" + const val SPACES_SPECIAL_FOLDER_NAME = "special_folder_name" + const val SPACES_SPECIAL_WEB_DAV_URL = "special_web_dav_url" } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt index 0952e1c0590..9ba18571a6d 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesEntity.kt @@ -2,6 +2,8 @@ * ownCloud Android client application * * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón + * * Copyright (C) 2022 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify @@ -16,25 +18,23 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.data.spaces.db import androidx.room.ColumnInfo import androidx.room.Embedded import androidx.room.Entity -import androidx.room.Index import androidx.room.Relation import com.owncloud.android.data.ProviderMeta import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ACCOUNT_NAME import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ID - -data class SpacesWithSpecials( - @Embedded val space: SpacesEntity, - @Relation( - parentColumn = SPACES_ID, - entityColumn = SpaceSpecialEntity.SPACES_SPECIAL_SPACE_ID, - ) - val specials: List -) +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_QUOTA_REMAINING +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_QUOTA_STATE +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_QUOTA_TOTAL +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_QUOTA_USED +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ROOT_ETAG +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ROOT_ID +import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ROOT_WEB_DAV_URL @Entity( tableName = ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME, @@ -56,9 +56,8 @@ data class SpacesEntity( val ownerId: String, @Embedded val quota: SpaceQuotaEntity?, - val rootETag: String, - val rootId: String, - val rootWebDavUrl: String, + @Embedded + val root: SpaceRootEntity?, @ColumnInfo(name = SPACES_WEB_URL) val webUrl: String, val description: String?, @@ -74,12 +73,41 @@ data class SpacesEntity( const val SPACES_LAST_MODIFIED_DATE_TIME = "last_modified_date_time" const val SPACES_WEB_URL = "web_url" const val SPACES_OWNER_ID = "owner_id" + const val SPACES_QUOTA_REMAINING = "quota_remaining" + const val SPACES_QUOTA_STATE = "quota_state" + const val SPACES_QUOTA_TOTAL = "quota_total" + const val SPACES_QUOTA_USED = "quota_used" + const val SPACES_ROOT_ETAG = "root_etag" + const val SPACES_ROOT_ID = "root_id" + const val SPACES_ROOT_WEB_DAV_URL = "root_web_dav_url" } } data class SpaceQuotaEntity( + @ColumnInfo(name = SPACES_QUOTA_REMAINING) val remaining: Long, + @ColumnInfo(name = SPACES_QUOTA_STATE) val state: String, - val total: Int, - val used: Int + @ColumnInfo(name = SPACES_QUOTA_TOTAL) + val total: Long, + @ColumnInfo(name = SPACES_QUOTA_USED) + val used: Long +) + +data class SpaceRootEntity( + @ColumnInfo(name = SPACES_ROOT_ETAG) + val eTag: String, + @ColumnInfo(name = SPACES_ROOT_ID) + val id: String, + @ColumnInfo(name = SPACES_ROOT_WEB_DAV_URL) + val webDavUrl: String +) + +data class SpacesWithSpecials( + @Embedded val space: SpacesEntity, + @Relation( + parentColumn = SPACES_ID, + entityColumn = SpaceSpecialEntity.SPACES_SPECIAL_SPACE_ID, + ) + val specials: List ) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt index 6479368c569..89ad3e9f1f6 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt @@ -61,7 +61,7 @@ data class SpaceRoot( data class SpaceSpecial( val eTag: String, - val file: File, + val file: SpaceFile, val id: String, val lastModifiedDateTime: String, val name: String, @@ -74,19 +74,19 @@ data class SpaceUser( val id: String ) -data class File( +data class SpaceFile( val mimeType: String ) -data class SpaceGrantedTo( - val user: SpaceUser? -) - data class SpacePermission( val grantedTo: List, val roles: List ) +data class SpaceGrantedTo( + val user: SpaceUser? +) + data class SpaceSpecialFolder( val name: String ) From 1096a77a957c666517e3e80fa44c01f77682c232 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Wed, 28 Dec 2022 15:11:21 +0100 Subject: [PATCH 06/20] Added usecase for retrieving project spaces as Flow from DB --- .../dependecyinjection/UseCaseModule.kt | 2 + .../dependecyinjection/ViewModelModule.kt | 2 + .../presentation/spaces/SpacesListFragment.kt | 17 +++++- .../spaces/SpacesListViewModel.kt | 52 ++++++++++++++++++ .../datasources/LocalSpacesDataSource.kt | 5 ++ .../implementation/OCLocalSpacesDataSource.kt | 53 ++++++++++++++++++- .../android/data/spaces/db/SpacesDao.kt | 24 +++++---- .../spaces/repository/OCSpacesRepository.kt | 6 +++ .../android/domain/spaces/SpacesRepository.kt | 7 +++ ...tProjectSpacesForAccountAsStreamUseCase.kt | 37 +++++++++++++ 10 files changed, 193 insertions(+), 12 deletions(-) create mode 100644 owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetProjectSpacesForAccountAsStreamUseCase.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt index 5a074c5c706..896abf3531d 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -77,6 +77,7 @@ import com.owncloud.android.domain.sharing.shares.usecases.EditPublicShareAsyncU import com.owncloud.android.domain.sharing.shares.usecases.GetShareAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.GetSharesAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.RefreshSharesFromServerAsyncUseCase +import com.owncloud.android.domain.spaces.usecases.GetProjectSpacesForAccountAsStreamUseCase import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase import com.owncloud.android.domain.transfers.usecases.ClearSuccessfulTransfersUseCase import com.owncloud.android.domain.transfers.usecases.GetAllTransfersAsLiveDataUseCase @@ -175,6 +176,7 @@ val useCaseModule = module { // Spaces factory { RefreshSpacesFromServerAsyncUseCase(get()) } + factory { GetProjectSpacesForAccountAsStreamUseCase(get()) } // Transfers factory { CancelDownloadForFileUseCase(get()) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt index fadeb2d4846..2ced99e2cdd 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -51,6 +51,7 @@ import com.owncloud.android.presentation.settings.logging.SettingsLogsViewModel import com.owncloud.android.presentation.settings.more.SettingsMoreViewModel import com.owncloud.android.presentation.settings.security.SettingsSecurityViewModel import com.owncloud.android.presentation.sharing.ShareViewModel +import com.owncloud.android.presentation.spaces.SpacesListViewModel import com.owncloud.android.presentation.transfers.TransfersViewModel import com.owncloud.android.ui.ReceiveExternalFilesViewModel import com.owncloud.android.ui.preview.PreviewImageViewModel @@ -112,4 +113,5 @@ val viewModelModule = module { viewModel { (ocFile: OCFile) -> ConflictsResolveViewModel(get(), get(), get(), get(), get(), ocFile) } viewModel { ReceiveExternalFilesViewModel(get(), get()) } viewModel { AccountsManagementViewModel(get()) } + viewModel { SpacesListViewModel(get(), get(), get(), get()) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt index 8f8f7ed28f4..876eda8305a 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt @@ -28,14 +28,18 @@ import androidx.core.view.isVisible import androidx.fragment.app.Fragment import com.owncloud.android.databinding.SpacesListFragmentBinding import com.owncloud.android.domain.files.model.FileListOption +import com.owncloud.android.extensions.collectLatestLifecycleFlow import com.owncloud.android.extensions.toDrawableRes import com.owncloud.android.extensions.toSubtitleStringRes import com.owncloud.android.extensions.toTitleStringRes +import org.koin.androidx.viewmodel.ext.android.viewModel class SpacesListFragment : Fragment() { private var _binding: SpacesListFragmentBinding? = null private val binding get() = _binding!! + private val spacesListViewModel: SpacesListViewModel by viewModel() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -46,7 +50,18 @@ class SpacesListFragment : Fragment() { } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - showOrHideEmptyView() + initViews() + subscribeToViewModels() + } + + private fun initViews() { + + } + + private fun subscribeToViewModels() { + collectLatestLifecycleFlow(spacesListViewModel.spacesList) { + showOrHideEmptyView() + } } // TODO: Use this method only when necessary, for the moment the empty view is shown always diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt new file mode 100644 index 00000000000..bf2b11ba4c0 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt @@ -0,0 +1,52 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.presentation.spaces + +import android.accounts.Account +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.owncloud.android.domain.spaces.model.OCSpace +import com.owncloud.android.domain.spaces.usecases.GetProjectSpacesForAccountAsStreamUseCase +import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase +import com.owncloud.android.providers.CoroutinesDispatcherProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class SpacesListViewModel( + private val refreshSpacesFromServerAsyncUseCase: RefreshSpacesFromServerAsyncUseCase, + getProjectSpacesForAccountAsStreamUseCase: GetProjectSpacesForAccountAsStreamUseCase, + coroutinesDispatcherProvider: CoroutinesDispatcherProvider, + private val account: Account, +) : ViewModel() { + val spacesList: MutableStateFlow> = MutableStateFlow(emptyList()) + + init { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + refreshSpacesFromServerAsyncUseCase.execute(RefreshSpacesFromServerAsyncUseCase.Params(account.name)) + spacesList.update { getProjectSpacesForAccountAsStreamUseCase.execute( + GetProjectSpacesForAccountAsStreamUseCase.Params( + accountName = account.name + )).first() } + } + } +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/LocalSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/LocalSpacesDataSource.kt index ec34eb73b4c..e2e2199da92 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/LocalSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/LocalSpacesDataSource.kt @@ -2,6 +2,8 @@ * ownCloud Android client application * * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón + * * Copyright (C) 2022 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify @@ -16,10 +18,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.data.spaces.datasources import com.owncloud.android.domain.spaces.model.OCSpace +import kotlinx.coroutines.flow.Flow interface LocalSpacesDataSource { fun saveSpacesForAccount(listOfSpaces: List) + fun getProjectSpacesForAccountAsFlow(accountName: String): Flow> } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt index 7473af56739..8704626a0b6 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt @@ -2,6 +2,8 @@ * ownCloud Android client application * * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón + * * Copyright (C) 2022 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify @@ -16,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.data.spaces.datasources.implementation import com.owncloud.android.data.spaces.datasources.LocalSpacesDataSource @@ -25,7 +28,13 @@ import com.owncloud.android.data.spaces.db.SpaceSpecialEntity import com.owncloud.android.data.spaces.db.SpacesDao import com.owncloud.android.data.spaces.db.SpacesEntity import com.owncloud.android.domain.spaces.model.OCSpace +import com.owncloud.android.domain.spaces.model.SpaceOwner +import com.owncloud.android.domain.spaces.model.SpaceQuota +import com.owncloud.android.domain.spaces.model.SpaceRoot import com.owncloud.android.domain.spaces.model.SpaceSpecial +import com.owncloud.android.domain.spaces.model.SpaceUser +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map class OCLocalSpacesDataSource( private val spacesDao: SpacesDao, @@ -42,9 +51,51 @@ class OCLocalSpacesDataSource( } } - spacesDao.upsertOrDeleteSpaces(spaceEntities, spaceSpecialEntities) + spacesDao.insertOrDeleteSpaces(spaceEntities, spaceSpecialEntities) } + override fun getProjectSpacesForAccountAsFlow(accountName: String): Flow> { + return spacesDao.getProjectSpacesForAccountAsFlow(accountName).map { spacesEntitiesList -> + spacesEntitiesList.map { spacesEntity -> + spacesEntity.toModel() + } + } + } + + private fun SpacesEntity.toModel() = + OCSpace( + accountName = accountName, + driveAlias = driveAlias, + driveType = driveType, + id = id, + lastModifiedDateTime = lastModifiedDateTime, + name = name, + owner = SpaceOwner( + user = SpaceUser( + id = ownerId + ) + ), + quota = quota?.let { + SpaceQuota( + remaining = it.remaining, + state = it.state, + total = it.total, + used = it.used + ) + }, + root = root!!.let { + SpaceRoot( + eTag = it.eTag, + id = it.id, + permissions = null, + webDavUrl = it.webDavUrl + ) + }, + webUrl = webUrl, + description = description, + special = null + ) + private fun OCSpace.toEntity() = SpacesEntity( accountName = accountName, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt index 08f9beab3b4..04c239721b9 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt @@ -2,6 +2,8 @@ * ownCloud Android client application * * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón + * * Copyright (C) 2022 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify @@ -16,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.data.spaces.db import androidx.room.Dao @@ -29,11 +32,12 @@ import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.DRIVE_TYPE_PRO import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ACCOUNT_NAME import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_DRIVE_TYPE import com.owncloud.android.data.spaces.db.SpacesEntity.Companion.SPACES_ID +import kotlinx.coroutines.flow.Flow @Dao interface SpacesDao { @Transaction - fun upsertOrDeleteSpaces( + fun insertOrDeleteSpaces( listOfSpacesEntities: List, listOfSpecialEntities: List, ) { @@ -49,7 +53,7 @@ interface SpacesDao { deleteSpaceForAccountById(accountName = spaceToDelete.accountName, spaceId = spaceToDelete.id) } - // Upsert new spaces + // Insert new spaces insertOrReplaceSpaces(listOfSpacesEntities) insertOrReplaceSpecials(listOfSpecialEntities) } @@ -62,20 +66,20 @@ interface SpacesDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertOrReplaceSpecials(listOfSpecialEntities: List): List - @Query(SELECT_ALL_SPACES) + @Query(SELECT_ALL_SPACES_FOR_ACCOUNT) fun getAllSpacesForAccount( accountName: String, ): List - @Query(SELECT_PERSONAL_SPACE) + @Query(SELECT_PERSONAL_SPACE_FOR_ACCOUNT) fun getPersonalSpacesForAccount( accountName: String, ): List - @Query(SELECT_PROJECT_SPACES) - fun getProjectSpacesForAccount( + @Query(SELECT_PROJECT_SPACES_FOR_ACCOUNT) + fun getProjectSpacesForAccountAsFlow( accountName: String, - ): List + ): Flow> @Query(DELETE_ALL_SPACES_FOR_ACCOUNT) fun deleteSpacesForAccount(accountName: String) @@ -84,19 +88,19 @@ interface SpacesDao { fun deleteSpaceForAccountById(accountName: String, spaceId: String) companion object { - private const val SELECT_ALL_SPACES = """ + private const val SELECT_ALL_SPACES_FOR_ACCOUNT = """ SELECT * FROM ${ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME} WHERE $SPACES_ACCOUNT_NAME = :accountName """ - private const val SELECT_PERSONAL_SPACE = """ + private const val SELECT_PERSONAL_SPACE_FOR_ACCOUNT = """ SELECT * FROM ${ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME} WHERE $SPACES_ACCOUNT_NAME = :accountName AND $SPACES_DRIVE_TYPE LIKE '$DRIVE_TYPE_PERSONAL' """ - private const val SELECT_PROJECT_SPACES = """ + private const val SELECT_PROJECT_SPACES_FOR_ACCOUNT = """ SELECT * FROM ${ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME} WHERE $SPACES_ACCOUNT_NAME = :accountName AND $SPACES_DRIVE_TYPE LIKE '$DRIVE_TYPE_PROJECT' diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt index a444e00ead7..2b37c83170c 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt @@ -2,6 +2,8 @@ * ownCloud Android client application * * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón + * * Copyright (C) 2022 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify @@ -16,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.data.spaces.repository import com.owncloud.android.data.spaces.datasources.LocalSpacesDataSource @@ -31,4 +34,7 @@ class OCSpacesRepository( localSpacesDataSource.saveSpacesForAccount(listOfSpaces) } } + + override fun getProjectSpacesForAccountAsFlow(accountName: String) = + localSpacesDataSource.getProjectSpacesForAccountAsFlow(accountName) } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt index e10e1c1b43d..aceac27c056 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt @@ -2,6 +2,8 @@ * ownCloud Android client application * * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón + * * Copyright (C) 2022 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify @@ -16,8 +18,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.domain.spaces +import com.owncloud.android.domain.spaces.model.OCSpace +import kotlinx.coroutines.flow.Flow + interface SpacesRepository { fun refreshSpacesForAccount(accountName: String) + fun getProjectSpacesForAccountAsFlow(accountName: String): Flow> } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetProjectSpacesForAccountAsStreamUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetProjectSpacesForAccountAsStreamUseCase.kt new file mode 100644 index 00000000000..fb85bf876ed --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetProjectSpacesForAccountAsStreamUseCase.kt @@ -0,0 +1,37 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.domain.spaces.usecases + +import com.owncloud.android.domain.BaseUseCase +import com.owncloud.android.domain.spaces.SpacesRepository +import com.owncloud.android.domain.spaces.model.OCSpace +import kotlinx.coroutines.flow.Flow + +class GetProjectSpacesForAccountAsStreamUseCase( + private val spacesRepository: SpacesRepository +) : BaseUseCase>, GetProjectSpacesForAccountAsStreamUseCase.Params>() { + + override fun run(params: Params) = spacesRepository.getProjectSpacesForAccountAsFlow(params.accountName) + + data class Params( + val accountName: String + ) +} From 7c2e41b248a0f2fd8e0ca9fe4b9b5e287c396892 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Thu, 29 Dec 2022 14:36:49 +0100 Subject: [PATCH 07/20] First approach for spaces list --- .../presentation/spaces/SpacesListAdapter.kt | 71 ++++++++++++++++ .../presentation/spaces/SpacesListFragment.kt | 16 +++- .../main/res/layout/spaces_list_fragment.xml | 11 ++- .../src/main/res/layout/spaces_list_item.xml | 81 +++++++++++++++++++ owncloudApp/src/main/res/values/colors.xml | 6 +- .../android/domain/spaces/model/OCSpace.kt | 10 +++ 6 files changed, 187 insertions(+), 8 deletions(-) create mode 100644 owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt create mode 100644 owncloudApp/src/main/res/layout/spaces_list_item.xml diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt new file mode 100644 index 00000000000..acb545c0479 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt @@ -0,0 +1,71 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.presentation.spaces + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.owncloud.android.databinding.SpacesListItemBinding +import com.owncloud.android.domain.spaces.model.OCSpace +import com.owncloud.android.utils.PreferenceUtils + +class SpacesListAdapter() : RecyclerView.Adapter() { + + private val spacesList = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val binding = SpacesListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) + binding.root.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(parent.context) + return SpacesViewHolder(binding) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val spacesViewHolder = holder as SpacesViewHolder + spacesViewHolder.binding.apply { + val space = spacesList[position] + + spacesListItemName.text = space.name + spacesListItemSubtitle.text = space.description + + space.getSpaceImageUrl()?.let { + Glide.with(holder.itemView) + .load(it) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .into(spacesListItemImage) + } + + } + + } + + fun setData(spaces: List) { + spacesList.clear() + spacesList.addAll(spaces) + } + + override fun getItemCount(): Int = spacesList.size + + fun getItem(position: Int) = spacesList[position] + + class SpacesViewHolder(val binding: SpacesListItemBinding) : RecyclerView.ViewHolder(binding.root) +} diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt index 876eda8305a..85999a39a33 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListFragment.kt @@ -26,6 +26,7 @@ import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.GridLayoutManager import com.owncloud.android.databinding.SpacesListFragmentBinding import com.owncloud.android.domain.files.model.FileListOption import com.owncloud.android.extensions.collectLatestLifecycleFlow @@ -40,6 +41,8 @@ class SpacesListFragment : Fragment() { private val spacesListViewModel: SpacesListViewModel by viewModel() + private lateinit var spacesListAdapter: SpacesListAdapter + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -55,21 +58,28 @@ class SpacesListFragment : Fragment() { } private fun initViews() { + val spacesListLayoutManager = GridLayoutManager(requireContext(), 2) + binding.recyclerSpacesList.layoutManager = spacesListLayoutManager + spacesListAdapter = SpacesListAdapter() + binding.recyclerSpacesList.adapter = spacesListAdapter + + binding.swipeRefreshSpacesList.setOnRefreshListener { + } } private fun subscribeToViewModels() { collectLatestLifecycleFlow(spacesListViewModel.spacesList) { showOrHideEmptyView() + spacesListAdapter.setData(spacesListViewModel.spacesList.value) } } - // TODO: Use this method only when necessary, for the moment the empty view is shown always private fun showOrHideEmptyView() { - binding.recyclerSpacesList.isVisible = false + binding.recyclerSpacesList.isVisible = spacesListViewModel.spacesList.value.isNotEmpty() with(binding.emptyDataParent) { - root.isVisible = true + root.isVisible = spacesListViewModel.spacesList.value.isEmpty() listEmptyDatasetIcon.setImageResource(FileListOption.SPACES_LIST.toDrawableRes()) listEmptyDatasetTitle.setText(FileListOption.SPACES_LIST.toTitleStringRes()) listEmptyDatasetSubTitle.setText(FileListOption.SPACES_LIST.toSubtitleStringRes()) diff --git a/owncloudApp/src/main/res/layout/spaces_list_fragment.xml b/owncloudApp/src/main/res/layout/spaces_list_fragment.xml index 1ee3a007d50..e7b09bc4aa5 100644 --- a/owncloudApp/src/main/res/layout/spaces_list_fragment.xml +++ b/owncloudApp/src/main/res/layout/spaces_list_fragment.xml @@ -1,4 +1,5 @@ - + + + + + + + + + + + + + + + + + + + + diff --git a/owncloudApp/src/main/res/values/colors.xml b/owncloudApp/src/main/res/values/colors.xml index 1473e10e5c9..2698497be5b 100644 --- a/owncloudApp/src/main/res/values/colors.xml +++ b/owncloudApp/src/main/res/values/colors.xml @@ -67,4 +67,8 @@ #6E758C #6E758C - \ No newline at end of file + + + #edf3fa + #4c5f79 + diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt index 89ad3e9f1f6..4a0e164fd01 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt @@ -2,6 +2,8 @@ * ownCloud Android client application * * @author Abel García de Prada + * @author Juan Carlos Garrote Gascón + * * Copyright (C) 2022 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify @@ -16,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.domain.spaces.model data class OCSpace( @@ -39,6 +42,13 @@ data class OCSpace( private const val DRIVE_TYPE_PERSONAL = "personal" private const val DRIVE_TYPE_PROJECT = "project" } + + fun getSpaceImageUrl(): String? { + val imageSpecial = special?.filter { + it.specialFolder.name == "image" + } + return imageSpecial?.first()?.webDavUrl + } } data class SpaceOwner( From afb8d87c975da8de7ebaaa1a97f5638aff8abb71 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Fri, 30 Dec 2022 10:41:38 +0100 Subject: [PATCH 08/20] Everything ready to load the images in spaces list --- .../dependecyinjection/UseCaseModule.kt | 4 +- .../presentation/spaces/SpacesListAdapter.kt | 13 +---- .../spaces/SpacesListViewModel.kt | 8 +-- .../src/main/res/layout/spaces_list_item.xml | 5 +- .../datasources/LocalSpacesDataSource.kt | 2 +- .../implementation/OCLocalSpacesDataSource.kt | 57 +++++++++++++------ .../data/spaces/db/SpaceSpecialEntity.kt | 2 +- .../android/data/spaces/db/SpacesDao.kt | 4 +- .../spaces/repository/OCSpacesRepository.kt | 4 +- .../android/domain/spaces/SpacesRepository.kt | 2 +- .../android/domain/spaces/model/OCSpace.kt | 4 +- ...sWithSpecialsForAccountAsStreamUseCase.kt} | 6 +- 12 files changed, 64 insertions(+), 47 deletions(-) rename owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/{GetProjectSpacesForAccountAsStreamUseCase.kt => GetProjectSpacesWithSpecialsForAccountAsStreamUseCase.kt} (83%) diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt index 896abf3531d..764aa1982ce 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -77,7 +77,7 @@ import com.owncloud.android.domain.sharing.shares.usecases.EditPublicShareAsyncU import com.owncloud.android.domain.sharing.shares.usecases.GetShareAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.GetSharesAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.RefreshSharesFromServerAsyncUseCase -import com.owncloud.android.domain.spaces.usecases.GetProjectSpacesForAccountAsStreamUseCase +import com.owncloud.android.domain.spaces.usecases.GetProjectSpacesWithSpecialsForAccountAsStreamUseCase import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase import com.owncloud.android.domain.transfers.usecases.ClearSuccessfulTransfersUseCase import com.owncloud.android.domain.transfers.usecases.GetAllTransfersAsLiveDataUseCase @@ -176,7 +176,7 @@ val useCaseModule = module { // Spaces factory { RefreshSpacesFromServerAsyncUseCase(get()) } - factory { GetProjectSpacesForAccountAsStreamUseCase(get()) } + factory { GetProjectSpacesWithSpecialsForAccountAsStreamUseCase(get()) } // Transfers factory { CancelDownloadForFileUseCase(get()) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt index acb545c0479..5924b59bbea 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt @@ -23,13 +23,11 @@ package com.owncloud.android.presentation.spaces import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import com.bumptech.glide.Glide -import com.bumptech.glide.load.engine.DiskCacheStrategy import com.owncloud.android.databinding.SpacesListItemBinding import com.owncloud.android.domain.spaces.model.OCSpace import com.owncloud.android.utils.PreferenceUtils -class SpacesListAdapter() : RecyclerView.Adapter() { +class SpacesListAdapter : RecyclerView.Adapter() { private val spacesList = mutableListOf() @@ -47,15 +45,10 @@ class SpacesListAdapter() : RecyclerView.Adapter() { spacesListItemName.text = space.name spacesListItemSubtitle.text = space.description - space.getSpaceImageUrl()?.let { - Glide.with(holder.itemView) - .load(it) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .into(spacesListItemImage) - } + space.getSpaceImageWebDavUrl()?.let { + } } - } fun setData(spaces: List) { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt index bf2b11ba4c0..a10e980492e 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListViewModel.kt @@ -24,7 +24,7 @@ import android.accounts.Account import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.owncloud.android.domain.spaces.model.OCSpace -import com.owncloud.android.domain.spaces.usecases.GetProjectSpacesForAccountAsStreamUseCase +import com.owncloud.android.domain.spaces.usecases.GetProjectSpacesWithSpecialsForAccountAsStreamUseCase import com.owncloud.android.domain.spaces.usecases.RefreshSpacesFromServerAsyncUseCase import com.owncloud.android.providers.CoroutinesDispatcherProvider import kotlinx.coroutines.flow.MutableStateFlow @@ -34,7 +34,7 @@ import kotlinx.coroutines.launch class SpacesListViewModel( private val refreshSpacesFromServerAsyncUseCase: RefreshSpacesFromServerAsyncUseCase, - getProjectSpacesForAccountAsStreamUseCase: GetProjectSpacesForAccountAsStreamUseCase, + getProjectSpacesWithSpecialsForAccountAsStreamUseCase: GetProjectSpacesWithSpecialsForAccountAsStreamUseCase, coroutinesDispatcherProvider: CoroutinesDispatcherProvider, private val account: Account, ) : ViewModel() { @@ -43,8 +43,8 @@ class SpacesListViewModel( init { viewModelScope.launch(coroutinesDispatcherProvider.io) { refreshSpacesFromServerAsyncUseCase.execute(RefreshSpacesFromServerAsyncUseCase.Params(account.name)) - spacesList.update { getProjectSpacesForAccountAsStreamUseCase.execute( - GetProjectSpacesForAccountAsStreamUseCase.Params( + spacesList.update { getProjectSpacesWithSpecialsForAccountAsStreamUseCase.execute( + GetProjectSpacesWithSpecialsForAccountAsStreamUseCase.Params( accountName = account.name )).first() } } diff --git a/owncloudApp/src/main/res/layout/spaces_list_item.xml b/owncloudApp/src/main/res/layout/spaces_list_item.xml index 8665bd5393b..855dbd4db9a 100644 --- a/owncloudApp/src/main/res/layout/spaces_list_item.xml +++ b/owncloudApp/src/main/res/layout/spaces_list_item.xml @@ -45,6 +45,9 @@ android:layout_width="match_parent" android:layout_height="150dp" android:src="@drawable/ic_spaces" + android:scaleType="center" + android:scaleX="2" + android:scaleY="2" app:tint="@color/spaces_card_default_image_color" /> ) - fun getProjectSpacesForAccountAsFlow(accountName: String): Flow> + fun getProjectSpacesWithSpecialsForAccountAsFlow(accountName: String): Flow> } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt index 8704626a0b6..ff846f51e90 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/datasources/implementation/OCLocalSpacesDataSource.kt @@ -27,11 +27,14 @@ import com.owncloud.android.data.spaces.db.SpaceRootEntity import com.owncloud.android.data.spaces.db.SpaceSpecialEntity import com.owncloud.android.data.spaces.db.SpacesDao import com.owncloud.android.data.spaces.db.SpacesEntity +import com.owncloud.android.data.spaces.db.SpacesWithSpecials import com.owncloud.android.domain.spaces.model.OCSpace +import com.owncloud.android.domain.spaces.model.SpaceFile import com.owncloud.android.domain.spaces.model.SpaceOwner import com.owncloud.android.domain.spaces.model.SpaceQuota import com.owncloud.android.domain.spaces.model.SpaceRoot import com.owncloud.android.domain.spaces.model.SpaceSpecial +import com.owncloud.android.domain.spaces.model.SpaceSpecialFolder import com.owncloud.android.domain.spaces.model.SpaceUser import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map @@ -54,28 +57,28 @@ class OCLocalSpacesDataSource( spacesDao.insertOrDeleteSpaces(spaceEntities, spaceSpecialEntities) } - override fun getProjectSpacesForAccountAsFlow(accountName: String): Flow> { - return spacesDao.getProjectSpacesForAccountAsFlow(accountName).map { spacesEntitiesList -> - spacesEntitiesList.map { spacesEntity -> - spacesEntity.toModel() + override fun getProjectSpacesWithSpecialsForAccountAsFlow(accountName: String): Flow> { + return spacesDao.getProjectSpacesWithSpecialsForAccountAsFlow(accountName).map { spacesWithSpecialsEntitiesList -> + spacesWithSpecialsEntitiesList.map { spacesWithSpecialsEntity -> + spacesWithSpecialsEntity.toModel() } } } - private fun SpacesEntity.toModel() = + private fun SpacesWithSpecials.toModel() = OCSpace( - accountName = accountName, - driveAlias = driveAlias, - driveType = driveType, - id = id, - lastModifiedDateTime = lastModifiedDateTime, - name = name, + accountName = space.accountName, + driveAlias = space.driveAlias, + driveType = space.driveType, + id = space.id, + lastModifiedDateTime = space.lastModifiedDateTime, + name = space.name, owner = SpaceOwner( user = SpaceUser( - id = ownerId + id = space.ownerId ) ), - quota = quota?.let { + quota = space.quota?.let { SpaceQuota( remaining = it.remaining, state = it.state, @@ -83,7 +86,7 @@ class OCLocalSpacesDataSource( used = it.used ) }, - root = root!!.let { + root = space.root!!.let { SpaceRoot( eTag = it.eTag, id = it.id, @@ -91,9 +94,27 @@ class OCLocalSpacesDataSource( webDavUrl = it.webDavUrl ) }, - webUrl = webUrl, - description = description, - special = null + webUrl = space.webUrl, + description = space.description, + special = specials.map { + it.toModel() + } + ) + + private fun SpaceSpecialEntity.toModel() = + SpaceSpecial( + eTag = eTag, + file = SpaceFile( + mimeType = fileMimeType + ), + id = id, + lastModifiedDateTime = lastModifiedDateTime, + name = name, + size = size, + specialFolder = SpaceSpecialFolder( + name = specialFolderName + ), + webDavUrl = webDavUrl ) private fun OCSpace.toEntity() = @@ -120,7 +141,7 @@ class OCLocalSpacesDataSource( accountName = accountName, spaceId = spaceId, eTag = eTag, - fileMymeType = file.mimeType, + fileMimeType = file.mimeType, id = id, lastModifiedDateTime = lastModifiedDateTime, name = name, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt index 1b154bcac1b..15c38bcac82 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpaceSpecialEntity.kt @@ -50,7 +50,7 @@ data class SpaceSpecialEntity( @ColumnInfo(name = SPACES_SPECIAL_ETAG) val eTag: String, @ColumnInfo(name = SPACES_SPECIAL_FILE_MIME_TYPE) - val fileMymeType: String, + val fileMimeType: String, @ColumnInfo(name = SPACES_SPECIAL_ID) val id: String, @ColumnInfo(name = SPACES_LAST_MODIFIED_DATE_TIME) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt index 04c239721b9..cc6ab5f9892 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt @@ -77,9 +77,9 @@ interface SpacesDao { ): List @Query(SELECT_PROJECT_SPACES_FOR_ACCOUNT) - fun getProjectSpacesForAccountAsFlow( + fun getProjectSpacesWithSpecialsForAccountAsFlow( accountName: String, - ): Flow> + ): Flow> @Query(DELETE_ALL_SPACES_FOR_ACCOUNT) fun deleteSpacesForAccount(accountName: String) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt index 2b37c83170c..5afd4fe8e52 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/repository/OCSpacesRepository.kt @@ -35,6 +35,6 @@ class OCSpacesRepository( } } - override fun getProjectSpacesForAccountAsFlow(accountName: String) = - localSpacesDataSource.getProjectSpacesForAccountAsFlow(accountName) + override fun getProjectSpacesWithSpecialsForAccountAsFlow(accountName: String) = + localSpacesDataSource.getProjectSpacesWithSpecialsForAccountAsFlow(accountName) } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt index aceac27c056..9f9b480cd40 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/SpacesRepository.kt @@ -26,5 +26,5 @@ import kotlinx.coroutines.flow.Flow interface SpacesRepository { fun refreshSpacesForAccount(accountName: String) - fun getProjectSpacesForAccountAsFlow(accountName: String): Flow> + fun getProjectSpacesWithSpecialsForAccountAsFlow(accountName: String): Flow> } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt index 4a0e164fd01..2f14c167166 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/model/OCSpace.kt @@ -43,11 +43,11 @@ data class OCSpace( private const val DRIVE_TYPE_PROJECT = "project" } - fun getSpaceImageUrl(): String? { + fun getSpaceImageWebDavUrl(): String? { val imageSpecial = special?.filter { it.specialFolder.name == "image" } - return imageSpecial?.first()?.webDavUrl + return if (!imageSpecial.isNullOrEmpty()) imageSpecial.first().webDavUrl else null } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetProjectSpacesForAccountAsStreamUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetProjectSpacesWithSpecialsForAccountAsStreamUseCase.kt similarity index 83% rename from owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetProjectSpacesForAccountAsStreamUseCase.kt rename to owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetProjectSpacesWithSpecialsForAccountAsStreamUseCase.kt index fb85bf876ed..92f3db4f63d 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetProjectSpacesForAccountAsStreamUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/spaces/usecases/GetProjectSpacesWithSpecialsForAccountAsStreamUseCase.kt @@ -25,11 +25,11 @@ import com.owncloud.android.domain.spaces.SpacesRepository import com.owncloud.android.domain.spaces.model.OCSpace import kotlinx.coroutines.flow.Flow -class GetProjectSpacesForAccountAsStreamUseCase( +class GetProjectSpacesWithSpecialsForAccountAsStreamUseCase( private val spacesRepository: SpacesRepository -) : BaseUseCase>, GetProjectSpacesForAccountAsStreamUseCase.Params>() { +) : BaseUseCase>, GetProjectSpacesWithSpecialsForAccountAsStreamUseCase.Params>() { - override fun run(params: Params) = spacesRepository.getProjectSpacesForAccountAsFlow(params.accountName) + override fun run(params: Params) = spacesRepository.getProjectSpacesWithSpecialsForAccountAsFlow(params.accountName) data class Params( val accountName: String From 52b7238a60c8f0207bde97b300f6b0d5079da4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Tue, 10 Jan 2023 14:12:37 +0100 Subject: [PATCH 09/20] Print the space image --- .../datamodel/ThumbnailsCacheManager.java | 61 +++++++++++++++++++ .../presentation/spaces/SpacesListAdapter.kt | 33 +++++++++- .../src/main/res/layout/spaces_list_item.xml | 5 +- .../android/domain/spaces/model/OCSpace.kt | 4 +- 4 files changed, 96 insertions(+), 7 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/owncloudApp/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index a30646e4352..848cda72494 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/owncloudApp/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -39,6 +39,7 @@ import com.owncloud.android.R; import com.owncloud.android.domain.files.model.OCFile; import com.owncloud.android.domain.files.usecases.DisableThumbnailsForFileUseCase; +import com.owncloud.android.domain.spaces.model.SpaceSpecial; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.SingleSessionManager; @@ -75,6 +76,7 @@ public class ThumbnailsCacheManager { private static OwnCloudClient mClient = null; private static final String PREVIEW_URI = "%s/remote.php/dav/files/%s%s?x=%d&y=%d&c=%s&preview=1"; + private static final String SPACE_SPECIAL_URI = "%s?scalingup=0&a=1&x=%d&y=%d&c=%s&preview=1"; public static Bitmap mDefaultImg = BitmapFactory.decodeResource( @@ -186,6 +188,8 @@ protected Bitmap doInBackground(Object... params) { thumbnail = doOCFileInBackground(); } else if (mFile instanceof File) { thumbnail = doFileInBackground(); + } else if (mFile instanceof SpaceSpecial) { + thumbnail = doSpaceImageInBackground(); //} else { do nothing } @@ -210,6 +214,8 @@ protected void onPostExecute(Bitmap bitmap) { tagId = String.valueOf(((OCFile) mFile).getId()); } else if (mFile instanceof File) { tagId = String.valueOf(mFile.hashCode()); + } else if (mFile instanceof SpaceSpecial) { + tagId = ((SpaceSpecial) mFile).getId(); } if (String.valueOf(imageView.getTag()).equals(tagId)) { imageView.setImageBitmap(bitmap); @@ -349,6 +355,61 @@ private Bitmap doFileInBackground() { return thumbnail; } + private String getSpaceSpecialUri(SpaceSpecial spaceSpecial) { + return String.format(Locale.ROOT, + SPACE_SPECIAL_URI, + spaceSpecial.getWebDavUrl(), + getThumbnailDimension(), + getThumbnailDimension(), + spaceSpecial.getETag()); + } + + private Bitmap doSpaceImageInBackground() { + SpaceSpecial spaceSpecial = (SpaceSpecial) mFile; + + final String imageKey = spaceSpecial.getId(); + + // Check disk cache in background thread + Bitmap thumbnail = getBitmapFromDiskCache(imageKey); + + // Not found in disk cache + if (thumbnail == null) { // TODO: Check if the current thumbnail is outdated + int px = getThumbnailDimension(); + + // Download thumbnail from server + if (mClient != null) { + GetMethod get; + try { + String uri = getSpaceSpecialUri(spaceSpecial); + Timber.d("URI: %s", uri); + get = new GetMethod(new URL(uri)); + int status = mClient.executeHttpMethod(get); + if (status == HttpConstants.HTTP_OK) { + InputStream inputStream = get.getResponseBodyAsStream(); + Bitmap bitmap = BitmapFactory.decodeStream(inputStream); + thumbnail = ThumbnailUtils.extractThumbnail(bitmap, px, px); + + // Handle PNG + if (spaceSpecial.getFile().getMimeType().equalsIgnoreCase("image/png")) { + thumbnail = handlePNG(thumbnail, px); + } + + // Add thumbnail to cache + if (thumbnail != null) { + addBitmapToCache(imageKey, thumbnail); + } + } else { + mClient.exhaustResponse(get.getResponseBodyAsStream()); + } + } catch (Exception e) { + Timber.e(e); + } + } + } + + return thumbnail; + + } } public static boolean cancelPotentialThumbnailWork(Object file, ImageView imageView) { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt index 5924b59bbea..b3caba81221 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt @@ -22,9 +22,14 @@ package com.owncloud.android.presentation.spaces import android.view.LayoutInflater import android.view.ViewGroup +import android.widget.ImageView +import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView +import com.owncloud.android.R import com.owncloud.android.databinding.SpacesListItemBinding +import com.owncloud.android.datamodel.ThumbnailsCacheManager import com.owncloud.android.domain.spaces.model.OCSpace +import com.owncloud.android.presentation.authentication.AccountUtils import com.owncloud.android.utils.PreferenceUtils class SpacesListAdapter : RecyclerView.Adapter() { @@ -45,8 +50,34 @@ class SpacesListAdapter : RecyclerView.Adapter() { spacesListItemName.text = space.name spacesListItemSubtitle.text = space.description - space.getSpaceImageWebDavUrl()?.let { + val spaceSpecialImage = space.getSpaceSpecialImage() + spacesListItemImage.tag = spaceSpecialImage?.id + if (spaceSpecialImage != null) { + val thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(spaceSpecialImage.id) + if (thumbnail != null) { + spacesListItemImage.run { + setImageBitmap(thumbnail) + scaleType = ImageView.ScaleType.CENTER_CROP + } + } + if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(spaceSpecialImage, spacesListItemImage)) { + val account = AccountUtils.getOwnCloudAccountByName(spacesViewHolder.itemView.context, space.accountName) + val task = ThumbnailsCacheManager.ThumbnailGenerationTask(spacesListItemImage, account) + val asyncDrawable = ThumbnailsCacheManager.AsyncThumbnailDrawable(spacesViewHolder.itemView.resources, thumbnail, task) + + // If drawable is not visible, do not update it. + if (asyncDrawable.minimumHeight > 0 && asyncDrawable.minimumWidth > 0) { + spacesListItemImage.run { + spacesListItemImage.setImageDrawable(asyncDrawable) + scaleType = ImageView.ScaleType.CENTER_CROP + } + } + task.execute(spaceSpecialImage) + } + if (spaceSpecialImage.file.mimeType == "image/png") { + spacesListItemImage.setBackgroundColor(ContextCompat.getColor(spacesViewHolder.itemView.context, R.color.background_color)) + } } } } diff --git a/owncloudApp/src/main/res/layout/spaces_list_item.xml b/owncloudApp/src/main/res/layout/spaces_list_item.xml index 855dbd4db9a..1847d8eda5f 100644 --- a/owncloudApp/src/main/res/layout/spaces_list_item.xml +++ b/owncloudApp/src/main/res/layout/spaces_list_item.xml @@ -45,10 +45,7 @@ android:layout_width="match_parent" android:layout_height="150dp" android:src="@drawable/ic_spaces" - android:scaleType="center" - android:scaleX="2" - android:scaleY="2" - app:tint="@color/spaces_card_default_image_color" /> + android:scaleType="center" /> Date: Tue, 10 Jan 2023 14:13:08 +0100 Subject: [PATCH 10/20] Reformat xml --- .../src/main/res/layout/spaces_list_item.xml | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/owncloudApp/src/main/res/layout/spaces_list_item.xml b/owncloudApp/src/main/res/layout/spaces_list_item.xml index 1847d8eda5f..b54538f9fa2 100644 --- a/owncloudApp/src/main/res/layout/spaces_list_item.xml +++ b/owncloudApp/src/main/res/layout/spaces_list_item.xml @@ -1,5 +1,4 @@ - - Found %1$s of data on your external storage. It will be moved to the safe storage on your device. Remaining files will be cleaned from your external storage after the migration to avoid duplicates and vulnerability. From 00bacc8bbd10965f8344030c888e743f9cb3e91a Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Tue, 10 Jan 2023 17:28:21 +0100 Subject: [PATCH 14/20] Added DiffUtil to spaces list adapter --- .../presentation/spaces/SpacesListAdapter.kt | 8 +-- .../presentation/spaces/SpacesListDiffUtil.kt | 52 +++++++++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) create mode 100644 owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListDiffUtil.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt index beab16ed048..c9ab83bbeba 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListAdapter.kt @@ -24,6 +24,7 @@ import android.view.LayoutInflater import android.view.ViewGroup import android.widget.ImageView import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.owncloud.android.R import com.owncloud.android.databinding.SpacesListItemBinding @@ -83,12 +84,13 @@ class SpacesListAdapter : RecyclerView.Adapter() { } fun setData(spaces: List) { - spacesList.clear() - // Let's filter the ones that are disabled for the moment. We may show them as disabled in the future. val onlyEnabledSpaces = spaces.filterNot { it.isDisabled } + val diffCallback = SpacesListDiffUtil(spacesList, onlyEnabledSpaces) + val diffResult = DiffUtil.calculateDiff(diffCallback) + spacesList.clear() spacesList.addAll(onlyEnabledSpaces) - notifyDataSetChanged() + diffResult.dispatchUpdatesTo(this) } override fun getItemCount(): Int = spacesList.size diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListDiffUtil.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListDiffUtil.kt new file mode 100644 index 00000000000..532c59f5a0a --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/spaces/SpacesListDiffUtil.kt @@ -0,0 +1,52 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2023 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.presentation.spaces + +import androidx.recyclerview.widget.DiffUtil +import com.owncloud.android.domain.spaces.model.OCSpace + +class SpacesListDiffUtil( + private val oldList: List, + private val newList: List +) : DiffUtil.Callback() { + override fun getOldListSize(): Int = oldList.size + + override fun getNewListSize(): Int = newList.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldItem = oldList[oldItemPosition] + val newItem = newList[newItemPosition] + + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldItem = oldList[oldItemPosition] + val newItem = newList[newItemPosition] + + if ((oldItem.name != newItem.name) || (oldItem.description != newItem.description) || + (oldItem.getSpaceSpecialImage()?.id != newItem.getSpaceSpecialImage()?.id)) { + return false + } + + return true + } +} From 9ee46f842ffa95e1280a2f0b12e21afb24c16a7f Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Tue, 10 Jan 2023 18:03:55 +0100 Subject: [PATCH 15/20] Spaces are sorted alphabetically now --- .../main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt index cc6ab5f9892..8ac89522ae7 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/spaces/db/SpacesDao.kt @@ -104,6 +104,7 @@ interface SpacesDao { SELECT * FROM ${ProviderMeta.ProviderTableMeta.SPACES_TABLE_NAME} WHERE $SPACES_ACCOUNT_NAME = :accountName AND $SPACES_DRIVE_TYPE LIKE '$DRIVE_TYPE_PROJECT' + ORDER BY name COLLATE NOCASE ASC """ private const val DELETE_ALL_SPACES_FOR_ACCOUNT = """ From 4ddcdf0a07fdcb1e299360a4f4b5e4d23c644769 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Wed, 11 Jan 2023 09:50:40 +0100 Subject: [PATCH 16/20] Update spaces icon to be the same as in web --- owncloudApp/src/main/res/drawable/ic_spaces.xml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/owncloudApp/src/main/res/drawable/ic_spaces.xml b/owncloudApp/src/main/res/drawable/ic_spaces.xml index f7893d63d89..1d062030439 100644 --- a/owncloudApp/src/main/res/drawable/ic_spaces.xml +++ b/owncloudApp/src/main/res/drawable/ic_spaces.xml @@ -1,9 +1,4 @@ - - + + From 3b2355de7f2ddd95c89d3517e66ac326701c6b91 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Wed, 11 Jan 2023 10:13:07 +0100 Subject: [PATCH 17/20] Fix bug with spaces tab when rotating the screen --- .../java/com/owncloud/android/ui/activity/DrawerActivity.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt index 5be87abac39..d0a61713a3f 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt @@ -224,6 +224,9 @@ abstract class DrawerActivity : ToolbarActivity() { // Allow or disallow touches with other visible windows getBottomNavigationView()?.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(this) if (account != null) { + capabilitiesViewModel.capabilities.value?.let { + setSpacesVisibilityBottomBar(it.peekContent()) + } capabilitiesViewModel.capabilities.observe(this, Event.EventObserver { uiResult: UIResult -> setSpacesVisibilityBottomBar(uiResult) }) From bb0260d3b529466fa75f4b87b73bb16b5dc5e113 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Wed, 11 Jan 2023 10:39:56 +0100 Subject: [PATCH 18/20] Make space card background color brandable --- owncloudApp/src/main/res/values/colors.xml | 1 - owncloudApp/src/main/res/values/setup.xml | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudApp/src/main/res/values/colors.xml b/owncloudApp/src/main/res/values/colors.xml index 2698497be5b..9af136159ab 100644 --- a/owncloudApp/src/main/res/values/colors.xml +++ b/owncloudApp/src/main/res/values/colors.xml @@ -69,6 +69,5 @@ #6E758C - #edf3fa #4c5f79 diff --git a/owncloudApp/src/main/res/values/setup.xml b/owncloudApp/src/main/res/values/setup.xml index ac582d36a12..f2bba3ce67b 100644 --- a/owncloudApp/src/main/res/values/setup.xml +++ b/owncloudApp/src/main/res/values/setup.xml @@ -45,6 +45,7 @@ #D6D7D7 @color/black @color/owncloud_blue_accent + #edf3fa @color/owncloud_blue From 89f504fa54f87847b73a5810ebb79bdffa82a695 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Wed, 11 Jan 2023 12:16:40 +0100 Subject: [PATCH 19/20] Simplify observation of capabilities from DrawerActivity --- .../com/owncloud/android/ui/activity/DrawerActivity.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt index d0a61713a3f..0033b651799 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.kt @@ -224,12 +224,9 @@ abstract class DrawerActivity : ToolbarActivity() { // Allow or disallow touches with other visible windows getBottomNavigationView()?.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(this) if (account != null) { - capabilitiesViewModel.capabilities.value?.let { - setSpacesVisibilityBottomBar(it.peekContent()) + capabilitiesViewModel.capabilities.observe(this) { event: Event> -> + setSpacesVisibilityBottomBar(event.peekContent()) } - capabilitiesViewModel.capabilities.observe(this, Event.EventObserver { uiResult: UIResult -> - setSpacesVisibilityBottomBar(uiResult) - }) } setCheckedItemAtBottomBar(menuItemId) getBottomNavigationView()?.setOnNavigationItemSelectedListener { menuItem: MenuItem -> From 2edfe5d6e5aaec8b5795412ac6959c8de31d2ac7 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Wed, 11 Jan 2023 12:35:10 +0100 Subject: [PATCH 20/20] Removed a color that could be reused --- owncloudApp/src/main/res/layout/spaces_list_item.xml | 2 +- owncloudApp/src/main/res/values/colors.xml | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/owncloudApp/src/main/res/layout/spaces_list_item.xml b/owncloudApp/src/main/res/layout/spaces_list_item.xml index b54538f9fa2..2c4b518a281 100644 --- a/owncloudApp/src/main/res/layout/spaces_list_item.xml +++ b/owncloudApp/src/main/res/layout/spaces_list_item.xml @@ -49,7 +49,7 @@ + android:background="@color/actionbar_start_color" /> #6E758C #6E758C - - - #4c5f79