From 74b4075da6226ea793401d2f607c9cbb700f9434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Thu, 14 Jul 2022 09:40:00 +0200 Subject: [PATCH 01/15] Add a new av offline status, so we can identify if a file needs to be synced --- .../ui/files/filelist/MainFileListFragment.kt | 2 +- .../implementation/OCLocalFileDataSource.kt | 5 +- .../android/data/files/db/OCFileEntity.kt | 8 ++-- .../files/model/AvailableOfflineStatus.kt | 47 +++++++++++++++++++ .../android/domain/files/model/OCFile.kt | 9 ++-- .../android/domain/files/model/OCFileTest.kt | 8 ++-- .../com/owncloud/android/testutil/OCFile.kt | 3 +- 7 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/AvailableOfflineStatus.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt index a6b57100fdc..ada0335b0c9 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt @@ -219,7 +219,7 @@ class MainFileListFragment : Fragment(), } .filter { when (fileListOption) { - FileListOption.AV_OFFLINE -> it.keepInSync == 1 + FileListOption.AV_OFFLINE -> it.isAvailableOffline FileListOption.SHARED_BY_LINK -> it.sharedByLink || it.sharedWithSharee == true else -> true } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt index c1f0b09d1bf..6e409e5e046 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt @@ -24,6 +24,7 @@ import androidx.lifecycle.Transformations import com.owncloud.android.data.files.datasources.LocalFileDataSource import com.owncloud.android.data.files.db.FileDao import com.owncloud.android.data.files.db.OCFileEntity +import com.owncloud.android.domain.files.model.AvailableOfflineStatus import com.owncloud.android.domain.files.model.MIME_DIR import com.owncloud.android.domain.files.model.MIME_PREFIX_IMAGE import com.owncloud.android.domain.files.model.OCFile @@ -159,7 +160,7 @@ class OCLocalFileDataSource( sharedByLink = sharedByLink, sharedWithSharee = sharedWithSharee, storagePath = storagePath, - keepInSync = keepInSync, + availableOfflineStatus = AvailableOfflineStatus.fromValue(availableOfflineStatus), needsToUpdateThumbnail = needsToUpdateThumbnail, fileIsDownloading = fileIsDownloading, lastSyncDateForData = lastSyncDateForData, @@ -186,7 +187,7 @@ class OCLocalFileDataSource( sharedByLink = sharedByLink, sharedWithSharee = sharedWithSharee, storagePath = storagePath, - keepInSync = keepInSync, + availableOfflineStatus = availableOfflineStatus?.ordinal ?: AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE.ordinal, needsToUpdateThumbnail = needsToUpdateThumbnail, fileIsDownloading = fileIsDownloading, lastSyncDateForData = lastSyncDateForData, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt index c43904e8432..79464a2187a 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt @@ -19,6 +19,7 @@ package com.owncloud.android.data.files.db import android.database.Cursor +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.FILES_TABLE_NAME @@ -47,6 +48,7 @@ import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.FILE_TREE_ETAG import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.FILE_UPDATE_THUMBNAIL import com.owncloud.android.data.ProviderMeta.ProviderTableMeta._ID import com.owncloud.android.domain.ext.isOneOf +import com.owncloud.android.domain.files.model.AvailableOfflineStatus import com.owncloud.android.domain.files.model.MIME_DIR import com.owncloud.android.domain.files.model.MIME_DIR_UNIX @@ -69,8 +71,8 @@ data class OCFileEntity( var name: String? = null, val treeEtag: String? = null, - //TODO: May not needed - val keepInSync: Int? = null, + @ColumnInfo(name = "keepInSync") + val availableOfflineStatus: Int? = null, val lastSyncDateForData: Long? = null, val fileShareViaLink: Int? = null, var lastSyncDateForProperties: Long? = null, @@ -111,7 +113,7 @@ data class OCFileEntity( treeEtag = cursor.getString(cursor.getColumnIndexOrThrow(FILE_TREE_ETAG)), lastSyncDateForProperties = cursor.getLong(cursor.getColumnIndexOrThrow(FILE_LAST_SYNC_DATE)), lastSyncDateForData = cursor.getLong(cursor.getColumnIndexOrThrow(FILE_LAST_SYNC_DATE_FOR_DATA)), - keepInSync = cursor.getInt(cursor.getColumnIndexOrThrow(FILE_KEEP_IN_SYNC)), + availableOfflineStatus = cursor.getInt(cursor.getColumnIndexOrThrow(FILE_KEEP_IN_SYNC)), fileShareViaLink = cursor.getInt(cursor.getColumnIndexOrThrow(FILE_SHARED_VIA_LINK)), needsToUpdateThumbnail = cursor.getInt(cursor.getColumnIndexOrThrow(FILE_UPDATE_THUMBNAIL)) == 1, modifiedAtLastSyncForData = cursor.getLong(cursor.getColumnIndexOrThrow(FILE_MODIFIED_AT_LAST_SYNC_FOR_DATA)), diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/AvailableOfflineStatus.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/AvailableOfflineStatus.kt new file mode 100644 index 00000000000..a6a58a8dc7c --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/AvailableOfflineStatus.kt @@ -0,0 +1,47 @@ +/** + * ownCloud Android client application + * + * @author Abel García de Prada + * Copyright (C) 2020 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.files.model + + +enum class AvailableOfflineStatus { + /** + * File is not available offline + */ + NOT_AVAILABLE_OFFLINE, + + /** + * File is available offline + */ + AVAILABLE_OFFLINE, + + /** + * File belongs to an available offline folder + */ + AVAILABLE_OFFLINE_PARENT; + + companion object { + fun fromValue(value: Int?): AvailableOfflineStatus { + return when (value) { + AVAILABLE_OFFLINE.ordinal -> AVAILABLE_OFFLINE + AVAILABLE_OFFLINE_PARENT.ordinal -> AVAILABLE_OFFLINE_PARENT + else -> NOT_AVAILABLE_OFFLINE + } + } + } +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt index 2e88ff94352..b619553057e 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt @@ -23,11 +23,12 @@ package com.owncloud.android.domain.files.model import android.os.Parcelable import android.webkit.MimeTypeMap import com.owncloud.android.domain.ext.isOneOf +import com.owncloud.android.domain.files.model.AvailableOfflineStatus.AVAILABLE_OFFLINE +import com.owncloud.android.domain.files.model.AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT import kotlinx.parcelize.Parcelize import java.io.File import java.util.Locale -//TODO: Add new attributes on demand. Let's try to perform a clean up :) @Parcelize data class OCFile( var id: Long? = null, @@ -45,8 +46,7 @@ data class OCFile( var storagePath: String? = null, var treeEtag: String? = "", - //TODO: May not needed - val keepInSync: Int? = null, + val availableOfflineStatus: AvailableOfflineStatus? = null, var lastSyncDateForData: Long? = 0, var lastSyncDateForProperties: Long? = 0, var needsToUpdateThumbnail: Boolean = false, @@ -142,6 +142,9 @@ data class OCFile( val isSharedWithMe get() = permissions != null && permissions.contains(PERMISSION_SHARED_WITH_ME) + val isAvailableOffline + get() = availableOfflineStatus?.isOneOf(AVAILABLE_OFFLINE, AVAILABLE_OFFLINE_PARENT) ?: false + val localModificationTimestamp: Long get() = storagePath?.takeIf { diff --git a/owncloudDomain/src/test/java/com/owncloud/android/domain/files/model/OCFileTest.kt b/owncloudDomain/src/test/java/com/owncloud/android/domain/files/model/OCFileTest.kt index db2c256e4d6..70bdb558337 100644 --- a/owncloudDomain/src/test/java/com/owncloud/android/domain/files/model/OCFileTest.kt +++ b/owncloudDomain/src/test/java/com/owncloud/android/domain/files/model/OCFileTest.kt @@ -45,7 +45,7 @@ class OCFileTest { OC_FILE.privateLink, OC_FILE.storagePath, OC_FILE.treeEtag, - OC_FILE.keepInSync, + OC_FILE.availableOfflineStatus, OC_FILE.lastSyncDateForData, OC_FILE.lastSyncDateForProperties, OC_FILE.needsToUpdateThumbnail, @@ -71,7 +71,7 @@ class OCFileTest { privateLink = OC_FILE.privateLink, storagePath = OC_FILE.storagePath, treeEtag = OC_FILE.treeEtag, - keepInSync = OC_FILE.keepInSync, + availableOfflineStatus = OC_FILE.availableOfflineStatus, lastSyncDateForData = OC_FILE.lastSyncDateForData, lastSyncDateForProperties = OC_FILE.lastSyncDateForProperties, needsToUpdateThumbnail = OC_FILE.needsToUpdateThumbnail, @@ -103,7 +103,7 @@ class OCFileTest { OC_FILE.privateLink, OC_FILE.storagePath, OC_FILE.treeEtag, - OC_FILE.keepInSync, + OC_FILE.availableOfflineStatus, OC_FILE.lastSyncDateForData, OC_FILE.lastSyncDateForProperties, OC_FILE.needsToUpdateThumbnail, @@ -129,7 +129,7 @@ class OCFileTest { privateLink = OC_FILE.privateLink, storagePath = OC_FILE.storagePath, treeEtag = OC_FILE.treeEtag, - keepInSync = OC_FILE.keepInSync, + availableOfflineStatus = OC_FILE.availableOfflineStatus, lastSyncDateForData = OC_FILE.lastSyncDateForData, lastSyncDateForProperties = OC_FILE.lastSyncDateForProperties, needsToUpdateThumbnail = OC_FILE.needsToUpdateThumbnail, diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt index 9abfc452f83..5eb979143ab 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt @@ -18,6 +18,7 @@ */ package com.owncloud.android.testutil +import com.owncloud.android.domain.files.model.AvailableOfflineStatus import com.owncloud.android.domain.files.model.OCFile val OC_FOLDER = OCFile( @@ -62,7 +63,7 @@ val OC_AVAILABLE_OFFLINE_FILE = OCFile( modificationTimestamp = 1593510589000, etag = "5efb0c13c688f", mimeType = "image/jpeg", - keepInSync = 1, + availableOfflineStatus = AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT, length = 3000000 ) From 65366f51443c69e42462589d736ddf05629deade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Fri, 15 Jul 2022 08:26:57 +0200 Subject: [PATCH 02/15] When syncing a folder, available offline must be synced too --- .../usecases/synchronization/SynchronizeFolderUseCase.kt | 7 +++++-- .../com/owncloud/android/data/files/db/OCFileEntity.kt | 1 - .../java/com/owncloud/android/domain/files/model/OCFile.kt | 5 ++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/synchronization/SynchronizeFolderUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/synchronization/SynchronizeFolderUseCase.kt index 8e95da21520..fb810d25564 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/usecases/synchronization/SynchronizeFolderUseCase.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/synchronization/SynchronizeFolderUseCase.kt @@ -40,7 +40,7 @@ class SynchronizeFolderUseCase( folderContent.forEach { ocFile -> if (ocFile.isFolder) { - if (params.syncMode.isOneOf(REFRESH_FOLDER_RECURSIVELY, SYNC_FOLDER_RECURSIVELY)) { + if (shouldSyncFolder(params.syncMode, ocFile)) { SynchronizeFolderUseCase(synchronizeFileUseCase, fileRepository).execute( Params( remotePath = ocFile.remotePath, @@ -59,8 +59,11 @@ class SynchronizeFolderUseCase( } } + private fun shouldSyncFolder(syncMode: SyncFolderMode, ocFolder: OCFile) = + syncMode.isOneOf(REFRESH_FOLDER_RECURSIVELY, SYNC_FOLDER_RECURSIVELY) || syncMode == SYNC_CONTENTS && ocFolder.isAvailableOffline + private fun shouldSyncFile(syncMode: SyncFolderMode, ocFile: OCFile) = - syncMode == SYNC_FOLDER_RECURSIVELY || (syncMode == SYNC_CONTENTS && ocFile.isAvailableLocally) + syncMode == SYNC_FOLDER_RECURSIVELY || (syncMode == SYNC_CONTENTS && (ocFile.isAvailableLocally || ocFile.isAvailableOffline)) data class Params( val remotePath: String, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt index 79464a2187a..5085773de37 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt @@ -48,7 +48,6 @@ import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.FILE_TREE_ETAG import com.owncloud.android.data.ProviderMeta.ProviderTableMeta.FILE_UPDATE_THUMBNAIL import com.owncloud.android.data.ProviderMeta.ProviderTableMeta._ID import com.owncloud.android.domain.ext.isOneOf -import com.owncloud.android.domain.files.model.AvailableOfflineStatus import com.owncloud.android.domain.files.model.MIME_DIR import com.owncloud.android.domain.files.model.MIME_DIR_UNIX diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt index b619553057e..05720e91f5c 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt @@ -46,7 +46,7 @@ data class OCFile( var storagePath: String? = null, var treeEtag: String? = "", - val availableOfflineStatus: AvailableOfflineStatus? = null, + var availableOfflineStatus: AvailableOfflineStatus? = null, var lastSyncDateForData: Long? = 0, var lastSyncDateForProperties: Long? = 0, var needsToUpdateThumbnail: Boolean = false, @@ -161,8 +161,7 @@ data class OCFile( storagePath = sourceFile.storagePath treeEtag = sourceFile.treeEtag etagInConflict = sourceFile.etagInConflict - // FIXME: 19/10/2020 : New_arch: Av.Offline -// setAvailableOfflineStatus(sourceFile.getAvailableOfflineStatus()) + availableOfflineStatus = sourceFile.availableOfflineStatus } /** From 88aceeb9a7e0adac495fc2201b670645b675ae81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Sat, 16 Jul 2022 08:43:01 +0200 Subject: [PATCH 03/15] First draft to set or unset a single file as available offline --- .../dependecyinjection/RepositoryModule.kt | 3 ++ .../dependecyinjection/UseCaseModule.kt | 6 +++ .../dependecyinjection/ViewModelModule.kt | 2 +- .../android/files/FileMenuFilter.java | 37 +++++++++---------- .../adapters/filelist/FileListAdapter.kt | 3 ++ .../ui/files/filelist/MainFileListFragment.kt | 6 +++ .../ui/files/operations/FileOperation.kt | 2 + .../operations/FileOperationsViewModel.kt | 18 +++++++++ .../AvailableOfflineRepositoryImpl.kt | 37 +++++++++++++++++++ .../files/datasources/LocalFileDataSource.kt | 2 + .../implementation/OCLocalFileDataSource.kt | 6 ++- .../owncloud/android/data/files/db/FileDao.kt | 32 ++++++++++++++++ .../data/files/repository/OCFileRepository.kt | 5 +++ .../AvailableOfflineRepository.kt | 27 ++++++++++++++ .../model/AvailableOfflineStatus.kt | 4 +- .../SetFileAsAvailableOfflineUseCase.kt | 35 ++++++++++++++++++ .../UnsetFileAsAvailableOfflineUseCase.kt | 35 ++++++++++++++++++ .../android/domain/files/FileRepository.kt | 3 ++ .../android/domain/files/model/OCFile.kt | 5 ++- .../com/owncloud/android/testutil/OCFile.kt | 2 +- 20 files changed, 244 insertions(+), 26 deletions(-) create mode 100644 owncloudData/src/main/java/com/owncloud/android/data/availableoffline/AvailableOfflineRepositoryImpl.kt create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/AvailableOfflineRepository.kt rename owncloudDomain/src/main/java/com/owncloud/android/domain/{files => availableoffline}/model/AvailableOfflineStatus.kt (93%) create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFileAsAvailableOfflineUseCase.kt create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFileAsAvailableOfflineUseCase.kt 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 17cd746feb1..468b807d571 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt @@ -23,6 +23,7 @@ package com.owncloud.android.dependecyinjection import com.owncloud.android.data.authentication.repository.OCAuthenticationRepository +import com.owncloud.android.data.availableoffline.AvailableOfflineRepositoryImpl import com.owncloud.android.data.capabilities.repository.OCCapabilityRepository import com.owncloud.android.data.files.repository.OCFileRepository import com.owncloud.android.data.folderbackup.FolderBackupRepositoryImpl @@ -34,6 +35,7 @@ import com.owncloud.android.data.transfers.repository.OCTransferRepository import com.owncloud.android.data.user.repository.OCUserRepository import com.owncloud.android.domain.authentication.AuthenticationRepository import com.owncloud.android.domain.authentication.oauth.OAuthRepository +import com.owncloud.android.domain.availableoffline.AvailableOfflineRepository import com.owncloud.android.domain.camerauploads.FolderBackupRepository import com.owncloud.android.domain.capabilities.CapabilityRepository import com.owncloud.android.domain.files.FileRepository @@ -55,4 +57,5 @@ val repositoryModule = module { factory { OAuthRepositoryImpl(get()) } factory { FolderBackupRepositoryImpl(get()) } factory { OCTransferRepository(get()) } + factory { AvailableOfflineRepositoryImpl(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 e95bc62bda3..9af8dbada03 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -29,6 +29,8 @@ import com.owncloud.android.domain.authentication.usecases.GetBaseUrlUseCase import com.owncloud.android.domain.authentication.usecases.LoginBasicAsyncUseCase import com.owncloud.android.domain.authentication.usecases.LoginOAuthAsyncUseCase import com.owncloud.android.domain.authentication.usecases.SupportsOAuth2UseCase +import com.owncloud.android.domain.availableoffline.usecases.SetFileAsAvailableOfflineUseCase +import com.owncloud.android.domain.availableoffline.usecases.UnsetFileAsAvailableOfflineUseCase import com.owncloud.android.domain.camerauploads.usecases.GetCameraUploadsConfigurationUseCase import com.owncloud.android.domain.camerauploads.usecases.GetPictureUploadsConfigurationStreamUseCase import com.owncloud.android.domain.camerauploads.usecases.GetVideoUploadsConfigurationStreamUseCase @@ -118,6 +120,10 @@ val useCaseModule = module { factory { SynchronizeFolderUseCase(get(), get()) } factory { SortFilesUseCase() } + // Av Offline + factory { SetFileAsAvailableOfflineUseCase(get()) } + factory { UnsetFileAsAvailableOfflineUseCase(get()) } + // Sharing factory { CreatePrivateShareAsyncUseCase(get()) } factory { CreatePublicShareAsyncUseCase(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 328dc7aca50..ee67335e06a 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -86,7 +86,7 @@ val viewModelModule = module { viewModel { PreviewImageViewModel(get(), get(), get()) } viewModel { FileDetailsViewModel(get(), get(), get(), get(), get()) } - viewModel { FileOperationsViewModel(get(), get(), get(), get(), get(), get(), get(), get()) } + viewModel { FileOperationsViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { MainFileListViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { TransfersViewModel(get(), get(), get()) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/files/FileMenuFilter.java b/owncloudApp/src/main/java/com/owncloud/android/files/FileMenuFilter.java index c5500d907b2..9e60015455e 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/files/FileMenuFilter.java +++ b/owncloudApp/src/main/java/com/owncloud/android/files/FileMenuFilter.java @@ -33,6 +33,7 @@ import androidx.work.WorkManager; import com.owncloud.android.R; import com.owncloud.android.domain.capabilities.model.OCCapability; +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus; import com.owncloud.android.domain.files.model.OCFile; import com.owncloud.android.extensions.WorkManagerExtKt; import com.owncloud.android.services.OperationsService.OperationsServiceBinder; @@ -360,32 +361,30 @@ private boolean anyFileDown() { return false; } - // FIXME: 13/10/2020 : New_arch: Av.Offline private boolean anyFavorite() { -// for (OCFile file : mFiles) { -// if (file.getAvailableOfflineStatus() == OCFile.AvailableOfflineStatus.AVAILABLE_OFFLINE) { -// return true; -// } -// } + for (OCFile file : mFiles) { + if (file.getAvailableOfflineStatus() == AvailableOfflineStatus.AVAILABLE_OFFLINE) { + return true; + } + } return false; } - // FIXME: 13/10/2020 : New_arch: Av.Offline private boolean anyUnfavorite() { -// for (OCFile file : mFiles) { -// if (file.getAvailableOfflineStatus() == OCFile.AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE) { -// return true; -// } -// } + for (OCFile file : mFiles) { + if (file.getAvailableOfflineStatus() == AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE) { + return true; + } + } return false; - } - // FIXME: 13/10/2020 : New_arch: Shared by Link + } + private boolean anyFileSharedWithMe() { -// for (OCFile file : mFiles) { -// if (file.isSharedWithMe()) { -// return true; -// } -// } + for (OCFile file : mFiles) { + if (file.isSharedWithMe()) { + return true; + } + } return false; } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/filelist/FileListAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/filelist/FileListAdapter.kt index 2fecc2822b4..352a838cc23 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/filelist/FileListAdapter.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/filelist/FileListAdapter.kt @@ -321,6 +321,9 @@ class FileListAdapter( // conflict localStateView.setImageResource(R.drawable.error_pin) localStateView.visibility = View.VISIBLE + } else if (file.isAvailableOffline) { + localStateView.visibility = View.VISIBLE + localStateView.setImageResource(R.drawable.offline_available_pin) } else if (file.isAvailableLocally) { localStateView.visibility = View.VISIBLE localStateView.setImageResource(R.drawable.downloaded_pin) diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt index ada0335b0c9..c983f8914f8 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt @@ -512,6 +512,12 @@ class MainFileListFragment : Fragment(), } return true } + R.id.action_set_available_offline -> { + fileOperationsViewModel.performOperation(FileOperation.SetFileAsAvailableOffline(singleFile)) + } + R.id.action_unset_available_offline -> { + fileOperationsViewModel.performOperation(FileOperation.UnsetFileAsAvailableOffline(singleFile)) + } } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperation.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperation.kt index 59706fba965..27e32d1d2d0 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperation.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperation.kt @@ -28,4 +28,6 @@ sealed class FileOperation { data class RemoveOperation(val listOfFilesToRemove: List, val removeOnlyLocalCopy: Boolean) : FileOperation() data class RenameOperation(val ocFileToRename: OCFile, val newName: String) : FileOperation() data class SynchronizeFileOperation(val fileToSync: OCFile, val accountName: String) : FileOperation() + data class SetFileAsAvailableOffline(val fileToUpdate: OCFile) : FileOperation() + data class UnsetFileAsAvailableOffline(val fileToUpdate: OCFile) : FileOperation() } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperationsViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperationsViewModel.kt index b3aad7a1cda..9f43f957014 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperationsViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperationsViewModel.kt @@ -25,6 +25,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.owncloud.android.domain.BaseUseCaseWithResult import com.owncloud.android.domain.UseCaseResult +import com.owncloud.android.domain.availableoffline.usecases.SetFileAsAvailableOfflineUseCase +import com.owncloud.android.domain.availableoffline.usecases.UnsetFileAsAvailableOfflineUseCase import com.owncloud.android.domain.exceptions.NoNetworkConnectionException import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.files.usecases.CopyFileUseCase @@ -48,6 +50,8 @@ class FileOperationsViewModel( private val removeFileUseCase: RemoveFileUseCase, private val renameFileUseCase: RenameFileUseCase, private val synchronizeFileUseCase: SynchronizeFileUseCase, + private val setFileAsAvailableOfflineUseCase: SetFileAsAvailableOfflineUseCase, + private val unsetFileAsAvailableOfflineUseCase: UnsetFileAsAvailableOfflineUseCase, private val contextProvider: ContextProvider, private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider ) : ViewModel() { @@ -78,6 +82,8 @@ class FileOperationsViewModel( is FileOperation.CopyOperation -> copyOperation(fileOperation) is FileOperation.SynchronizeFileOperation -> syncFileOperation(fileOperation) is FileOperation.CreateFolder -> createFolderOperation(fileOperation) + is FileOperation.SetFileAsAvailableOffline -> setFileAsAvailableOffline(fileOperation) + is FileOperation.UnsetFileAsAvailableOffline -> unsetFileAsAvailableOffline(fileOperation) } } @@ -136,6 +142,18 @@ class FileOperationsViewModel( ) } + private fun setFileAsAvailableOffline(fileOperation: FileOperation.SetFileAsAvailableOffline) { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + setFileAsAvailableOfflineUseCase.execute(SetFileAsAvailableOfflineUseCase.Params(fileOperation.fileToUpdate)) + } + } + + private fun unsetFileAsAvailableOffline(fileOperation: FileOperation.UnsetFileAsAvailableOffline) { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + unsetFileAsAvailableOfflineUseCase.execute(UnsetFileAsAvailableOfflineUseCase.Params(fileOperation.fileToUpdate)) + } + } + private fun runOperation( liveData: MediatorLiveData>>, useCase: BaseUseCaseWithResult, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/availableoffline/AvailableOfflineRepositoryImpl.kt b/owncloudData/src/main/java/com/owncloud/android/data/availableoffline/AvailableOfflineRepositoryImpl.kt new file mode 100644 index 00000000000..e2f7d58e5bf --- /dev/null +++ b/owncloudData/src/main/java/com/owncloud/android/data/availableoffline/AvailableOfflineRepositoryImpl.kt @@ -0,0 +1,37 @@ +/** + * 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.availableoffline + +import com.owncloud.android.domain.availableoffline.AvailableOfflineRepository +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus +import com.owncloud.android.domain.files.FileRepository +import com.owncloud.android.domain.files.model.OCFile + +class AvailableOfflineRepositoryImpl( + val fileRepository: FileRepository, +) : AvailableOfflineRepository { + override fun setFileAsAvailableOffline(fileToSetAsAvailableOffline: OCFile) { + fileRepository.updateFileWithNewAvailableOfflineStatus(fileToSetAsAvailableOffline, AvailableOfflineStatus.AVAILABLE_OFFLINE) + } + + override fun unsetFileAsAvailableOffline(fileToSetAsAvailableOffline: OCFile) { + fileRepository.updateFileWithNewAvailableOfflineStatus(fileToSetAsAvailableOffline, AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE) + } + +} diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt index 91bab5b7115..0c900833148 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt @@ -21,6 +21,7 @@ package com.owncloud.android.data.files.datasources import androidx.lifecycle.LiveData +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus import com.owncloud.android.domain.files.model.OCFile interface LocalFileDataSource { @@ -41,4 +42,5 @@ interface LocalFileDataSource { fun saveFile(file: OCFile) fun removeFile(fileId: Long) fun renameFile(fileToRename: OCFile, finalRemotePath: String, finalStoragePath: String) + fun updateAvailableOfflineStatusForFile(ocFile: OCFile, newAvailableOfflineStatus: AvailableOfflineStatus) } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt index 6e409e5e046..0b553da0d16 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt @@ -24,7 +24,7 @@ import androidx.lifecycle.Transformations import com.owncloud.android.data.files.datasources.LocalFileDataSource import com.owncloud.android.data.files.db.FileDao import com.owncloud.android.data.files.db.OCFileEntity -import com.owncloud.android.domain.files.model.AvailableOfflineStatus +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus import com.owncloud.android.domain.files.model.MIME_DIR import com.owncloud.android.domain.files.model.MIME_PREFIX_IMAGE import com.owncloud.android.domain.files.model.OCFile @@ -141,6 +141,10 @@ class OCLocalFileDataSource( ) } + override fun updateAvailableOfflineStatusForFile(ocFile: OCFile, newAvailableOfflineStatus: AvailableOfflineStatus) { + fileDao.updateAvailableOfflineStatusForFile(ocFile, newAvailableOfflineStatus.ordinal) + } + companion object { @VisibleForTesting fun OCFileEntity.toModel(): OCFile = diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt index 7345b6e5c30..c5ec6cf2ac6 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt @@ -25,6 +25,8 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction import com.owncloud.android.data.ProviderMeta +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus +import com.owncloud.android.domain.files.model.OCFile import java.io.File.separatorChar @Dao @@ -203,6 +205,31 @@ abstract class FileDao { @Query(DELETE_FILE_WITH_ID) abstract fun deleteFileWithId(id: Long) + @Transaction + open fun updateAvailableOfflineStatusForFile(ocFile: OCFile, newAvailableOfflineStatus: Int) { + if (ocFile.isFolder) { + updateFolderAsAvailableOffline(ocFile.id!!, newAvailableOfflineStatus) + } else { + updateFileWithAvailableOfflineStatus(ocFile.id!!, newAvailableOfflineStatus) + } + } + + private fun updateFolderAsAvailableOffline(ocFolderId: Long, newAvailableOfflineStatus: Int) { + updateFileWithAvailableOfflineStatus(ocFolderId, newAvailableOfflineStatus) + + val folderContent = getFolderContent(ocFolderId) + folderContent.forEach { + if (it.isFolder) { + updateFolderAsAvailableOffline(it.id, AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT.ordinal) + } else { + updateFileWithAvailableOfflineStatus(it.id, AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT.ordinal) + } + } + } + + @Query(UPDATE_FILE_WITH_NEW_AVAILABLE_OFFLINE_STATUS) + abstract fun updateFileWithAvailableOfflineStatus(id: Long, availableOfflineStatus: Int) + private fun moveSingleFile( sourceFile: OCFileEntity, targetFile: OCFileEntity, @@ -327,5 +354,10 @@ abstract class FileDao { "FROM ${ProviderMeta.ProviderTableMeta.FILES_TABLE_NAME} " + "WHERE owner = :accountOwner " + "AND keepInSync = '1'" + + private const val UPDATE_FILE_WITH_NEW_AVAILABLE_OFFLINE_STATUS = + "UPDATE ${ProviderMeta.ProviderTableMeta.FILES_TABLE_NAME} " + + "SET keepInSync = :availableOfflineStatus " + + "WHERE id = :id" } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index d0e11d3038a..ce38b0addaf 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -24,6 +24,7 @@ import androidx.lifecycle.LiveData import com.owncloud.android.data.files.datasources.LocalFileDataSource import com.owncloud.android.data.files.datasources.RemoteFileDataSource import com.owncloud.android.data.storage.LocalStorageProvider +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus import com.owncloud.android.domain.exceptions.ConflictException import com.owncloud.android.domain.exceptions.FileAlreadyExistsException import com.owncloud.android.domain.exceptions.FileNotFoundException @@ -314,6 +315,10 @@ class OCFileRepository( localFileDataSource.saveFile(file) } + override fun updateFileWithNewAvailableOfflineStatus(ocFile: OCFile, newAvailableOfflineStatus: AvailableOfflineStatus) { + localFileDataSource.updateAvailableOfflineStatusForFile(ocFile, newAvailableOfflineStatus) + } + private fun removeLocalFolderRecursively(ocFile: OCFile, onlyFromLocalStorage: Boolean) { val folderContent = localFileDataSource.getFolderContent(ocFile.id!!) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/AvailableOfflineRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/AvailableOfflineRepository.kt new file mode 100644 index 00000000000..7c4c3759b92 --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/AvailableOfflineRepository.kt @@ -0,0 +1,27 @@ +/** + * 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.availableoffline + +import com.owncloud.android.domain.files.model.OCFile + +interface AvailableOfflineRepository { + fun setFileAsAvailableOffline(fileToSetAsAvailableOffline: OCFile) + fun unsetFileAsAvailableOffline(fileToSetAsAvailableOffline: OCFile) + +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/AvailableOfflineStatus.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt similarity index 93% rename from owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/AvailableOfflineStatus.kt rename to owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt index a6a58a8dc7c..7dd8ae7502a 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/AvailableOfflineStatus.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt @@ -2,7 +2,7 @@ * ownCloud Android client application * * @author Abel García de Prada - * Copyright (C) 2020 ownCloud GmbH. + * 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, @@ -16,7 +16,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.files.model +package com.owncloud.android.domain.availableoffline.model enum class AvailableOfflineStatus { diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFileAsAvailableOfflineUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFileAsAvailableOfflineUseCase.kt new file mode 100644 index 00000000000..0a2b085c70d --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFileAsAvailableOfflineUseCase.kt @@ -0,0 +1,35 @@ +/** + * 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.availableoffline.usecases + +import com.owncloud.android.domain.BaseUseCaseWithResult +import com.owncloud.android.domain.availableoffline.AvailableOfflineRepository +import com.owncloud.android.domain.files.model.OCFile + +class SetFileAsAvailableOfflineUseCase( + private val availableOfflineRepository: AvailableOfflineRepository, +) : BaseUseCaseWithResult() { + + override fun run(params: Params) = + availableOfflineRepository.setFileAsAvailableOffline(params.fileToSetAsAvailableOffline) + + data class Params( + val fileToSetAsAvailableOffline: OCFile + ) +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFileAsAvailableOfflineUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFileAsAvailableOfflineUseCase.kt new file mode 100644 index 00000000000..d3b5af8921a --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFileAsAvailableOfflineUseCase.kt @@ -0,0 +1,35 @@ +/** + * 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.availableoffline.usecases + +import com.owncloud.android.domain.BaseUseCaseWithResult +import com.owncloud.android.domain.availableoffline.AvailableOfflineRepository +import com.owncloud.android.domain.files.model.OCFile + +class UnsetFileAsAvailableOfflineUseCase( + private val availableOfflineRepository: AvailableOfflineRepository, +) : BaseUseCaseWithResult() { + + override fun run(params: Params) = + availableOfflineRepository.unsetFileAsAvailableOffline(params.fileToUnsetAsAvailableOffline) + + data class Params( + val fileToUnsetAsAvailableOffline: OCFile + ) +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt index aa204961e16..9d833784164 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt @@ -21,6 +21,7 @@ package com.owncloud.android.domain.files import androidx.lifecycle.LiveData +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus import com.owncloud.android.domain.files.model.FileListOption import com.owncloud.android.domain.files.model.OCFile @@ -41,4 +42,6 @@ interface FileRepository { fun removeFile(listOfFilesToRemove: List, removeOnlyLocalCopy: Boolean) fun renameFile(ocFile: OCFile, newName: String) fun saveFile(file: OCFile) + + fun updateFileWithNewAvailableOfflineStatus(ocFile: OCFile, newAvailableOfflineStatus: AvailableOfflineStatus) } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt index 05720e91f5c..8111f713ed7 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/model/OCFile.kt @@ -22,9 +22,10 @@ package com.owncloud.android.domain.files.model import android.os.Parcelable import android.webkit.MimeTypeMap +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus import com.owncloud.android.domain.ext.isOneOf -import com.owncloud.android.domain.files.model.AvailableOfflineStatus.AVAILABLE_OFFLINE -import com.owncloud.android.domain.files.model.AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus.AVAILABLE_OFFLINE +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT import kotlinx.parcelize.Parcelize import java.io.File import java.util.Locale diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt index 5eb979143ab..aa3b7fcdc02 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt @@ -18,7 +18,7 @@ */ package com.owncloud.android.testutil -import com.owncloud.android.domain.files.model.AvailableOfflineStatus +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus import com.owncloud.android.domain.files.model.OCFile val OC_FOLDER = OCFile( From 9517e489efcf4fe2eeddda610922cb9fb305de47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Sat, 16 Jul 2022 09:03:19 +0200 Subject: [PATCH 04/15] Allow to set or unset several files as available offline --- .../dependecyinjection/UseCaseModule.kt | 8 ++++---- .../ui/files/filelist/MainFileListFragment.kt | 16 ++++----------- .../ui/files/operations/FileOperation.kt | 4 ++-- .../operations/FileOperationsViewModel.kt | 20 +++++++++---------- ...t => SetFilesAsAvailableOfflineUseCase.kt} | 16 ++++++++++----- ...=> UnsetFilesAsAvailableOfflineUseCase.kt} | 16 ++++++++++----- 6 files changed, 42 insertions(+), 38 deletions(-) rename owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/{SetFileAsAvailableOfflineUseCase.kt => SetFilesAsAvailableOfflineUseCase.kt} (64%) rename owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/{UnsetFileAsAvailableOfflineUseCase.kt => UnsetFilesAsAvailableOfflineUseCase.kt} (63%) 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 9af8dbada03..de520c94324 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -29,8 +29,8 @@ import com.owncloud.android.domain.authentication.usecases.GetBaseUrlUseCase import com.owncloud.android.domain.authentication.usecases.LoginBasicAsyncUseCase import com.owncloud.android.domain.authentication.usecases.LoginOAuthAsyncUseCase import com.owncloud.android.domain.authentication.usecases.SupportsOAuth2UseCase -import com.owncloud.android.domain.availableoffline.usecases.SetFileAsAvailableOfflineUseCase -import com.owncloud.android.domain.availableoffline.usecases.UnsetFileAsAvailableOfflineUseCase +import com.owncloud.android.domain.availableoffline.usecases.SetFilesAsAvailableOfflineUseCase +import com.owncloud.android.domain.availableoffline.usecases.UnsetFilesAsAvailableOfflineUseCase import com.owncloud.android.domain.camerauploads.usecases.GetCameraUploadsConfigurationUseCase import com.owncloud.android.domain.camerauploads.usecases.GetPictureUploadsConfigurationStreamUseCase import com.owncloud.android.domain.camerauploads.usecases.GetVideoUploadsConfigurationStreamUseCase @@ -121,8 +121,8 @@ val useCaseModule = module { factory { SortFilesUseCase() } // Av Offline - factory { SetFileAsAvailableOfflineUseCase(get()) } - factory { UnsetFileAsAvailableOfflineUseCase(get()) } + factory { SetFilesAsAvailableOfflineUseCase(get()) } + factory { UnsetFilesAsAvailableOfflineUseCase(get()) } // Sharing factory { CreatePrivateShareAsyncUseCase(get()) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt index c983f8914f8..0a9abd06cc3 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/filelist/MainFileListFragment.kt @@ -513,10 +513,10 @@ class MainFileListFragment : Fragment(), return true } R.id.action_set_available_offline -> { - fileOperationsViewModel.performOperation(FileOperation.SetFileAsAvailableOffline(singleFile)) + fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(listOf(singleFile))) } R.id.action_unset_available_offline -> { - fileOperationsViewModel.performOperation(FileOperation.UnsetFileAsAvailableOffline(singleFile)) + fileOperationsViewModel.performOperation(FileOperation.UnsetFilesAsAvailableOffline(listOf(singleFile))) } } } @@ -550,19 +550,11 @@ class MainFileListFragment : Fragment(), return true } R.id.action_set_available_offline -> { - // TODO Waiting to be implemented - //containerActivity?.fileOperationsHelper?.toggleAvailableOffline(checkedFiles, true) - //getListView().invalidateViews() + fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(checkedFiles)) return true } R.id.action_unset_available_offline -> { - // TODO Waiting to be implemented - //containerActivity?.fileOperationsHelper?.toggleAvailableOffline(checkedFiles, false) - //getListView().invalidateViews() - //invalidateActionMode() - /*if (fileListOption?.isAvailableOffline() == true) { - onRefresh() - }*/ + fileOperationsViewModel.performOperation(FileOperation.UnsetFilesAsAvailableOffline(checkedFiles)) return true } R.id.action_move -> { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperation.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperation.kt index 27e32d1d2d0..b1e157a571b 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperation.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperation.kt @@ -28,6 +28,6 @@ sealed class FileOperation { data class RemoveOperation(val listOfFilesToRemove: List, val removeOnlyLocalCopy: Boolean) : FileOperation() data class RenameOperation(val ocFileToRename: OCFile, val newName: String) : FileOperation() data class SynchronizeFileOperation(val fileToSync: OCFile, val accountName: String) : FileOperation() - data class SetFileAsAvailableOffline(val fileToUpdate: OCFile) : FileOperation() - data class UnsetFileAsAvailableOffline(val fileToUpdate: OCFile) : FileOperation() + data class SetFilesAsAvailableOffline(val filesToUpdate: List) : FileOperation() + data class UnsetFilesAsAvailableOffline(val filesToUpdate: List) : FileOperation() } diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperationsViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperationsViewModel.kt index 9f43f957014..c9efcb62ac3 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperationsViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/operations/FileOperationsViewModel.kt @@ -25,8 +25,8 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.owncloud.android.domain.BaseUseCaseWithResult import com.owncloud.android.domain.UseCaseResult -import com.owncloud.android.domain.availableoffline.usecases.SetFileAsAvailableOfflineUseCase -import com.owncloud.android.domain.availableoffline.usecases.UnsetFileAsAvailableOfflineUseCase +import com.owncloud.android.domain.availableoffline.usecases.SetFilesAsAvailableOfflineUseCase +import com.owncloud.android.domain.availableoffline.usecases.UnsetFilesAsAvailableOfflineUseCase import com.owncloud.android.domain.exceptions.NoNetworkConnectionException import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.domain.files.usecases.CopyFileUseCase @@ -50,8 +50,8 @@ class FileOperationsViewModel( private val removeFileUseCase: RemoveFileUseCase, private val renameFileUseCase: RenameFileUseCase, private val synchronizeFileUseCase: SynchronizeFileUseCase, - private val setFileAsAvailableOfflineUseCase: SetFileAsAvailableOfflineUseCase, - private val unsetFileAsAvailableOfflineUseCase: UnsetFileAsAvailableOfflineUseCase, + private val setFilesAsAvailableOfflineUseCase: SetFilesAsAvailableOfflineUseCase, + private val unsetFilesAsAvailableOfflineUseCase: UnsetFilesAsAvailableOfflineUseCase, private val contextProvider: ContextProvider, private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider ) : ViewModel() { @@ -82,8 +82,8 @@ class FileOperationsViewModel( is FileOperation.CopyOperation -> copyOperation(fileOperation) is FileOperation.SynchronizeFileOperation -> syncFileOperation(fileOperation) is FileOperation.CreateFolder -> createFolderOperation(fileOperation) - is FileOperation.SetFileAsAvailableOffline -> setFileAsAvailableOffline(fileOperation) - is FileOperation.UnsetFileAsAvailableOffline -> unsetFileAsAvailableOffline(fileOperation) + is FileOperation.SetFilesAsAvailableOffline -> setFileAsAvailableOffline(fileOperation) + is FileOperation.UnsetFilesAsAvailableOffline -> unsetFileAsAvailableOffline(fileOperation) } } @@ -142,15 +142,15 @@ class FileOperationsViewModel( ) } - private fun setFileAsAvailableOffline(fileOperation: FileOperation.SetFileAsAvailableOffline) { + private fun setFileAsAvailableOffline(fileOperation: FileOperation.SetFilesAsAvailableOffline) { viewModelScope.launch(coroutinesDispatcherProvider.io) { - setFileAsAvailableOfflineUseCase.execute(SetFileAsAvailableOfflineUseCase.Params(fileOperation.fileToUpdate)) + setFilesAsAvailableOfflineUseCase.execute(SetFilesAsAvailableOfflineUseCase.Params(fileOperation.filesToUpdate)) } } - private fun unsetFileAsAvailableOffline(fileOperation: FileOperation.UnsetFileAsAvailableOffline) { + private fun unsetFileAsAvailableOffline(fileOperation: FileOperation.UnsetFilesAsAvailableOffline) { viewModelScope.launch(coroutinesDispatcherProvider.io) { - unsetFileAsAvailableOfflineUseCase.execute(UnsetFileAsAvailableOfflineUseCase.Params(fileOperation.fileToUpdate)) + unsetFilesAsAvailableOfflineUseCase.execute(UnsetFilesAsAvailableOfflineUseCase.Params(fileOperation.filesToUpdate)) } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFileAsAvailableOfflineUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt similarity index 64% rename from owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFileAsAvailableOfflineUseCase.kt rename to owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt index 0a2b085c70d..c4cc2dfd08e 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFileAsAvailableOfflineUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt @@ -22,14 +22,20 @@ import com.owncloud.android.domain.BaseUseCaseWithResult import com.owncloud.android.domain.availableoffline.AvailableOfflineRepository import com.owncloud.android.domain.files.model.OCFile -class SetFileAsAvailableOfflineUseCase( +class SetFilesAsAvailableOfflineUseCase( private val availableOfflineRepository: AvailableOfflineRepository, -) : BaseUseCaseWithResult() { +) : BaseUseCaseWithResult() { - override fun run(params: Params) = - availableOfflineRepository.setFileAsAvailableOffline(params.fileToSetAsAvailableOffline) + override fun run(params: Params) { + params.filesToSetAsAvailableOffline.forEach { fileToSetAsAvailableOffline -> + // Its possible to multiselect several files including already available offline files. + if (!fileToSetAsAvailableOffline.isAvailableOffline) { + availableOfflineRepository.setFileAsAvailableOffline(fileToSetAsAvailableOffline) + } + } + } data class Params( - val fileToSetAsAvailableOffline: OCFile + val filesToSetAsAvailableOffline: List ) } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFileAsAvailableOfflineUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt similarity index 63% rename from owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFileAsAvailableOfflineUseCase.kt rename to owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt index d3b5af8921a..91cb0774dfc 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFileAsAvailableOfflineUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt @@ -22,14 +22,20 @@ import com.owncloud.android.domain.BaseUseCaseWithResult import com.owncloud.android.domain.availableoffline.AvailableOfflineRepository import com.owncloud.android.domain.files.model.OCFile -class UnsetFileAsAvailableOfflineUseCase( +class UnsetFilesAsAvailableOfflineUseCase( private val availableOfflineRepository: AvailableOfflineRepository, -) : BaseUseCaseWithResult() { +) : BaseUseCaseWithResult() { - override fun run(params: Params) = - availableOfflineRepository.unsetFileAsAvailableOffline(params.fileToUnsetAsAvailableOffline) + override fun run(params: Params) { + params.filesToUnsetAsAvailableOffline.forEach { fileToUnsetAsAvailableOffline -> + // Its possible to multiselect several files including not available offline files. + if (fileToUnsetAsAvailableOffline.isAvailableOffline) { + availableOfflineRepository.unsetFileAsAvailableOffline(fileToUnsetAsAvailableOffline) + } + } + } data class Params( - val fileToUnsetAsAvailableOffline: OCFile + val filesToUnsetAsAvailableOffline: List ) } From 07b7b474fef2f4fcf3d596589a18cb91a809a6b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Sat, 16 Jul 2022 11:16:33 +0200 Subject: [PATCH 05/15] Toggle available offline done with new usecases --- .../android/ui/fragment/FileDetailFragment.kt | 7 ++- .../ui/helpers/FileOperationsHelper.java | 43 ------------------- .../ui/preview/PreviewAudioFragment.kt | 11 +++-- .../ui/preview/PreviewImageFragment.kt | 10 ++++- .../ui/preview/PreviewTextFragment.java | 19 ++++++-- .../ui/preview/PreviewVideoFragment.java | 19 ++++++-- 6 files changed, 52 insertions(+), 57 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt index eab67416188..bec38d409af 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.kt @@ -46,6 +46,8 @@ import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.extensions.observeWorkerTillItFinishes import com.owncloud.android.extensions.showMessageInSnackbar import com.owncloud.android.files.FileMenuFilter +import com.owncloud.android.presentation.ui.files.operations.FileOperation +import com.owncloud.android.presentation.ui.files.operations.FileOperationsViewModel import com.owncloud.android.presentation.ui.files.removefile.RemoveFilesDialogFragment import com.owncloud.android.presentation.viewmodels.files.FileDetailsViewModel import com.owncloud.android.ui.activity.ComponentsGetter @@ -71,6 +73,7 @@ class FileDetailFragment : FileFragment(), View.OnClickListener { private var progressController: TransferProgressController? = null private val fileDetailsViewModel: FileDetailsViewModel by inject() + private val fileOperationsViewModel: FileOperationsViewModel by inject() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { setHasOptionsMenu(true) @@ -270,11 +273,11 @@ class FileDetailFragment : FileFragment(), View.OnClickListener { true } R.id.action_set_available_offline -> { - mContainerActivity.fileOperationsHelper.toggleAvailableOffline(file, true) + fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(listOf(file))) true } R.id.action_unset_available_offline -> { - mContainerActivity.fileOperationsHelper.toggleAvailableOffline(file, false) + fileOperationsViewModel.performOperation(FileOperation.UnsetFilesAsAvailableOffline(listOf(file))) true } else -> super.onOptionsItemSelected(item) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/owncloudApp/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index fbc5c577363..be033da2d35 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -284,49 +284,6 @@ public void syncFile(OCFile file) { } } - public void toggleAvailableOffline(Collection files, boolean isAvailableOffline) { - for (OCFile file : files) { - toggleAvailableOffline(file, isAvailableOffline); - } - } - - public void toggleAvailableOffline(OCFile file, boolean isAvailableOffline) { - // FIXME: 13/10/2020 : New_arch: Av.Offline - - // if (OCFile.AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT == file.getAvailableOfflineStatus()) { - // /// files descending of an av-offline folder can't be toggled - // mFileActivity.showSnackMessage( - // mFileActivity.getString(R.string.available_offline_inherited_msg) - // ); - // - // } else { - // /// update local property, for file and all its descendents (if folder) - // OCFile.AvailableOfflineStatus targetAvailableOfflineStatus = isAvailableOffline ? - // OCFile.AvailableOfflineStatus.AVAILABLE_OFFLINE : - // OCFile.AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE; - // file.setAvailableOfflineStatus(targetAvailableOfflineStatus); - // boolean success = mFileActivity.getStorageManager().saveLocalAvailableOfflineStatus(file); - // - // if (success) { - // // Schedule job to check to watch for local changes in available offline files and sync them - // AvailableOfflineHandler availableOfflineHandler = new AvailableOfflineHandler(mFileActivity); - // availableOfflineHandler.scheduleAvailableOfflineJob(mFileActivity); - // - // /// immediate content synchronization - // if (OCFile.AvailableOfflineStatus.AVAILABLE_OFFLINE == file.getAvailableOfflineStatus()) { - // syncFile(file); - // } else { - // cancelTransference(file); - // } - // } else { - // /// unexpected error - // mFileActivity.showSnackMessage( - // mFileActivity.getString(R.string.common_error_unknown) - // ); - // } - // } - } - /** * Cancel the transference in downloads (files/folders) and file uploads * diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt index 8cba9e36a97..3c0f478d514 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewAudioFragment.kt @@ -48,11 +48,14 @@ import com.owncloud.android.files.FileMenuFilter import com.owncloud.android.media.MediaControlView import com.owncloud.android.media.MediaService import com.owncloud.android.media.MediaServiceBinder +import com.owncloud.android.presentation.ui.files.operations.FileOperation +import com.owncloud.android.presentation.ui.files.operations.FileOperationsViewModel +import com.owncloud.android.presentation.ui.files.removefile.RemoveFilesDialogFragment import com.owncloud.android.ui.controller.TransferProgressController import com.owncloud.android.ui.dialog.ConfirmationDialogFragment -import com.owncloud.android.presentation.ui.files.removefile.RemoveFilesDialogFragment import com.owncloud.android.ui.fragment.FileFragment import com.owncloud.android.utils.PreferenceUtils +import org.koin.android.ext.android.inject import timber.log.Timber /** @@ -83,6 +86,8 @@ class PreviewAudioFragment : FileFragment() { private var progressBar: ProgressBar? = null var progressController: TransferProgressController? = null + private val fileOperationsViewModel: FileOperationsViewModel by inject() + /** * {@inheritDoc} */ @@ -302,11 +307,11 @@ class PreviewAudioFragment : FileFragment() { true } R.id.action_set_available_offline -> { - mContainerActivity.fileOperationsHelper.toggleAvailableOffline(file, true) + fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(listOf(file))) true } R.id.action_unset_available_offline -> { - mContainerActivity.fileOperationsHelper.toggleAvailableOffline(file, false) + fileOperationsViewModel.performOperation(FileOperation.UnsetFilesAsAvailableOffline(listOf(file))) true } else -> super.onOptionsItemSelected(item) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt index ae8a17b5c7b..96604d0ba36 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt @@ -51,11 +51,15 @@ import com.owncloud.android.databinding.TopProgressBarBinding import com.owncloud.android.domain.files.model.MIME_SVG import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.files.FileMenuFilter +import com.owncloud.android.presentation.ui.files.operations.FileOperation +import com.owncloud.android.presentation.ui.files.operations.FileOperationsViewModel import com.owncloud.android.ui.controller.TransferProgressController import com.owncloud.android.ui.dialog.ConfirmationDialogFragment import com.owncloud.android.presentation.ui.files.removefile.RemoveFilesDialogFragment import com.owncloud.android.ui.fragment.FileFragment import com.owncloud.android.utils.PreferenceUtils +import org.koin.android.ext.android.inject +import org.koin.java.KoinJavaComponent.inject import timber.log.Timber import java.io.File @@ -86,6 +90,8 @@ class PreviewImageFragment : FileFragment() { private var _bindingTopProgress: TopProgressBarBinding? = null private val bindingTopProgress get() = _bindingTopProgress!! + private val fileOperationsViewModel: FileOperationsViewModel by inject() + /** * {@inheritDoc} */ @@ -254,11 +260,11 @@ class PreviewImageFragment : FileFragment() { true } R.id.action_set_available_offline -> { - mContainerActivity.fileOperationsHelper.toggleAvailableOffline(file, true) + fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(listOf(file))) true } R.id.action_unset_available_offline -> { - mContainerActivity.fileOperationsHelper.toggleAvailableOffline(file, false) + fileOperationsViewModel.performOperation(FileOperation.UnsetFilesAsAvailableOffline(listOf(file))) true } else -> super.onOptionsItemSelected(item) diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java index fa78e85bf31..3c60f89a556 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java @@ -39,10 +39,12 @@ import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.domain.files.model.OCFile; import com.owncloud.android.files.FileMenuFilter; +import com.owncloud.android.presentation.ui.files.operations.FileOperation; +import com.owncloud.android.presentation.ui.files.operations.FileOperationsViewModel; +import com.owncloud.android.presentation.ui.files.removefile.RemoveFilesDialogFragment; import com.owncloud.android.ui.controller.TransferProgressController; import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; import com.owncloud.android.ui.dialog.LoadingDialog; -import com.owncloud.android.presentation.ui.files.removefile.RemoveFilesDialogFragment; import com.owncloud.android.ui.fragment.FileFragment; import com.owncloud.android.utils.PreferenceUtils; import timber.log.Timber; @@ -52,10 +54,13 @@ import java.io.IOException; import java.io.StringWriter; import java.lang.ref.WeakReference; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Scanner; +import static org.koin.java.KoinJavaComponent.get; + public class PreviewTextFragment extends FileFragment { private static final String EXTRA_FILE = "FILE"; private static final String EXTRA_ACCOUNT = "ACCOUNT"; @@ -66,6 +71,8 @@ public class PreviewTextFragment extends FileFragment { private TextView mTextPreview; private TextLoadAsyncTask mTextLoadTask; + FileOperationsViewModel fileOperationsViewModel = get(FileOperationsViewModel.class); + /** * Public factory method to create new PreviewTextFragment instances. * @@ -371,15 +378,19 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; } case R.id.action_sync_file: { - mContainerActivity.getFileOperationsHelper().syncFile(getFile()); + fileOperationsViewModel.performOperation(new FileOperation.SynchronizeFileOperation(getFile(), mAccount.name)); return true; } case R.id.action_set_available_offline: { - mContainerActivity.getFileOperationsHelper().toggleAvailableOffline(getFile(), true); + ArrayList fileToSetAsAvailableOffline = new ArrayList<>(); + fileToSetAsAvailableOffline.add(getFile()); + fileOperationsViewModel.performOperation(new FileOperation.SetFilesAsAvailableOffline(fileToSetAsAvailableOffline)); return true; } case R.id.action_unset_available_offline: { - mContainerActivity.getFileOperationsHelper().toggleAvailableOffline(getFile(), false); + ArrayList fileToUnsetAsAvailableOffline = new ArrayList<>(); + fileToUnsetAsAvailableOffline.add(getFile()); + fileOperationsViewModel.performOperation(new FileOperation.UnsetFilesAsAvailableOffline(fileToUnsetAsAvailableOffline)); return true; } default: diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoFragment.java b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoFragment.java index def707352ad..46ae6943a33 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoFragment.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/preview/PreviewVideoFragment.java @@ -51,14 +51,20 @@ import com.owncloud.android.domain.files.model.MimeTypeConstantsKt; import com.owncloud.android.domain.files.model.OCFile; import com.owncloud.android.files.FileMenuFilter; +import com.owncloud.android.presentation.ui.files.operations.FileOperation; +import com.owncloud.android.presentation.ui.files.operations.FileOperationsViewModel; +import com.owncloud.android.presentation.ui.files.removefile.RemoveFilesDialogFragment; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.controller.TransferProgressController; import com.owncloud.android.ui.dialog.ConfirmationDialogFragment; -import com.owncloud.android.presentation.ui.files.removefile.RemoveFilesDialogFragment; import com.owncloud.android.ui.fragment.FileFragment; import timber.log.Timber; +import java.util.ArrayList; + +import static org.koin.java.KoinJavaComponent.get; + /** * This fragment shows a preview of a downloaded video file, or starts streaming if file is not * downloaded yet. @@ -97,6 +103,8 @@ public class PreviewVideoFragment extends FileFragment implements View.OnClickLi private boolean mAutoplay; private long mPlaybackPosition; + FileOperationsViewModel fileOperationsViewModel = get(FileOperationsViewModel.class); + /** * Public factory method to create new PreviewVideoFragment instances. * @@ -347,11 +355,16 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; } case R.id.action_set_available_offline: { - mContainerActivity.getFileOperationsHelper().toggleAvailableOffline(getFile(), true); + ArrayList fileToSetAsAvailableOffline = new ArrayList<>(); + fileToSetAsAvailableOffline.add(getFile()); + fileOperationsViewModel.performOperation(new FileOperation.SetFilesAsAvailableOffline(fileToSetAsAvailableOffline)); return true; } case R.id.action_unset_available_offline: { - mContainerActivity.getFileOperationsHelper().toggleAvailableOffline(getFile(), false); + + ArrayList fileToUnsetAsAvailableOffline = new ArrayList<>(); + fileToUnsetAsAvailableOffline.add(getFile()); + fileOperationsViewModel.performOperation(new FileOperation.UnsetFilesAsAvailableOffline(fileToUnsetAsAvailableOffline)); return true; } case R.id.action_download_file: { From aebad0794f5b0716366425f8df7a4fe209adf5d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Sat, 16 Jul 2022 12:06:17 +0200 Subject: [PATCH 06/15] New files from server will get the parent av offline status and update status in dao recursively when setting a file as av offline --- .../datamodel/FileDataStorageManager.kt | 64 ------------------- .../removefile/RemoveFilesDialogFragment.kt | 10 ++- .../android/providers/FileContentProvider.kt | 4 +- .../ui/activity/FileDisplayActivity.kt | 6 +- .../owncloud/android/data/files/db/FileDao.kt | 17 +++-- .../data/files/repository/OCFileRepository.kt | 9 +++ 6 files changed, 27 insertions(+), 83 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt b/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt index c9e6704de7a..1ae9758ef95 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt @@ -158,53 +158,6 @@ class FileDataStorageManager : KoinComponent { // return result } - /** - * Get a collection with all the files set by the user as available offline, from current account - * putting away files whose parent is also available offline - * - * @return List with all the files set by current user as available offline. - */ - val availableOfflineFilesFromCurrentAccount: Vector - get() { - return Vector() - // FIXME: 13/10/2020 : New_arch: Av.Offline -// val result = Vector() -// -// var cursorOnKeptInSync: Cursor? = null -// try { -// cursorOnKeptInSync = performQuery( -// uri = CONTENT_URI, -// projection = null, -// selection = "($FILE_KEEP_IN_SYNC = ? AND NOT $FILE_KEEP_IN_SYNC = ? ) AND $FILE_ACCOUNT_OWNER = ? ", -// selectionArgs = arrayOf( -// AVAILABLE_OFFLINE.value.toString(), -// AVAILABLE_OFFLINE_PARENT.value.toString(), -// account.name -// ), -// sortOrder = null, -// performWithContentProviderClient = false -// ) -// -// if (cursorOnKeptInSync != null && cursorOnKeptInSync.moveToFirst()) { -// var file: OCFile? -// do { -// file = createFileInstance(cursorOnKeptInSync) -// result.add(file) -// } while (cursorOnKeptInSync.moveToNext()) -// } else { -// Timber.d("No available offline files found") -// } -// -// } catch (e: Exception) { -// Timber.e(e, "Exception retrieving all the available offline files") -// -// } finally { -// cursorOnKeptInSync?.close() -// } -// -// return result.apply { sort() } - } - fun sharedByLinkFilesFromCurrentAccount(): List? = runBlocking(CoroutinesDispatcherProvider().io) { val getFilesSharedByLinkUseCase: GetFilesSharedByLinkUseCase by inject() @@ -498,23 +451,6 @@ class FileDataStorageManager : KoinComponent { // } } - /** - * Adds the appropriate initial value for FILE_KEEP_IN_SYNC to - * passed [ContentValues] instance. - * - * @param file [OCFile] which av-offline property will be set. - * @param cv [ContentValues] instance where the property is added. - */ - private fun setInitialAvailableOfflineStatus(file: OCFile, cv: ContentValues) { - // set appropriate av-off folder depending on ancestor - val inFolderAvailableOffline = isAnyAncestorAvailableOfflineFolder(file) - if (inFolderAvailableOffline) { - cv.put(FILE_KEEP_IN_SYNC, AVAILABLE_OFFLINE_PARENT.value) - } else { - cv.put(FILE_KEEP_IN_SYNC, NOT_AVAILABLE_OFFLINE.value) - } - } - fun migrateLegacyToScopedPath( legacyStorageDirectoryPath: String, rootStorageDirectoryPath: String, diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/removefile/RemoveFilesDialogFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/removefile/RemoveFilesDialogFragment.kt index f67943563e3..cd6ab2225b0 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/removefile/RemoveFilesDialogFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/removefile/RemoveFilesDialogFragment.kt @@ -29,7 +29,6 @@ import com.owncloud.android.presentation.ui.files.operations.FileOperationsViewM import com.owncloud.android.ui.dialog.ConfirmationDialogFragment import com.owncloud.android.ui.dialog.ConfirmationDialogFragment.ConfirmationDialogFragmentListener import org.koin.androidx.viewmodel.ext.android.sharedViewModel -import java.util.ArrayList /** * Dialog requiring confirmation before removing a collection of given OCFiles. @@ -82,7 +81,7 @@ class RemoveFilesDialogFragment : ConfirmationDialogFragment(), ConfirmationDial val messageStringId: Int var containsFolder = false var containsDown = false - val containsAvailableOffline = false + var containsAvailableOffline = false for (file in files) { if (file.isFolder) { containsFolder = true @@ -90,10 +89,9 @@ class RemoveFilesDialogFragment : ConfirmationDialogFragment(), ConfirmationDial if (file.isAvailableLocally) { containsDown = true } - // FIXME: 13/10/2020 : New_arch: Av.Offline - // if (file.getAvailableOfflineStatus() != OCFile.AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE) { - // containsAvailableOffline = true; - // } + if (file.isAvailableOffline) { + containsAvailableOffline = true; + } } messageStringId = if (files.size == 1) { // choose message for a single file diff --git a/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt index 0c85feeefa4..9ab33857826 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/providers/FileContentProvider.kt @@ -1064,10 +1064,8 @@ class FileContentProvider(val executors: Executors = Executors()) : ContentProvi } } - // TODO: Remove old database once it is not needed anymore. - // FIXME: 29/10/2020 : New_arch: Av.Offline // Drop old files table from old database - //db.execSQL("DROP TABLE IF EXISTS " + ProviderTableMeta.FILE_TABLE_NAME + ";") + db.execSQL("DROP TABLE IF EXISTS " + ProviderTableMeta.FILE_TABLE_NAME + ";") } } if (!upgraded) { diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 510362e20a9..0a87c5802ac 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -1570,10 +1570,8 @@ class FileDisplayActivity : FileActivity(), fileOperationsViewModel.performOperation(FileOperation.SynchronizeFileOperation(file, account.name)) } PreviewVideoFragment.canBePreviewed(file) && !WorkManager.getInstance(this).isDownloadPending(account, file) -> { - // FIXME: 13/10/2020 : New_arch: Av.Offline - // Available offline exception, don't initialize streaming - // if (!file.isAvailableLocally() && file.isAvailableOffline()) { - if (file.isAvailableLocally) { + // Available offline but not downloaded yet, don't initialize streaming + if (!file.isAvailableLocally && file.isAvailableOffline) { // sync file content, then open with external apps startSyncThenOpen(file) } else { diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt index c5ec6cf2ac6..92b19ab56b6 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt @@ -208,21 +208,26 @@ abstract class FileDao { @Transaction open fun updateAvailableOfflineStatusForFile(ocFile: OCFile, newAvailableOfflineStatus: Int) { if (ocFile.isFolder) { - updateFolderAsAvailableOffline(ocFile.id!!, newAvailableOfflineStatus) + updateFolderWithNewAvailableOfflineStatus(ocFile.id!!, newAvailableOfflineStatus) } else { updateFileWithAvailableOfflineStatus(ocFile.id!!, newAvailableOfflineStatus) } } - private fun updateFolderAsAvailableOffline(ocFolderId: Long, newAvailableOfflineStatus: Int) { + private fun updateFolderWithNewAvailableOfflineStatus(ocFolderId: Long, newAvailableOfflineStatus: Int) { updateFileWithAvailableOfflineStatus(ocFolderId, newAvailableOfflineStatus) + val newStatusForChildren = if (newAvailableOfflineStatus == AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE.ordinal) { + AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE.ordinal + } else { + AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT.ordinal + } val folderContent = getFolderContent(ocFolderId) - folderContent.forEach { - if (it.isFolder) { - updateFolderAsAvailableOffline(it.id, AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT.ordinal) + folderContent.forEach { folderChild -> + if (folderChild.isFolder) { + updateFolderWithNewAvailableOfflineStatus(folderChild.id, newStatusForChildren) } else { - updateFileWithAvailableOfflineStatus(it.id, AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT.ordinal) + updateFileWithAvailableOfflineStatus(folderChild.id, newStatusForChildren) } } } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index ce38b0addaf..92a27f580df 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -25,6 +25,8 @@ import com.owncloud.android.data.files.datasources.LocalFileDataSource import com.owncloud.android.data.files.datasources.RemoteFileDataSource import com.owncloud.android.data.storage.LocalStorageProvider import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE import com.owncloud.android.domain.exceptions.ConflictException import com.owncloud.android.domain.exceptions.FileAlreadyExistsException import com.owncloud.android.domain.exceptions.FileNotFoundException @@ -227,6 +229,9 @@ class OCFileRepository( needsToUpdateThumbnail = !remoteChild.isFolder // remote eTag will not be set unless file CONTENTS are synchronized etag = "" + availableOfflineStatus = + if (remoteFolder.isAvailableOffline) AVAILABLE_OFFLINE_PARENT else NOT_AVAILABLE_OFFLINE + }) } else { // File exists in the database, we need to check several stuff. @@ -237,6 +242,10 @@ class OCFileRepository( etag = localChildToSync.etag needsToUpdateThumbnail = !remoteChild.isFolder && remoteChild.modificationTimestamp != localChildToSync.modificationTimestamp + // Probably not needed, if the child was already in the database, the av offline status should be also there + if (remoteFolder.isAvailableOffline) { + availableOfflineStatus = AVAILABLE_OFFLINE_PARENT + } // FIXME: What about renames? Need to fix storage path }) } From e01eac8ebf62bdd1ac750801807962a2cd55b28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Sat, 16 Jul 2022 12:13:30 +0200 Subject: [PATCH 07/15] Keep polishing the code. Already done in new architecture --- .../datamodel/FileDataStorageManager.kt | 63 ------------------- .../SynchronizeFolderOperation.java | 15 +++-- 2 files changed, 7 insertions(+), 71 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt b/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt index 1ae9758ef95..ef89b8c212c 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt @@ -496,69 +496,6 @@ class FileDataStorageManager : KoinComponent { Timber.d("Updated path for ${filesWithPathUpdated.size} downloaded files") } - /** - * Updates available-offline status of OCFile received as a parameter, with its current value. - * - * - * Saves the new value property for the given file in persistent storage. - * - * - * If the file is a folder, updates the value of all its known descendants accordingly. - * - * @param file File which available-offline status will be updated. - * @return 'true' if value was updated, 'false' otherwise. - */ - fun saveLocalAvailableOfflineStatus(file: OCFile): Boolean { - return false - // FIXME: 13/10/2020 : New_arch: Av.Offline -// if (!fileExists(file.fileId)) { -// return false -// } -// -// val newStatus = file.availableOfflineStatus -// require(AVAILABLE_OFFLINE_PARENT != newStatus) { -// "Forbidden value, AVAILABLE_OFFLINE_PARENT is calculated, cannot be set" -// } -// -// val cv = ContentValues() -// cv.put(FILE_KEEP_IN_SYNC, file.availableOfflineStatus.value) -// -// var updatedCount: Int -// try { -// updatedCount = performUpdate( -// uri = CONTENT_URI, -// contentValues = cv, -// where = "$_ID=?", -// selectionArgs = arrayOf(file.fileId.toString()) -// ) -// -// // Update descendants -// if (file.isFolder && updatedCount > 0) { -// val descendantsCv = ContentValues() -// if (newStatus == AVAILABLE_OFFLINE) { -// // all descendant files MUST be av-off due to inheritance, not due to previous value -// descendantsCv.put(FILE_KEEP_IN_SYNC, AVAILABLE_OFFLINE_PARENT.value) -// } else { -// // all descendant files MUST be not-available offline -// descendantsCv.put(FILE_KEEP_IN_SYNC, NOT_AVAILABLE_OFFLINE.value) -// } -// val selectDescendants = selectionForAllDescendantsOf(file) -// updatedCount += performUpdate( -// uri = CONTENT_URI, -// contentValues = descendantsCv, -// where = selectDescendants.first, -// selectionArgs = selectDescendants.second -// ) -// } -// -// } catch (e: RemoteException) { -// Timber.e(e, "Fail updating available offline status") -// return false -// } -// -// return updatedCount > 0 - } - // TODO: New_arch: Remove this and call usecase inside FilesViewModel fun getFolderContent(parentId: Long): List = runBlocking(CoroutinesDispatcherProvider().io) { val getFolderContentUseCase: GetFolderContentUseCase by inject() diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java index fb7d569c078..576b760489e 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -33,6 +33,7 @@ import com.owncloud.android.datamodel.OCUpload; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.domain.UseCaseResult; +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus; import com.owncloud.android.domain.files.model.OCFile; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.OperationCancelledException; @@ -364,12 +365,11 @@ private void mergeRemoteFolder(ArrayList remoteFolderAndFiles) // remote eTag will not be set unless file CONTENTS are synchronized updatedLocalFile.setEtag(""); // new files need to check av-off status of parent folder! - // FIXME: 19/10/2020 : New_arch: Av.Offline - // if (updatedFolder.isAvailableOffline()) { - // updatedLocalFile.setAvailableOfflineStatus( - // OCFile.AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT - // ); - // } + if (updatedFolder.isAvailableOffline()) { + updatedLocalFile.setAvailableOfflineStatus( + AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT + ); + } // new files need to update thumbnails updatedLocalFile.setNeedsToUpdateThumbnail(true); } @@ -417,8 +417,7 @@ private void preparePushOfLocalChanges() { */ private boolean addToSyncContents(OCFile localFile, OCFile remoteFile) { - // FIXME: 13/10/2020 : New_arch: Av.Offline - boolean shouldSyncContents = (mSyncContentOfRegularFiles || localFile.isAvailableLocally()); //|| localFile.isAvailableOffline()); + boolean shouldSyncContents = (mSyncContentOfRegularFiles || localFile.isAvailableLocally() || localFile.isAvailableOffline()); boolean serverUnchanged; if (localFile.isFolder()) { From 56e6b190969956d4aa98d4ce37329ec53533f83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Sat, 16 Jul 2022 13:11:31 +0200 Subject: [PATCH 08/15] When copying or moving a file, it will get the available offline status depending on the parent --- .../files/datasources/LocalFileDataSource.kt | 4 +- .../implementation/OCLocalFileDataSource.kt | 10 +-- .../owncloud/android/data/files/db/FileDao.kt | 77 +++++++++++++------ .../android/data/files/db/OCFileEntity.kt | 2 +- .../data/files/repository/OCFileRepository.kt | 4 +- .../model/AvailableOfflineStatus.kt | 1 + 6 files changed, 63 insertions(+), 35 deletions(-) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt index 0c900833148..ea65fe436e0 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt @@ -25,7 +25,7 @@ import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus import com.owncloud.android.domain.files.model.OCFile interface LocalFileDataSource { - fun copyFile(sourceFile: OCFile, targetFile: OCFile, finalRemotePath: String, remoteId: String) + fun copyFile(sourceFile: OCFile, targetFolder: OCFile, finalRemotePath: String, remoteId: String) fun getFileById(fileId: Long): OCFile? fun getFileByRemotePath(remotePath: String, owner: String): OCFile? fun getFileByRemoteId(remoteId: String): OCFile? @@ -37,7 +37,7 @@ interface LocalFileDataSource { fun getFolderImages(folderId: Long): List fun getFilesSharedByLink(owner: String): List fun getFilesAvailableOffline(owner: String): List - fun moveFile(sourceFile: OCFile, targetFile: OCFile, finalRemotePath: String, finalStoragePath: String) + fun moveFile(sourceFile: OCFile, targetFolder: OCFile, finalRemotePath: String, finalStoragePath: String) fun saveFilesInFolder(listOfFiles: List, folder: OCFile) fun saveFile(file: OCFile) fun removeFile(fileId: Long) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt index 0b553da0d16..964d15daf10 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt @@ -34,10 +34,10 @@ import com.owncloud.android.domain.files.model.OCFile.Companion.ROOT_PATH class OCLocalFileDataSource( private val fileDao: FileDao, ) : LocalFileDataSource { - override fun copyFile(sourceFile: OCFile, targetFile: OCFile, finalRemotePath: String, remoteId: String) { + override fun copyFile(sourceFile: OCFile, targetFolder: OCFile, finalRemotePath: String, remoteId: String) { fileDao.copy( sourceFile = sourceFile.toEntity(), - targetFile = targetFile.toEntity(), + targetFolder = targetFolder.toEntity(), finalRemotePath = finalRemotePath, remoteId = remoteId ) @@ -108,10 +108,10 @@ class OCLocalFileDataSource( it.toModel() } - override fun moveFile(sourceFile: OCFile, targetFile: OCFile, finalRemotePath: String, finalStoragePath: String) = + override fun moveFile(sourceFile: OCFile, targetFolder: OCFile, finalRemotePath: String, finalStoragePath: String) = fileDao.moveFile( sourceFile = sourceFile.toEntity(), - targetFile = targetFile.toEntity(), + targetFolder = targetFolder.toEntity(), finalRemotePath = finalRemotePath, finalStoragePath = sourceFile.storagePath?.let { finalStoragePath } ) @@ -135,7 +135,7 @@ class OCLocalFileDataSource( override fun renameFile(fileToRename: OCFile, finalRemotePath: String, finalStoragePath: String) { fileDao.moveFile( sourceFile = fileToRename.toEntity(), - targetFile = fileDao.getFileById(fileToRename.parentId!!)!!, + targetFolder = fileDao.getFileById(fileToRename.parentId!!)!!, finalRemotePath = finalRemotePath, finalStoragePath = fileToRename.storagePath?.let { finalStoragePath } ) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt index 92b19ab56b6..733b3e38f1e 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt @@ -25,7 +25,10 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.Transaction import com.owncloud.android.data.ProviderMeta -import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus.AVAILABLE_OFFLINE +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE +import com.owncloud.android.domain.ext.isOneOf import com.owncloud.android.domain.files.model.OCFile import java.io.File.separatorChar @@ -106,7 +109,10 @@ abstract class FileDao { val folderId = insert(folder) folderContent.forEach { fileToInsert -> - insert(fileToInsert.apply { parentId = folderId }) + insert(fileToInsert.apply { + parentId = folderId + availableOfflineStatus = getNewAvailableOfflineStatus(folder.availableOfflineStatus, fileToInsert.availableOfflineStatus) + }) } } @@ -128,6 +134,7 @@ abstract class FileDao { storagePath = localFile.storagePath, treeEtag = localFile.treeEtag, etagInConflict = localFile.etagInConflict, + availableOfflineStatus = localFile.availableOfflineStatus, ).apply { id = localFile.id }) @@ -137,22 +144,22 @@ abstract class FileDao { @Transaction open fun copy( sourceFile: OCFileEntity, - targetFile: OCFileEntity, + targetFolder: OCFileEntity, finalRemotePath: String, remoteId: String? ) { // 1. Update target size insert( - targetFile.copy( - length = targetFile.length + sourceFile.length - ).apply { id = targetFile.id } + targetFolder.copy( + length = targetFolder.length + sourceFile.length + ).apply { id = targetFolder.id } ) // 2. Insert a new file with common attributes and retrieved remote id insert( OCFileEntity( - parentId = targetFile.id, - owner = targetFile.owner, + parentId = targetFolder.id, + owner = targetFolder.owner, remotePath = finalRemotePath, remoteId = remoteId, length = sourceFile.length, @@ -163,7 +170,8 @@ abstract class FileDao { etag = "", creationTimestamp = null, permissions = null, - treeEtag = "" + treeEtag = "", + availableOfflineStatus = NOT_AVAILABLE_OFFLINE.ordinal, ) ) } @@ -171,15 +179,15 @@ abstract class FileDao { @Transaction open fun moveFile( sourceFile: OCFileEntity, - targetFile: OCFileEntity, + targetFolder: OCFileEntity, finalRemotePath: String, finalStoragePath: String? ) { // 1. Update target size insert( - targetFile.copy( - length = targetFile.length + sourceFile.length - ).apply { id = targetFile.id } + targetFolder.copy( + length = targetFolder.length + sourceFile.length + ).apply { id = targetFolder.id } ) // 2. Update source @@ -187,7 +195,7 @@ abstract class FileDao { // Update remote path and storage path when moving a folder moveFolder( sourceFolder = sourceFile, - targetFile = targetFile, + targetFolder = targetFolder, targetRemotePath = finalRemotePath, targetStoragePath = finalStoragePath ) @@ -195,7 +203,7 @@ abstract class FileDao { // Update remote path, storage path, parent file when moving a file moveSingleFile( sourceFile = sourceFile, - targetFile = targetFile, + targetFolder = targetFolder, finalRemotePath = finalRemotePath, finalStoragePath = finalStoragePath ) @@ -217,10 +225,10 @@ abstract class FileDao { private fun updateFolderWithNewAvailableOfflineStatus(ocFolderId: Long, newAvailableOfflineStatus: Int) { updateFileWithAvailableOfflineStatus(ocFolderId, newAvailableOfflineStatus) - val newStatusForChildren = if (newAvailableOfflineStatus == AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE.ordinal) { - AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE.ordinal + val newStatusForChildren = if (newAvailableOfflineStatus == NOT_AVAILABLE_OFFLINE.ordinal) { + NOT_AVAILABLE_OFFLINE.ordinal } else { - AvailableOfflineStatus.AVAILABLE_OFFLINE_PARENT.ordinal + AVAILABLE_OFFLINE_PARENT.ordinal } val folderContent = getFolderContent(ocFolderId) folderContent.forEach { folderChild -> @@ -237,22 +245,23 @@ abstract class FileDao { private fun moveSingleFile( sourceFile: OCFileEntity, - targetFile: OCFileEntity, + targetFolder: OCFileEntity, finalRemotePath: String, finalStoragePath: String? ) { insert( sourceFile.copy( - parentId = targetFile.id, + parentId = targetFolder.id, remotePath = finalRemotePath, - storagePath = finalStoragePath + storagePath = finalStoragePath, + availableOfflineStatus = getNewAvailableOfflineStatus(targetFolder.availableOfflineStatus, sourceFile.availableOfflineStatus) ).apply { id = sourceFile.id } ) } private fun moveFolder( sourceFolder: OCFileEntity, - targetFile: OCFileEntity, + targetFolder: OCFileEntity, targetRemotePath: String, targetStoragePath: String? ) { @@ -264,7 +273,7 @@ abstract class FileDao { moveSingleFile( sourceFile = sourceFolder, - targetFile = targetFile, + targetFolder = targetFolder, finalRemotePath = folderRemotePath, finalStoragePath = folderStoragePath ) @@ -279,14 +288,14 @@ abstract class FileDao { if (file.isFolder) { moveFolder( sourceFolder = file, - targetFile = sourceFolder, + targetFolder = sourceFolder, targetRemotePath = remotePathForChild, targetStoragePath = storagePathForChild ) } else { moveSingleFile( sourceFile = file, - targetFile = sourceFolder, + targetFolder = sourceFolder, finalRemotePath = remotePathForChild, finalStoragePath = storagePathForChild ) @@ -294,6 +303,24 @@ abstract class FileDao { } } + /** + * If folder is available offline, the child gets the AVAILABLE_OFFLINE_PARENT status + * If child was available offline because of the previous parent, it won't be av offline anymore + * Otherwise, keep the child available offline status + */ + private fun getNewAvailableOfflineStatus( + parentFolderAvailableOfflineStatus: Int?, + currentFileAvailableOfflineStatus: Int?, + ): Int { + return if ((parentFolderAvailableOfflineStatus != null) && + parentFolderAvailableOfflineStatus.isOneOf(AVAILABLE_OFFLINE.ordinal, AVAILABLE_OFFLINE_PARENT.ordinal) + ) { + AVAILABLE_OFFLINE_PARENT.ordinal + } else if (currentFileAvailableOfflineStatus == AVAILABLE_OFFLINE.ordinal) { + AVAILABLE_OFFLINE.ordinal + } else NOT_AVAILABLE_OFFLINE.ordinal + } + companion object { private const val SELECT_FILE_WITH_ID = "SELECT * " + diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt index 5085773de37..d105804e5d4 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/OCFileEntity.kt @@ -71,7 +71,7 @@ data class OCFileEntity( val treeEtag: String? = null, @ColumnInfo(name = "keepInSync") - val availableOfflineStatus: Int? = null, + var availableOfflineStatus: Int? = null, val lastSyncDateForData: Long? = null, val fileShareViaLink: Int? = null, var lastSyncDateForProperties: Long? = null, diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index 92a27f580df..e699f6be1ad 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -102,7 +102,7 @@ class OCFileRepository( // 3. Update database with latest changes localFileDataSource.copyFile( sourceFile = ocFile, - targetFile = targetFolder, + targetFolder = targetFolder, finalRemotePath = finalRemotePath, remoteId = remoteId ) @@ -173,7 +173,7 @@ class OCFileRepository( // 3. Update database with latest changes localFileDataSource.moveFile( sourceFile = ocFile, - targetFile = targetFile, + targetFolder = targetFile, finalRemotePath = finalRemotePath, finalStoragePath = finalStoragePath ) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt index 7dd8ae7502a..0440a98f0a6 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt @@ -18,6 +18,7 @@ */ package com.owncloud.android.domain.availableoffline.model +import com.owncloud.android.domain.ext.isOneOf enum class AvailableOfflineStatus { /** From 70034cd7477b17b416ff147124439d1a768bc616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Sat, 16 Jul 2022 13:31:45 +0200 Subject: [PATCH 09/15] Add a new usecase to retrieve the available offline from every account. --- .../dependecyinjection/UseCaseModule.kt | 6 ++-- .../files/datasources/LocalFileDataSource.kt | 3 +- .../implementation/OCLocalFileDataSource.kt | 12 ++++++-- .../owncloud/android/data/files/db/FileDao.kt | 14 +++++++-- .../data/files/repository/OCFileRepository.kt | 7 +++-- ...ilesAvailableOfflineFromAccountUseCase.kt} | 8 ++--- ...AvailableOfflineFromEveryAccountUseCase.kt | 30 +++++++++++++++++++ .../android/domain/files/FileRepository.kt | 3 +- .../GetFilesAvailableOfflineUseCaseTest.kt | 13 ++++---- 9 files changed, 74 insertions(+), 22 deletions(-) rename owncloudDomain/src/main/java/com/owncloud/android/domain/{files/usecases/GetFilesAvailableOfflineUseCase.kt => availableoffline/usecases/GetFilesAvailableOfflineFromAccountUseCase.kt} (84%) create mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.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 de520c94324..ecc3c3467a2 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -29,6 +29,8 @@ import com.owncloud.android.domain.authentication.usecases.GetBaseUrlUseCase import com.owncloud.android.domain.authentication.usecases.LoginBasicAsyncUseCase import com.owncloud.android.domain.authentication.usecases.LoginOAuthAsyncUseCase import com.owncloud.android.domain.authentication.usecases.SupportsOAuth2UseCase +import com.owncloud.android.domain.availableoffline.usecases.GetFilesAvailableOfflineFromAccountUseCase +import com.owncloud.android.domain.availableoffline.usecases.GetFilesAvailableOfflineFromEveryAccountUseCase import com.owncloud.android.domain.availableoffline.usecases.SetFilesAsAvailableOfflineUseCase import com.owncloud.android.domain.availableoffline.usecases.UnsetFilesAsAvailableOfflineUseCase import com.owncloud.android.domain.camerauploads.usecases.GetCameraUploadsConfigurationUseCase @@ -45,7 +47,6 @@ import com.owncloud.android.domain.files.usecases.CopyFileUseCase import com.owncloud.android.domain.files.usecases.CreateFolderAsyncUseCase import com.owncloud.android.domain.files.usecases.GetFileByIdUseCase import com.owncloud.android.domain.files.usecases.GetFileByRemotePathUseCase -import com.owncloud.android.domain.files.usecases.GetFilesAvailableOfflineUseCase import com.owncloud.android.domain.files.usecases.GetFilesSharedByLinkUseCase import com.owncloud.android.domain.files.usecases.GetFolderContentAsLiveDataUseCase import com.owncloud.android.domain.files.usecases.GetFolderContentUseCase @@ -114,7 +115,8 @@ val useCaseModule = module { factory { RenameFileUseCase(get()) } factory { SaveFileOrFolderUseCase(get()) } factory { GetFilesSharedByLinkUseCase(get()) } - factory { GetFilesAvailableOfflineUseCase(get()) } + factory { GetFilesAvailableOfflineFromAccountUseCase(get()) } + factory { GetFilesAvailableOfflineFromEveryAccountUseCase(get()) } factory { GetSearchFolderContentUseCase(get()) } factory { SynchronizeFileUseCase(get(), get(), get(), get()) } factory { SynchronizeFolderUseCase(get(), get()) } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt index ea65fe436e0..71c9156cd1f 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/LocalFileDataSource.kt @@ -36,7 +36,8 @@ interface LocalFileDataSource { fun getFolderContentAsLiveData(folderId: Long): LiveData> fun getFolderImages(folderId: Long): List fun getFilesSharedByLink(owner: String): List - fun getFilesAvailableOffline(owner: String): List + fun getFilesAvailableOfflineFromAccount(owner: String): List + fun getFilesAvailableOfflineFromEveryAccount(): List fun moveFile(sourceFile: OCFile, targetFolder: OCFile, finalRemotePath: String, finalStoragePath: String) fun saveFilesInFolder(listOfFiles: List, folder: OCFile) fun saveFile(file: OCFile) diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt index 964d15daf10..000ca7ffa64 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/datasources/implementation/OCLocalFileDataSource.kt @@ -104,9 +104,15 @@ class OCLocalFileDataSource( it.toModel() } - override fun getFilesAvailableOffline(owner: String): List = fileDao.getFilesAvailableOffline(accountOwner = owner).map { - it.toModel() - } + override fun getFilesAvailableOfflineFromAccount(owner: String): List = + fileDao.getFilesAvailableOfflineFromAccount(accountOwner = owner).map { + it.toModel() + } + + override fun getFilesAvailableOfflineFromEveryAccount(): List = + fileDao.getFilesAvailableOfflineFromEveryAccount().map { + it.toModel() + } override fun moveFile(sourceFile: OCFile, targetFolder: OCFile, finalRemotePath: String, finalStoragePath: String) = fileDao.moveFile( diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt index 733b3e38f1e..71ba79811aa 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/db/FileDao.kt @@ -90,11 +90,14 @@ abstract class FileDao { accountOwner: String ): List - @Query(SELECT_FILES_AVAILABLE_OFFLINE) - abstract fun getFilesAvailableOffline( + @Query(SELECT_FILES_AVAILABLE_OFFLINE_FROM_ACCOUNT) + abstract fun getFilesAvailableOfflineFromAccount( accountOwner: String ): List + @Query(SELECT_FILES_AVAILABLE_OFFLINE_FROM_EVERY_ACCOUNT) + abstract fun getFilesAvailableOfflineFromEveryAccount(): List + @Insert(onConflict = OnConflictStrategy.REPLACE) abstract fun insert(ocFileEntity: OCFileEntity): Long @@ -381,12 +384,17 @@ abstract class FileDao { "AND sharedByLink NOT LIKE '%0%' " + "OR sharedWithSharee NOT LIKE '%0%'" - private const val SELECT_FILES_AVAILABLE_OFFLINE = + private const val SELECT_FILES_AVAILABLE_OFFLINE_FROM_ACCOUNT = "SELECT * " + "FROM ${ProviderMeta.ProviderTableMeta.FILES_TABLE_NAME} " + "WHERE owner = :accountOwner " + "AND keepInSync = '1'" + private const val SELECT_FILES_AVAILABLE_OFFLINE_FROM_EVERY_ACCOUNT = + "SELECT * " + + "FROM ${ProviderMeta.ProviderTableMeta.FILES_TABLE_NAME} " + + "WHERE keepInSync = '1'" + private const val UPDATE_FILE_WITH_NEW_AVAILABLE_OFFLINE_STATUS = "UPDATE ${ProviderMeta.ProviderTableMeta.FILES_TABLE_NAME} " + "SET keepInSync = :availableOfflineStatus " + diff --git a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt index e699f6be1ad..a9949459e67 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/files/repository/OCFileRepository.kt @@ -134,8 +134,11 @@ class OCFileRepository( override fun getFilesSharedByLink(owner: String): List = localFileDataSource.getFilesSharedByLink(owner) - override fun getFilesAvailableOffline(owner: String): List = - localFileDataSource.getFilesAvailableOffline(owner) + override fun getFilesAvailableOfflineFromAccount(owner: String): List = + localFileDataSource.getFilesAvailableOfflineFromAccount(owner) + + override fun getFilesAvailableOfflineFromEveryAccount(): List = + localFileDataSource.getFilesAvailableOfflineFromEveryAccount() override fun moveFile(listOfFilesToMove: List, targetFile: OCFile) { listOfFilesToMove.forEach { ocFile -> diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromAccountUseCase.kt similarity index 84% rename from owncloudDomain/src/main/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineUseCase.kt rename to owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromAccountUseCase.kt index 6a8b02ac109..9ae27add63f 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromAccountUseCase.kt @@ -15,17 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package com.owncloud.android.domain.files.usecases +package com.owncloud.android.domain.availableoffline.usecases import com.owncloud.android.domain.BaseUseCaseWithResult import com.owncloud.android.domain.files.FileRepository import com.owncloud.android.domain.files.model.OCFile -class GetFilesAvailableOfflineUseCase( +class GetFilesAvailableOfflineFromAccountUseCase( private val fileRepository: FileRepository -) : BaseUseCaseWithResult, GetFilesAvailableOfflineUseCase.Params>() { +) : BaseUseCaseWithResult, GetFilesAvailableOfflineFromAccountUseCase.Params>() { - override fun run(params: Params): List = fileRepository.getFilesAvailableOffline(params.owner) + override fun run(params: Params): List = fileRepository.getFilesAvailableOfflineFromAccount(params.owner) data class Params( val owner: String diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt new file mode 100644 index 00000000000..ae79f422b57 --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt @@ -0,0 +1,30 @@ +/* + * ownCloud Android client application + * + * 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.availableoffline.usecases + +import com.owncloud.android.domain.BaseUseCase +import com.owncloud.android.domain.BaseUseCaseWithResult +import com.owncloud.android.domain.files.FileRepository +import com.owncloud.android.domain.files.model.OCFile + +class GetFilesAvailableOfflineFromEveryAccountUseCase( + private val fileRepository: FileRepository +) : BaseUseCase, Unit>() { + + override fun run(params: Unit): List = fileRepository.getFilesAvailableOfflineFromEveryAccount() +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt index 9d833784164..18c8a2e8ed3 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/files/FileRepository.kt @@ -35,7 +35,8 @@ interface FileRepository { fun getFolderContentAsLiveData(folderId: Long): LiveData> fun getFolderImages(folderId: Long): List fun getFilesSharedByLink(owner: String): List - fun getFilesAvailableOffline(owner: String): List + fun getFilesAvailableOfflineFromAccount(owner: String): List + fun getFilesAvailableOfflineFromEveryAccount(): List fun moveFile(listOfFilesToMove: List, targetFile: OCFile) fun readFile(remotePath: String): OCFile fun refreshFolder(remotePath: String): List diff --git a/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineUseCaseTest.kt b/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineUseCaseTest.kt index e0df9c44af3..1acacd8d87f 100644 --- a/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineUseCaseTest.kt +++ b/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineUseCaseTest.kt @@ -17,6 +17,7 @@ */ package com.owncloud.android.domain.files.usecases +import com.owncloud.android.domain.availableoffline.usecases.GetFilesAvailableOfflineUseCase import com.owncloud.android.domain.exceptions.UnauthorizedException import com.owncloud.android.domain.files.FileRepository import com.owncloud.android.testutil.OC_EMPTY_FILES @@ -35,37 +36,37 @@ class GetFilesAvailableOfflineUseCaseTest { @Test fun `get files available offline - ok`() { - every { repository.getFilesAvailableOffline(useCaseParams.owner) } returns OC_AVAILABLE_OFFLINE_FILES + every { repository.getFilesAvailableOfflineFromAccount(useCaseParams.owner) } returns OC_AVAILABLE_OFFLINE_FILES val useCaseResult = useCase.execute(useCaseParams) Assert.assertTrue(useCaseResult.isSuccess) Assert.assertEquals(OC_AVAILABLE_OFFLINE_FILES, useCaseResult.getDataOrNull()) - verify(exactly = 1) { repository.getFilesAvailableOffline(useCaseParams.owner) } + verify(exactly = 1) { repository.getFilesAvailableOfflineFromAccount(useCaseParams.owner) } } @Test fun `get files available offline - ok - empty list`() { - every { repository.getFilesAvailableOffline(useCaseParams.owner) } returns OC_EMPTY_FILES + every { repository.getFilesAvailableOfflineFromAccount(useCaseParams.owner) } returns OC_EMPTY_FILES val useCaseResult = useCase.execute(useCaseParams) Assert.assertTrue(useCaseResult.isSuccess) Assert.assertEquals(OC_EMPTY_FILES, useCaseResult.getDataOrNull()) - verify(exactly = 1) { repository.getFilesAvailableOffline(useCaseParams.owner) } + verify(exactly = 1) { repository.getFilesAvailableOfflineFromAccount(useCaseParams.owner) } } @Test fun `get files savailable offline - ko`() { - every { repository.getFilesAvailableOffline(useCaseParams.owner) } throws UnauthorizedException() + every { repository.getFilesAvailableOfflineFromAccount(useCaseParams.owner) } throws UnauthorizedException() val useCaseResult = useCase.execute(useCaseParams) Assert.assertTrue(useCaseResult.isError) Assert.assertTrue(useCaseResult.getThrowableOrNull() is UnauthorizedException) - verify(exactly = 1) { repository.getFilesAvailableOffline(useCaseParams.owner) } + verify(exactly = 1) { repository.getFilesAvailableOfflineFromAccount(useCaseParams.owner) } } } From a61b023aeb4e83c19efac2ebe605c2aba7d7806a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Sat, 16 Jul 2022 13:50:15 +0200 Subject: [PATCH 10/15] First draft to enqueue the available offline periodic worker. --- .../ui/settings/SettingsActivity.kt | 7 ++ .../android/providers/WorkManagerProvider.kt | 20 +++++ .../workers/AvailableOfflinePeriodicWorker.kt | 74 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 owncloudApp/src/main/java/com/owncloud/android/workers/AvailableOfflinePeriodicWorker.kt diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/SettingsActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/SettingsActivity.kt index a2826d265b2..77311c6e50c 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/SettingsActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/settings/SettingsActivity.kt @@ -36,6 +36,7 @@ import com.owncloud.android.presentation.ui.settings.fragments.SettingsMoreFragm import com.owncloud.android.presentation.ui.settings.fragments.SettingsPictureUploadsFragment import com.owncloud.android.presentation.ui.settings.fragments.SettingsSecurityFragment import com.owncloud.android.presentation.ui.settings.fragments.SettingsVideoUploadsFragment +import com.owncloud.android.providers.WorkManagerProvider import com.owncloud.android.ui.activity.FileDisplayActivity class SettingsActivity : AppCompatActivity() { @@ -61,6 +62,12 @@ class SettingsActivity : AppCompatActivity() { redirectToSubsection(intent) } + override fun onDestroy() { + val workerProvider = WorkManagerProvider(context = this) + workerProvider.enqueueAvailableOfflinePeriodicWorker() + super.onDestroy() + } + private fun updateToolbarTitle() { val titleId = when (supportFragmentManager.fragments.lastOrNull()) { is SettingsSecurityFragment -> R.string.prefs_subsection_security diff --git a/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt index b3fc3d90591..70ba84bce55 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt @@ -25,6 +25,8 @@ import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.NetworkType import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager +import com.owncloud.android.workers.AvailableOfflinePeriodicWorker +import com.owncloud.android.workers.AvailableOfflinePeriodicWorker.Companion.AVAILABLE_OFFLINE_PERIODIC_WORKER import com.owncloud.android.workers.CameraUploadsWorker import com.owncloud.android.workers.OldLogsCollectorWorker @@ -56,4 +58,22 @@ class WorkManagerProvider( WorkManager.getInstance(context) .enqueueUniquePeriodicWork(OldLogsCollectorWorker.OLD_LOGS_COLLECTOR_WORKER, ExistingPeriodicWorkPolicy.REPLACE, oldLogsCollectorWorker) } + + fun enqueueAvailableOfflinePeriodicWorker() { + val constraintsRequired = Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .setRequiresBatteryNotLow(true) + .build() + + val availableOfflinePeriodicWorker = PeriodicWorkRequestBuilder( + repeatInterval = AvailableOfflinePeriodicWorker.repeatInterval, + repeatIntervalTimeUnit = AvailableOfflinePeriodicWorker.repeatIntervalTimeUnit + ) + .addTag(AVAILABLE_OFFLINE_PERIODIC_WORKER) + .setConstraints(constraintsRequired) + .build() + + WorkManager.getInstance(context) + .enqueueUniquePeriodicWork(AVAILABLE_OFFLINE_PERIODIC_WORKER, ExistingPeriodicWorkPolicy.KEEP, availableOfflinePeriodicWorker) + } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/workers/AvailableOfflinePeriodicWorker.kt b/owncloudApp/src/main/java/com/owncloud/android/workers/AvailableOfflinePeriodicWorker.kt new file mode 100644 index 00000000000..d5b4924e32a --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/workers/AvailableOfflinePeriodicWorker.kt @@ -0,0 +1,74 @@ +/** + * 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.workers + +import android.content.Context +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters +import com.owncloud.android.domain.availableoffline.usecases.GetFilesAvailableOfflineFromEveryAccountUseCase +import com.owncloud.android.usecases.synchronization.SynchronizeFileUseCase +import com.owncloud.android.usecases.synchronization.SynchronizeFolderUseCase +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import timber.log.Timber +import java.util.concurrent.TimeUnit + +class AvailableOfflinePeriodicWorker( + val appContext: Context, + workerParameters: WorkerParameters +) : CoroutineWorker( + appContext, + workerParameters +), KoinComponent { + + private val getFilesAvailableOfflineFromEveryAccountUseCase: GetFilesAvailableOfflineFromEveryAccountUseCase by inject() + private val synchronizeFileUseCase: SynchronizeFileUseCase by inject() + private val synchronizeFolderUseCase: SynchronizeFolderUseCase by inject() + + override suspend fun doWork(): Result { + + return try { + val availableOfflineFiles = getFilesAvailableOfflineFromEveryAccountUseCase.execute(Unit) + Timber.i("Available offline files that needs to be synced: ${availableOfflineFiles.size}") + + availableOfflineFiles.forEach { + if (it.isFolder) { + synchronizeFolderUseCase.execute( + SynchronizeFolderUseCase.Params( + remotePath = it.remotePath, + accountName = it.owner, + syncMode = SynchronizeFolderUseCase.SyncFolderMode.SYNC_FOLDER_RECURSIVELY + ) + ) + } else { + synchronizeFileUseCase.execute(SynchronizeFileUseCase.Params(it)) + } + } + Result.success() + } catch (exception: Exception) { + Result.failure() + } + } + + companion object { + const val AVAILABLE_OFFLINE_PERIODIC_WORKER = "AVAILABLE_OFFLINE_PERIODIC_WORKER" + const val repeatInterval: Long = 15L + val repeatIntervalTimeUnit: TimeUnit = TimeUnit.MINUTES + } +} From 08def894f79a47d9d39bb5a8e41fda9f3bb1fc97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Mon, 18 Jul 2022 08:34:46 +0200 Subject: [PATCH 11/15] Polish a little bit the code --- .../workers/AvailableOfflinePeriodicWorker.kt | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/workers/AvailableOfflinePeriodicWorker.kt b/owncloudApp/src/main/java/com/owncloud/android/workers/AvailableOfflinePeriodicWorker.kt index d5b4924e32a..c000173cf2e 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/workers/AvailableOfflinePeriodicWorker.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/workers/AvailableOfflinePeriodicWorker.kt @@ -22,6 +22,7 @@ import android.content.Context import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.owncloud.android.domain.availableoffline.usecases.GetFilesAvailableOfflineFromEveryAccountUseCase +import com.owncloud.android.domain.files.model.OCFile import com.owncloud.android.usecases.synchronization.SynchronizeFileUseCase import com.owncloud.android.usecases.synchronization.SynchronizeFolderUseCase import org.koin.core.component.KoinComponent @@ -47,25 +48,29 @@ class AvailableOfflinePeriodicWorker( val availableOfflineFiles = getFilesAvailableOfflineFromEveryAccountUseCase.execute(Unit) Timber.i("Available offline files that needs to be synced: ${availableOfflineFiles.size}") - availableOfflineFiles.forEach { - if (it.isFolder) { - synchronizeFolderUseCase.execute( - SynchronizeFolderUseCase.Params( - remotePath = it.remotePath, - accountName = it.owner, - syncMode = SynchronizeFolderUseCase.SyncFolderMode.SYNC_FOLDER_RECURSIVELY - ) - ) - } else { - synchronizeFileUseCase.execute(SynchronizeFileUseCase.Params(it)) - } - } + syncAvailableOfflineFiles(availableOfflineFiles) + Result.success() } catch (exception: Exception) { Result.failure() } } + private fun syncAvailableOfflineFiles(availableOfflineFiles: List) { + availableOfflineFiles.forEach { + if (it.isFolder) { + synchronizeFolderUseCase.execute( + SynchronizeFolderUseCase.Params( + remotePath = it.remotePath, + accountName = it.owner, + syncMode = SynchronizeFolderUseCase.SyncFolderMode.SYNC_FOLDER_RECURSIVELY + ) + ) + } else { + synchronizeFileUseCase.execute(SynchronizeFileUseCase.Params(it)) + } + } + } companion object { const val AVAILABLE_OFFLINE_PERIODIC_WORKER = "AVAILABLE_OFFLINE_PERIODIC_WORKER" const val repeatInterval: Long = 15L From ea09a9123995f762ab5e40a7819a4d3cc28a528e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Mon, 18 Jul 2022 13:11:14 +0200 Subject: [PATCH 12/15] The legacy synchronize folder operation should not trigger a full sync. Just a refresh should be enough for the moment --- .../android/operations/SynchronizeFolderOperation.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java index 576b760489e..c06cbf123a3 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -60,6 +60,7 @@ import java.util.Vector; import java.util.concurrent.atomic.AtomicBoolean; +import static com.owncloud.android.usecases.synchronization.SynchronizeFolderUseCase.SyncFolderMode.REFRESH_FOLDER; import static com.owncloud.android.usecases.synchronization.SynchronizeFolderUseCase.SyncFolderMode.SYNC_FOLDER_RECURSIVELY; import static org.koin.java.KoinJavaComponent.get; import static org.koin.java.KoinJavaComponent.inject; @@ -208,7 +209,7 @@ protected RemoteOperationResult> run(OwnCloudClient client final RemoteOperationResult> fetchFolderResult; SynchronizeFolderUseCase synchronizeFolderUseCase = get(SynchronizeFolderUseCase.class); - synchronizeFolderUseCase.execute(new SynchronizeFolderUseCase.Params(mRemotePath, mAccount.name, SYNC_FOLDER_RECURSIVELY)); + synchronizeFolderUseCase.execute(new SynchronizeFolderUseCase.Params(mRemotePath, mAccount.name, REFRESH_FOLDER)); mFailsInFileSyncsFound = 0; mConflictsFound = 0; From 5db221e015f9147dddc18365eb8f1be39895d34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Mon, 18 Jul 2022 13:40:03 +0200 Subject: [PATCH 13/15] Remove legacy code, available offline will be synced via workmanager --- owncloudApp/src/main/AndroidManifest.xml | 3 - .../datamodel/FileDataStorageManager.kt | 90 --------- .../services/AvailableOfflineHandler.java | 74 ------- .../AvailableOfflineSyncJobService.java | 186 ------------------ .../SetFilesAsAvailableOfflineUseCase.kt | 1 + .../UnsetFilesAsAvailableOfflineUseCase.kt | 1 + 6 files changed, 2 insertions(+), 353 deletions(-) delete mode 100644 owncloudApp/src/main/java/com/owncloud/android/files/services/AvailableOfflineHandler.java delete mode 100644 owncloudApp/src/main/java/com/owncloud/android/files/services/AvailableOfflineSyncJobService.java diff --git a/owncloudApp/src/main/AndroidManifest.xml b/owncloudApp/src/main/AndroidManifest.xml index 0abeee83540..8f15da93d0b 100644 --- a/owncloudApp/src/main/AndroidManifest.xml +++ b/owncloudApp/src/main/AndroidManifest.xml @@ -138,9 +138,6 @@ android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter_files" /> - > { - return listOf() - // FIXME: 13/10/2020 : New_arch: Av.Offline -// val result = ArrayList>() -// var cursorOnKeptInSync: Cursor? = null -// try { -// cursorOnKeptInSync = performQuery( -// uri = CONTENT_URI, -// projection = null, -// selection = "$FILE_KEEP_IN_SYNC = ? OR $FILE_KEEP_IN_SYNC = ?", -// selectionArgs = arrayOf(AVAILABLE_OFFLINE.value.toString(), AVAILABLE_OFFLINE_PARENT.value.toString()), -// sortOrder = null, -// performWithContentProviderClient = false -// ) -// -// if (cursorOnKeptInSync != null && cursorOnKeptInSync.moveToFirst()) { -// var file: OCFile? -// var accountName: String -// do { -// file = createFileInstance(cursorOnKeptInSync) -// accountName = -// cursorOnKeptInSync.getStringFromColumnOrEmpty(FILE_ACCOUNT_OWNER) -// if (!file!!.isFolder && AccountUtils.exists(accountName, mContext)) { -// result.add(Pair(file, accountName)) -// } -// } while (cursorOnKeptInSync.moveToNext()) -// } else { -// Timber.d("No available offline files found") -// } -// -// } catch (e: Exception) { -// Timber.e(e, "Exception retrieving all the available offline files") -// -// } finally { -// cursorOnKeptInSync?.close() -// } -// -// return result - } - fun sharedByLinkFilesFromCurrentAccount(): List? = runBlocking(CoroutinesDispatcherProvider().io) { val getFilesSharedByLinkUseCase: GetFilesSharedByLinkUseCase by inject() @@ -506,37 +454,6 @@ class FileDataStorageManager : KoinComponent { result ?: listOf() } - /** - * Checks if it is favorite or it is inside a favorite folder - * - * @param file [OCFile] which ancestors will be searched. - * @return true/false - */ - // FIXME: 13/10/2020 : New_arch: Av.Offline - private fun isAnyAncestorAvailableOfflineFolder(file: OCFile) = false //getAvailableOfflineAncestorOf(file) != null - - /** - * Returns ancestor folder with available offline status AVAILABLE_OFFLINE. - * - * @param file [OCFile] which ancestors will be searched. - * @return Ancestor folder with available offline status AVAILABLE_OFFLINE, or null if - * does not exist. - */ - // FIXME: 13/10/2020 : New_arch: Av.Offline - private fun getAvailableOfflineAncestorOf(file: OCFile): OCFile? { - return null -// var avOffAncestor: OCFile? = null -// val parent = getFileById(file.parentId) -// if (parent != null && parent.isFolder) { // file is null for the parent of the root folder -// if (parent.availableOfflineStatus == AVAILABLE_OFFLINE) { -// avOffAncestor = parent -// } else if (parent.fileName != ROOT_PATH) { -// avOffAncestor = getAvailableOfflineAncestorOf(parent) -// } -// } -// return avOffAncestor - } - // FIXME: 13/10/2020 : New_arch: Migration private fun createFileInstance(c: Cursor?): OCFile? = null//c?.let { // OCFile(it.getStringFromColumnOrThrow(FILE_PATH)).apply { @@ -840,13 +757,6 @@ class FileDataStorageManager : KoinComponent { ) } - // FIXME: 13/10/2020 : New_arch: Av.Offline - private fun selectionForAllDescendantsOf(file: OCFile): Pair> { - val selection = "$FILE_ACCOUNT_OWNER=? AND $FILE_PATH LIKE ? " - val selectionArgs = arrayOf(account.name, "${file.remotePath}_%") // one or more characters after remote path - return Pair(selection, selectionArgs) - } - private fun performQuery( uri: Uri, projection: Array?, diff --git a/owncloudApp/src/main/java/com/owncloud/android/files/services/AvailableOfflineHandler.java b/owncloudApp/src/main/java/com/owncloud/android/files/services/AvailableOfflineHandler.java deleted file mode 100644 index 331877eb874..00000000000 --- a/owncloudApp/src/main/java/com/owncloud/android/files/services/AvailableOfflineHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * ownCloud Android client application - * - * @author David González Verdugo - * Copyright (C) 2020 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.files.services; - -import android.app.job.JobInfo; -import android.app.job.JobScheduler; -import android.content.ComponentName; -import android.content.Context; -import android.os.PersistableBundle; - -import com.owncloud.android.utils.Extras; -import timber.log.Timber; - -/** - * Schedule the periodic job responsible for synchronizing available offline files, a.k.a. kept-in-sync files, that - * have been updated locally, with the remote server - */ -public class AvailableOfflineHandler { - - private static final long MILLISECONDS_INTERVAL_AVAILABLE_OFFLINE = 900000; - - // It needs to be always the same so that the previous job is removed and replaced with a new one with the recent - // configuration - private static final int JOB_ID_AVAILABLE_OFFLINE = 2; - - private JobScheduler mJobScheduler; - - public AvailableOfflineHandler(Context context) { - mJobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - } - - /** - * Schedule a periodic job to check whether recently updated available offline files need to be synchronized - */ - public void scheduleAvailableOfflineJob(Context context) { - ComponentName serviceComponent = new ComponentName(context, AvailableOfflineSyncJobService.class); - JobInfo.Builder builder; - - builder = new JobInfo.Builder(JOB_ID_AVAILABLE_OFFLINE, serviceComponent); - - builder.setPersisted(true); - - // Execute job every 15 minutes - builder.setPeriodic(MILLISECONDS_INTERVAL_AVAILABLE_OFFLINE); - - // Extra data - PersistableBundle extras = new PersistableBundle(); - - extras.putInt(Extras.EXTRA_AVAILABLE_OFFLINE_SYNC_JOB_ID, JOB_ID_AVAILABLE_OFFLINE); - - builder.setExtras(extras); - - Timber.d("Scheduling an AvailableOfflineSyncJobService"); - - mJobScheduler.schedule(builder.build()); - } -} diff --git a/owncloudApp/src/main/java/com/owncloud/android/files/services/AvailableOfflineSyncJobService.java b/owncloudApp/src/main/java/com/owncloud/android/files/services/AvailableOfflineSyncJobService.java deleted file mode 100644 index ca349aa5ffb..00000000000 --- a/owncloudApp/src/main/java/com/owncloud/android/files/services/AvailableOfflineSyncJobService.java +++ /dev/null @@ -1,186 +0,0 @@ -/** - * ownCloud Android client application - * - * @author David González Verdugo - * Copyright (C) 2020 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.files.services; - -import android.app.job.JobParameters; -import android.app.job.JobScheduler; -import android.app.job.JobService; -import android.content.Context; -import android.os.AsyncTask; - -import androidx.core.util.Pair; -import com.owncloud.android.domain.files.model.OCFile; -import timber.log.Timber; - -import java.util.List; - -/** - * Job to watch for local changes in available offline files (formerly known as kept-in-sync files) and try to - * synchronize them with the OC server. - * This job should be executed every 15 minutes since a file is set as available offline for the first time and stopped - * when there's no available offline files - */ -public class AvailableOfflineSyncJobService extends JobService { - - @Override - public boolean onStartJob(JobParameters jobParameters) { - Timber.d("Starting job to sync available offline files"); - - new AvailableOfflineJobTask(this).execute(jobParameters); - - return true; // True because we have a thread still running in background - } - - private static class AvailableOfflineJobTask extends AsyncTask { - - private final JobService mAvailableOfflineJobService; - - public AvailableOfflineJobTask(JobService mAvailableOfflineJobService) { - this.mAvailableOfflineJobService = mAvailableOfflineJobService; - } - - @Override - protected JobParameters doInBackground(JobParameters... jobParams) { -// FIXME: 13/10/2020 : New_arch: Av.Offline -// Account account; -// -// if (mAvailableOfflineJobService != null) { -// account = AccountUtils.getCurrentOwnCloudAccount(mAvailableOfflineJobService.getApplicationContext()); -// } else { -// Timber.w("AvailableOfflineJobService is null"); -// return jobParams[0]; -// } -// -// if (account == null) { -// Timber.w("Account is null, do not sync av. offline files."); -// return jobParams[0]; -// } -// -// if (mAvailableOfflineJobService.getContentResolver() == null) { -// Timber.w("Content resolver is null, do not sync av. offline files."); -// return jobParams[0]; -// } -// -// FileDataStorageManager fileDataStorageManager = new FileDataStorageManager( -// mAvailableOfflineJobService, account, mAvailableOfflineJobService.getContentResolver() -// ); -// -// List> availableOfflineFilesFromEveryAccount = fileDataStorageManager. -// getAvailableOfflineFilesFromEveryAccount(); -// -// // Cancel periodic job if there's no available offline files to watch for local changes -// if (availableOfflineFilesFromEveryAccount.isEmpty()) { -// Timber.w("No available files for any account."); -// cancelPeriodicJob(jobParams[0].getJobId()); -// return jobParams[0]; -// } else { -// syncAvailableOfflineFiles(availableOfflineFilesFromEveryAccount); -// } - - return jobParams[0]; - } - - private void syncAvailableOfflineFiles(List> availableOfflineFilesForAccount) { - // FIXME: 13/10/2020 : New_arch: Av.Offline -// for (Pair fileForAccount : availableOfflineFilesForAccount) { -// -// String localPath = fileForAccount.first.getStoragePath(); -// -// if (localPath == null) { -// localPath = FileStorageUtils.getDefaultSavePathFor( -// fileForAccount.second, // Account name -// fileForAccount.first // OCFile -// ); -// } -// -// File localFile = new File(localPath); -// -// if (localFile.lastModified() <= fileForAccount.first.getLastSyncDateForData() && -// MainApp.Companion.getEnabledLogging()) { -// Timber.i("File " + fileForAccount.first.getRemotePath() + " already synchronized " + -// "in account " + fileForAccount.second + ", ignoring"); -// continue; -// } -// -// startSyncOperation(fileForAccount.first, fileForAccount.second); -// } - } - - /** - * Triggers an operation to synchronize the contents of a recently modified available offline file with - * its remote counterpart in the associated ownCloud account. - * - * @param availableOfflineFile file to synchronize - * @param accountName account to synchronize the available offline file with - */ - private void startSyncOperation(OCFile availableOfflineFile, String accountName) { - // FIXME: 13/10/2020 : New_arch: Av.Offline - Timber.i("Requested synchronization for file %1s in account %2s", - availableOfflineFile.getRemotePath(), accountName); -// -// Account account = AccountUtils.getOwnCloudAccountByName(mAvailableOfflineJobService, accountName); -// if (account == null) { -// Timber.w("Account '" + accountName + "' not found in account manager. Aborting Sync operation..."); -// return; -// } -// -// FileDataStorageManager storageManager = -// new FileDataStorageManager(mAvailableOfflineJobService, account, mAvailableOfflineJobService.getContentResolver()); -// -// SynchronizeFileOperation synchronizeFileOperation = -// new SynchronizeFileOperation(availableOfflineFile, null, account, false, -// mAvailableOfflineJobService, true); -// -// RemoteOperationResult result = synchronizeFileOperation.execute(storageManager, mAvailableOfflineJobService); -// -// if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) { -// notifyConflict(availableOfflineFile, account, mAvailableOfflineJobService); -// } - } - - /** - * Cancel the periodic job - * - * @param jobId id of the job to cancel - */ - private void cancelPeriodicJob(int jobId) { - JobScheduler jobScheduler = (JobScheduler) mAvailableOfflineJobService.getSystemService( - Context.JOB_SCHEDULER_SERVICE); - - jobScheduler.cancel(jobId); - - Timber.d("No available offline files to check, cancelling the periodic job"); - } - - @Override - protected void onPostExecute(JobParameters jobParameters) { - mAvailableOfflineJobService.jobFinished(jobParameters, false); - } - } - - @Override - /* - * Called by the system if the job is cancelled before being finished - */ - public boolean onStopJob(JobParameters jobParameters) { - Timber.d("Job was cancelled before finishing."); - return true; - } -} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt index c4cc2dfd08e..dacd8b1d2ae 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt @@ -29,6 +29,7 @@ class SetFilesAsAvailableOfflineUseCase( override fun run(params: Params) { params.filesToSetAsAvailableOffline.forEach { fileToSetAsAvailableOffline -> // Its possible to multiselect several files including already available offline files. + // If it is already available offline, we will ignore it. if (!fileToSetAsAvailableOffline.isAvailableOffline) { availableOfflineRepository.setFileAsAvailableOffline(fileToSetAsAvailableOffline) } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt index 91cb0774dfc..4d425580574 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt @@ -29,6 +29,7 @@ class UnsetFilesAsAvailableOfflineUseCase( override fun run(params: Params) { params.filesToUnsetAsAvailableOffline.forEach { fileToUnsetAsAvailableOffline -> // Its possible to multiselect several files including not available offline files. + // If it is not available offline, we will ignore it. if (fileToUnsetAsAvailableOffline.isAvailableOffline) { availableOfflineRepository.unsetFileAsAvailableOffline(fileToUnsetAsAvailableOffline) } From 686a267b7da120b4674c19f612572d3f247f3867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Wed, 20 Jul 2022 08:58:46 +0200 Subject: [PATCH 14/15] Polishment --- .../android/datamodel/FileDataStorageManager.kt | 2 -- .../ui/files/removefile/RemoveFilesDialogFragment.kt | 2 +- .../availableoffline/model/AvailableOfflineStatus.kt | 1 - .../GetFilesAvailableOfflineFromEveryAccountUseCase.kt | 1 - ... GetFilesAvailableOfflineFromAccountUseCaseTest.kt} | 10 +++++----- .../main/java/com/owncloud/android/testutil/OCFile.kt | 3 ++- 6 files changed, 8 insertions(+), 11 deletions(-) rename owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/{GetFilesAvailableOfflineUseCaseTest.kt => GetFilesAvailableOfflineFromAccountUseCaseTest.kt} (90%) diff --git a/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt b/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt index 54e50264848..f66e76cb1ef 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.kt @@ -32,7 +32,6 @@ import android.content.Context import android.database.Cursor import android.net.Uri import android.os.RemoteException -import androidx.core.util.Pair import com.owncloud.android.db.ProviderMeta.ProviderTableMeta.CAPABILITIES_ACCOUNT_NAME import com.owncloud.android.db.ProviderMeta.ProviderTableMeta.CAPABILITIES_CORE_POLLINTERVAL import com.owncloud.android.db.ProviderMeta.ProviderTableMeta.CAPABILITIES_DAV_CHUNKING_VERSION @@ -83,7 +82,6 @@ import kotlinx.coroutines.withContext import org.koin.core.component.KoinComponent import org.koin.core.component.inject import timber.log.Timber -import java.util.Vector class FileDataStorageManager : KoinComponent { diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/removefile/RemoveFilesDialogFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/removefile/RemoveFilesDialogFragment.kt index cd6ab2225b0..7438d08306d 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/removefile/RemoveFilesDialogFragment.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/files/removefile/RemoveFilesDialogFragment.kt @@ -90,7 +90,7 @@ class RemoveFilesDialogFragment : ConfirmationDialogFragment(), ConfirmationDial containsDown = true } if (file.isAvailableOffline) { - containsAvailableOffline = true; + containsAvailableOffline = true } } messageStringId = if (files.size == 1) { diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt index 0440a98f0a6..7dd8ae7502a 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/model/AvailableOfflineStatus.kt @@ -18,7 +18,6 @@ */ package com.owncloud.android.domain.availableoffline.model -import com.owncloud.android.domain.ext.isOneOf enum class AvailableOfflineStatus { /** diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt index ae79f422b57..6120a3b30cb 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt @@ -18,7 +18,6 @@ package com.owncloud.android.domain.availableoffline.usecases import com.owncloud.android.domain.BaseUseCase -import com.owncloud.android.domain.BaseUseCaseWithResult import com.owncloud.android.domain.files.FileRepository import com.owncloud.android.domain.files.model.OCFile diff --git a/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineUseCaseTest.kt b/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineFromAccountUseCaseTest.kt similarity index 90% rename from owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineUseCaseTest.kt rename to owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineFromAccountUseCaseTest.kt index 1acacd8d87f..ea8eeb06abe 100644 --- a/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineUseCaseTest.kt +++ b/owncloudDomain/src/test/java/com/owncloud/android/domain/files/usecases/GetFilesAvailableOfflineFromAccountUseCaseTest.kt @@ -17,22 +17,22 @@ */ package com.owncloud.android.domain.files.usecases -import com.owncloud.android.domain.availableoffline.usecases.GetFilesAvailableOfflineUseCase +import com.owncloud.android.domain.availableoffline.usecases.GetFilesAvailableOfflineFromAccountUseCase import com.owncloud.android.domain.exceptions.UnauthorizedException import com.owncloud.android.domain.files.FileRepository -import com.owncloud.android.testutil.OC_EMPTY_FILES import com.owncloud.android.testutil.OC_AVAILABLE_OFFLINE_FILES +import com.owncloud.android.testutil.OC_EMPTY_FILES import io.mockk.every import io.mockk.spyk import io.mockk.verify import org.junit.Assert import org.junit.Test -class GetFilesAvailableOfflineUseCaseTest { +class GetFilesAvailableOfflineFromAccountUseCaseTest { private val repository: FileRepository = spyk() - private val useCase = GetFilesAvailableOfflineUseCase(repository) - private val useCaseParams = GetFilesAvailableOfflineUseCase.Params(owner = "owner") + private val useCase = GetFilesAvailableOfflineFromAccountUseCase(repository) + private val useCaseParams = GetFilesAvailableOfflineFromAccountUseCase.Params(owner = "owner") @Test fun `get files available offline - ok`() { diff --git a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt index aa3b7fcdc02..505cbbc9527 100644 --- a/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt +++ b/owncloudTestUtil/src/main/java/com/owncloud/android/testutil/OCFile.kt @@ -48,7 +48,8 @@ val OC_FILE = OCFile( modificationTimestamp = 1593510589000, etag = "5efb0c13c688f", mimeType = "image/jpeg", - length = 3000000 + length = 3000000, + availableOfflineStatus = AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE, ) val OC_AVAILABLE_OFFLINE_FILE = OCFile( From f7ecb01b5fc0b79e207ca1d9f709e5b13c62bcb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Wed, 20 Jul 2022 17:20:09 +0200 Subject: [PATCH 15/15] Remove redundant repository --- .../dependecyinjection/RepositoryModule.kt | 4 +- .../dependecyinjection/UseCaseModule.kt | 4 +- .../AvailableOfflineRepositoryImpl.kt | 37 ------------------- .../AvailableOfflineRepository.kt | 27 -------------- ...AvailableOfflineFromEveryAccountUseCase.kt | 1 + .../SetFilesAsAvailableOfflineUseCase.kt | 10 +++-- .../UnsetFilesAsAvailableOfflineUseCase.kt | 12 ++++-- 7 files changed, 19 insertions(+), 76 deletions(-) delete mode 100644 owncloudData/src/main/java/com/owncloud/android/data/availableoffline/AvailableOfflineRepositoryImpl.kt delete mode 100644 owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/AvailableOfflineRepository.kt 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 468b807d571..33f941ea5f2 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/RepositoryModule.kt @@ -23,7 +23,6 @@ package com.owncloud.android.dependecyinjection import com.owncloud.android.data.authentication.repository.OCAuthenticationRepository -import com.owncloud.android.data.availableoffline.AvailableOfflineRepositoryImpl import com.owncloud.android.data.capabilities.repository.OCCapabilityRepository import com.owncloud.android.data.files.repository.OCFileRepository import com.owncloud.android.data.folderbackup.FolderBackupRepositoryImpl @@ -35,7 +34,6 @@ import com.owncloud.android.data.transfers.repository.OCTransferRepository import com.owncloud.android.data.user.repository.OCUserRepository import com.owncloud.android.domain.authentication.AuthenticationRepository import com.owncloud.android.domain.authentication.oauth.OAuthRepository -import com.owncloud.android.domain.availableoffline.AvailableOfflineRepository import com.owncloud.android.domain.camerauploads.FolderBackupRepository import com.owncloud.android.domain.capabilities.CapabilityRepository import com.owncloud.android.domain.files.FileRepository @@ -57,5 +55,5 @@ val repositoryModule = module { factory { OAuthRepositoryImpl(get()) } factory { FolderBackupRepositoryImpl(get()) } factory { OCTransferRepository(get()) } - factory { AvailableOfflineRepositoryImpl(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 ecc3c3467a2..b2b1a442424 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -115,14 +115,14 @@ val useCaseModule = module { factory { RenameFileUseCase(get()) } factory { SaveFileOrFolderUseCase(get()) } factory { GetFilesSharedByLinkUseCase(get()) } - factory { GetFilesAvailableOfflineFromAccountUseCase(get()) } - factory { GetFilesAvailableOfflineFromEveryAccountUseCase(get()) } factory { GetSearchFolderContentUseCase(get()) } factory { SynchronizeFileUseCase(get(), get(), get(), get()) } factory { SynchronizeFolderUseCase(get(), get()) } factory { SortFilesUseCase() } // Av Offline + factory { GetFilesAvailableOfflineFromAccountUseCase(get()) } + factory { GetFilesAvailableOfflineFromEveryAccountUseCase(get()) } factory { SetFilesAsAvailableOfflineUseCase(get()) } factory { UnsetFilesAsAvailableOfflineUseCase(get()) } diff --git a/owncloudData/src/main/java/com/owncloud/android/data/availableoffline/AvailableOfflineRepositoryImpl.kt b/owncloudData/src/main/java/com/owncloud/android/data/availableoffline/AvailableOfflineRepositoryImpl.kt deleted file mode 100644 index e2f7d58e5bf..00000000000 --- a/owncloudData/src/main/java/com/owncloud/android/data/availableoffline/AvailableOfflineRepositoryImpl.kt +++ /dev/null @@ -1,37 +0,0 @@ -/** - * 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.availableoffline - -import com.owncloud.android.domain.availableoffline.AvailableOfflineRepository -import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus -import com.owncloud.android.domain.files.FileRepository -import com.owncloud.android.domain.files.model.OCFile - -class AvailableOfflineRepositoryImpl( - val fileRepository: FileRepository, -) : AvailableOfflineRepository { - override fun setFileAsAvailableOffline(fileToSetAsAvailableOffline: OCFile) { - fileRepository.updateFileWithNewAvailableOfflineStatus(fileToSetAsAvailableOffline, AvailableOfflineStatus.AVAILABLE_OFFLINE) - } - - override fun unsetFileAsAvailableOffline(fileToSetAsAvailableOffline: OCFile) { - fileRepository.updateFileWithNewAvailableOfflineStatus(fileToSetAsAvailableOffline, AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE) - } - -} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/AvailableOfflineRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/AvailableOfflineRepository.kt deleted file mode 100644 index 7c4c3759b92..00000000000 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/AvailableOfflineRepository.kt +++ /dev/null @@ -1,27 +0,0 @@ -/** - * 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.availableoffline - -import com.owncloud.android.domain.files.model.OCFile - -interface AvailableOfflineRepository { - fun setFileAsAvailableOffline(fileToSetAsAvailableOffline: OCFile) - fun unsetFileAsAvailableOffline(fileToSetAsAvailableOffline: OCFile) - -} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt index 6120a3b30cb..daa06c819fc 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/GetFilesAvailableOfflineFromEveryAccountUseCase.kt @@ -1,6 +1,7 @@ /* * 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 diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt index dacd8b1d2ae..05b8136b6ac 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/SetFilesAsAvailableOfflineUseCase.kt @@ -19,11 +19,12 @@ package com.owncloud.android.domain.availableoffline.usecases import com.owncloud.android.domain.BaseUseCaseWithResult -import com.owncloud.android.domain.availableoffline.AvailableOfflineRepository +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus +import com.owncloud.android.domain.files.FileRepository import com.owncloud.android.domain.files.model.OCFile class SetFilesAsAvailableOfflineUseCase( - private val availableOfflineRepository: AvailableOfflineRepository, + private val fileRepository: FileRepository, ) : BaseUseCaseWithResult() { override fun run(params: Params) { @@ -31,7 +32,10 @@ class SetFilesAsAvailableOfflineUseCase( // Its possible to multiselect several files including already available offline files. // If it is already available offline, we will ignore it. if (!fileToSetAsAvailableOffline.isAvailableOffline) { - availableOfflineRepository.setFileAsAvailableOffline(fileToSetAsAvailableOffline) + fileRepository.updateFileWithNewAvailableOfflineStatus( + ocFile = fileToSetAsAvailableOffline, + newAvailableOfflineStatus = AvailableOfflineStatus.AVAILABLE_OFFLINE, + ) } } } diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt index 4d425580574..e2dfbf10919 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/availableoffline/usecases/UnsetFilesAsAvailableOfflineUseCase.kt @@ -19,19 +19,23 @@ package com.owncloud.android.domain.availableoffline.usecases import com.owncloud.android.domain.BaseUseCaseWithResult -import com.owncloud.android.domain.availableoffline.AvailableOfflineRepository +import com.owncloud.android.domain.availableoffline.model.AvailableOfflineStatus +import com.owncloud.android.domain.files.FileRepository import com.owncloud.android.domain.files.model.OCFile class UnsetFilesAsAvailableOfflineUseCase( - private val availableOfflineRepository: AvailableOfflineRepository, + private val fileRepository: FileRepository, ) : BaseUseCaseWithResult() { override fun run(params: Params) { params.filesToUnsetAsAvailableOffline.forEach { fileToUnsetAsAvailableOffline -> // Its possible to multiselect several files including not available offline files. // If it is not available offline, we will ignore it. - if (fileToUnsetAsAvailableOffline.isAvailableOffline) { - availableOfflineRepository.unsetFileAsAvailableOffline(fileToUnsetAsAvailableOffline) + if (fileToUnsetAsAvailableOffline.availableOfflineStatus == AvailableOfflineStatus.AVAILABLE_OFFLINE) { + fileRepository.updateFileWithNewAvailableOfflineStatus( + ocFile = fileToUnsetAsAvailableOffline, + newAvailableOfflineStatus = AvailableOfflineStatus.NOT_AVAILABLE_OFFLINE, + ) } } }