Skip to content

Commit

Permalink
Merge pull request #4399 from owncloud/fix/av_offline_files_removed_l…
Browse files Browse the repository at this point in the history
…ocally_with_local_only_option

[BUG] Av. offline files removed locally with the `Local only` option
  • Loading branch information
Aitorbp committed Jun 14, 2024
2 parents 2684ebf + 080a201 commit 8af346d
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 47 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ ownCloud admins and users.
* Bugfix - Video streaming in spaces: [#4328](https://github.com/owncloud/android/issues/4328)
* Bugfix - Retried successful uploads are cleaned up from the temporary folder: [#4335](https://github.com/owncloud/android/issues/4335)
* Bugfix - Resolve incorrect truncation of long display names in Manage Accounts: [#4351](https://github.com/owncloud/android/issues/4351)
* Bugfix - Av. offline files are not removed when "Local only" option is clicked: [#4353](https://github.com/owncloud/android/issues/4353)
* Bugfix - Unwanted DELETE operations when synchronization in single file fails: [#6638](https://github.com/owncloud/enterprise/issues/6638)
* Change - Upgrade minimum SDK version to Android 7.0 (v24): [#4230](https://github.com/owncloud/android/issues/4230)
* Change - Automatic discovery of the account in login: [#4301](https://github.com/owncloud/android/issues/4301)
Expand Down Expand Up @@ -100,6 +101,16 @@ ownCloud admins and users.
https://github.com/owncloud/android/issues/4351
https://github.com/owncloud/android/pull/4380

* Bugfix - Av. offline files are not removed when "Local only" option is clicked: [#4353](https://github.com/owncloud/android/issues/4353)

"Local only" option in remove dialog will be displayed when the selected folder
contains at least one downloaded file, ignoring those available offline. If the
"Local only" option is displayed and clicked, available offline files will not
be deleted.

https://github.com/owncloud/android/issues/4353
https://github.com/owncloud/android/pull/4399

* Bugfix - Unwanted DELETE operations when synchronization in single file fails: [#6638](https://github.com/owncloud/enterprise/issues/6638)

A new exception is now thrown and handled when the account of the network client
Expand Down
7 changes: 7 additions & 0 deletions changelog/unreleased/4399
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Bugfix: Av. offline files are not removed when "Local only" option is clicked

"Local only" option in remove dialog will be displayed when the selected folder contains at least one downloaded file, ignoring those available offline.
If the "Local only" option is displayed and clicked, available offline files will not be deleted.

https://github.com/owncloud/android/issues/4353
https://github.com/owncloud/android/pull/4399
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import com.owncloud.android.domain.camerauploads.usecases.SaveVideoUploadsConfig
import com.owncloud.android.domain.capabilities.usecases.GetCapabilitiesAsLiveDataUseCase
import com.owncloud.android.domain.capabilities.usecases.GetStoredCapabilitiesUseCase
import com.owncloud.android.domain.capabilities.usecases.RefreshCapabilitiesFromServerAsyncUseCase
import com.owncloud.android.domain.files.usecases.IsAnyFileAvailableLocallyAndNotAvailableOfflineUseCase
import com.owncloud.android.domain.files.usecases.CleanConflictUseCase
import com.owncloud.android.domain.files.usecases.CleanWorkersUUIDUseCase
import com.owncloud.android.domain.files.usecases.CopyFileUseCase
Expand All @@ -60,7 +61,6 @@ import com.owncloud.android.domain.files.usecases.GetFileByRemotePathUseCase
import com.owncloud.android.domain.files.usecases.GetFileWithSyncInfoByIdUseCase
import com.owncloud.android.domain.files.usecases.GetFolderContentAsStreamUseCase
import com.owncloud.android.domain.files.usecases.GetFolderContentUseCase
import com.owncloud.android.domain.files.usecases.IsAnyFileAvailableLocallyUseCase
import com.owncloud.android.domain.files.usecases.GetFolderImagesUseCase
import com.owncloud.android.domain.files.usecases.GetPersonalRootFolderForAccountUseCase
import com.owncloud.android.domain.files.usecases.GetSearchFolderContentUseCase
Expand Down Expand Up @@ -168,7 +168,7 @@ val useCaseModule = module {
factoryOf(::GetFolderContentAsStreamUseCase)
factoryOf(::GetFolderContentUseCase)
factoryOf(::GetFolderImagesUseCase)
factoryOf(::IsAnyFileAvailableLocallyUseCase)
factoryOf(::IsAnyFileAvailableLocallyAndNotAvailableOfflineUseCase)
factoryOf(::GetPersonalRootFolderForAccountUseCase)
factoryOf(::GetSearchFolderContentUseCase)
factoryOf(::GetSharedByLinkForAccountAsStreamUseCase)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,13 @@ class MainFileListFragment : Fragment(),
FileMenuOption.SET_AV_OFFLINE -> {
fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(listOf(file)))
if (file.isFolder) {
fileOperationsViewModel.performOperation(FileOperation.SynchronizeFolderOperation(file, file.owner))
fileOperationsViewModel.performOperation(
FileOperation.SynchronizeFolderOperation(
folderToSync = file,
accountName = file.owner,
isActionSetFolderAvailableOfflineOrSynchronize = true,
)
)
} else {
fileOperationsViewModel.performOperation(FileOperation.SynchronizeFileOperation(file, file.owner))
}
Expand Down Expand Up @@ -647,7 +653,7 @@ class MainFileListFragment : Fragment(),
}
}

collectLatestLifecycleFlow(fileOperationsViewModel.checkIfFileLocalSharedFlow) {
collectLatestLifecycleFlow(fileOperationsViewModel.checkIfFileIsLocalAndNotAvailableOfflineSharedFlow) {
val fileActivity = (requireActivity() as FileActivity)
when (it) {
is UIResult.Loading -> fileActivity.showLoadingDialog(R.string.common_loading)
Expand Down Expand Up @@ -990,8 +996,8 @@ class MainFileListFragment : Fragment(),
}
}

private fun onShowRemoveDialog(filesToRemove: List<OCFile>, isLocal: Boolean) {
val dialog = RemoveFilesDialogFragment.newInstance(ArrayList(filesToRemove), isLocal)
private fun onShowRemoveDialog(filesToRemove: List<OCFile>, isAvailableLocallyAndNotAvailableOffline: Boolean) {
val dialog = RemoveFilesDialogFragment.newInstance(ArrayList(filesToRemove), isAvailableLocallyAndNotAvailableOffline)
dialog.show(requireActivity().supportFragmentManager, ConfirmationDialogFragment.FTAG_CONFIRMATION)
fileListAdapter.clearSelection()
updateActionModeAfterTogglingSelected()
Expand Down Expand Up @@ -1129,7 +1135,13 @@ class MainFileListFragment : Fragment(),
R.id.action_set_available_offline -> {
fileOperationsViewModel.performOperation(FileOperation.SetFilesAsAvailableOffline(listOf(singleFile)))
if (singleFile.isFolder) {
fileOperationsViewModel.performOperation(FileOperation.SynchronizeFolderOperation(singleFile, singleFile.owner))
fileOperationsViewModel.performOperation(
FileOperation.SynchronizeFolderOperation(
folderToSync = singleFile,
accountName = singleFile.owner,
isActionSetFolderAvailableOfflineOrSynchronize = true,
)
)
} else {
fileOperationsViewModel.performOperation(FileOperation.SynchronizeFileOperation(singleFile, singleFile.owner))
}
Expand Down Expand Up @@ -1379,7 +1391,13 @@ class MainFileListFragment : Fragment(),
private fun syncFiles(files: List<OCFile>) {
for (file in files) {
if (file.isFolder) {
fileOperationsViewModel.performOperation(FileOperation.SynchronizeFolderOperation(folderToSync = file, accountName = file.owner))
fileOperationsViewModel.performOperation(
FileOperation.SynchronizeFolderOperation(
folderToSync = file,
accountName = file.owner,
isActionSetFolderAvailableOfflineOrSynchronize = true,
)
)
} else {
fileOperationsViewModel.performOperation(FileOperation.SynchronizeFileOperation(fileToSync = file, accountName = file.owner))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
*
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2023 ownCloud GmbH.
* Copyright (C) 2024 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,
Expand Down Expand Up @@ -44,7 +45,11 @@ sealed interface FileOperation {
data class RemoveOperation(val listOfFilesToRemove: List<OCFile>, 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 SynchronizeFolderOperation(val folderToSync: OCFile, val accountName: String) : FileOperation
data class SynchronizeFolderOperation(
val folderToSync: OCFile,
val accountName: String,
val isActionSetFolderAvailableOfflineOrSynchronize: Boolean = false,
) : FileOperation
data class RefreshFolderOperation(val folderToRefresh: OCFile, val shouldSyncContents: Boolean) : FileOperation
data class CreateFileWithAppProviderOperation(val accountName: String, val parentContainerId: String, val filename: String) : FileOperation
data class SetFilesAsAvailableOffline(val filesToUpdate: List<OCFile>) : FileOperation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
*
* @author Abel García de Prada
* @author Juan Carlos Garrote Gascón
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2023 ownCloud GmbH.
* Copyright (C) 2024 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,
Expand Down Expand Up @@ -36,7 +37,7 @@ import com.owncloud.android.domain.exceptions.NoNetworkConnectionException
import com.owncloud.android.domain.files.model.OCFile
import com.owncloud.android.domain.files.usecases.CopyFileUseCase
import com.owncloud.android.domain.files.usecases.CreateFolderAsyncUseCase
import com.owncloud.android.domain.files.usecases.IsAnyFileAvailableLocallyUseCase
import com.owncloud.android.domain.files.usecases.IsAnyFileAvailableLocallyAndNotAvailableOfflineUseCase
import com.owncloud.android.domain.files.usecases.ManageDeepLinkUseCase
import com.owncloud.android.domain.files.usecases.MoveFileUseCase
import com.owncloud.android.domain.files.usecases.RemoveFileUseCase
Expand Down Expand Up @@ -71,7 +72,7 @@ class FileOperationsViewModel(
private val unsetFilesAsAvailableOfflineUseCase: UnsetFilesAsAvailableOfflineUseCase,
private val manageDeepLinkUseCase: ManageDeepLinkUseCase,
private val setLastUsageFileUseCase: SetLastUsageFileUseCase,
private val isAnyFileAvailableLocallyUseCase: IsAnyFileAvailableLocallyUseCase,
private val isAnyFileAvailableLocallyAndNotAvailableOfflineUseCase: IsAnyFileAvailableLocallyAndNotAvailableOfflineUseCase,
private val contextProvider: ContextProvider,
private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider,
) : ViewModel() {
Expand Down Expand Up @@ -106,8 +107,8 @@ class FileOperationsViewModel(
private val _deepLinkFlow = MutableStateFlow<Event<UIResult<OCFile?>>?>(null)
val deepLinkFlow: StateFlow<Event<UIResult<OCFile?>>?> = _deepLinkFlow

private val _checkIfFileLocalSharedFlow = MutableSharedFlow<UIResult<Boolean>>()
val checkIfFileLocalSharedFlow: SharedFlow<UIResult<Boolean>> = _checkIfFileLocalSharedFlow
private val _checkIfFileIsLocalAndNotAvailableOfflineSharedFlow = MutableSharedFlow<UIResult<Boolean>>()
val checkIfFileIsLocalAndNotAvailableOfflineSharedFlow: SharedFlow<UIResult<Boolean>> = _checkIfFileIsLocalAndNotAvailableOfflineSharedFlow

val openDialogs = mutableListOf<FileAlreadyExistsDialog>()

Expand All @@ -134,9 +135,9 @@ class FileOperationsViewModel(
runUseCaseWithResult(
coroutineDispatcher = coroutinesDispatcherProvider.io,
showLoading = true,
sharedFlow = _checkIfFileLocalSharedFlow,
useCase = isAnyFileAvailableLocallyUseCase,
useCaseParams = IsAnyFileAvailableLocallyUseCase.Params(filesToRemove),
sharedFlow = _checkIfFileIsLocalAndNotAvailableOfflineSharedFlow,
useCase = isAnyFileAvailableLocallyAndNotAvailableOfflineUseCase,
useCaseParams = IsAnyFileAvailableLocallyAndNotAvailableOfflineUseCase.Params(filesToRemove),
requiresConnection = false
)
}
Expand Down Expand Up @@ -258,7 +259,8 @@ class FileOperationsViewModel(
remotePath = fileOperation.folderToSync.remotePath,
accountName = fileOperation.folderToSync.owner,
spaceId = fileOperation.folderToSync.spaceId,
syncMode = SynchronizeFolderUseCase.SyncFolderMode.SYNC_FOLDER_RECURSIVELY
syncMode = SynchronizeFolderUseCase.SyncFolderMode.SYNC_FOLDER_RECURSIVELY,
isActionSetFolderAvailableOfflineOrSynchronize = fileOperation.isActionSetFolderAvailableOfflineOrSynchronize,
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
*
* @author David A. Velasco
* @author Abel García de Prada
* Copyright (C) 2021 ownCloud GmbH.
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2024 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,
Expand Down Expand Up @@ -77,17 +79,13 @@ class RemoveFilesDialogFragment : ConfirmationDialogFragment(), ConfirmationDial
* @return Dialog ready to show.
*/
@JvmStatic
fun newInstance(files: ArrayList<OCFile>, isAvailableLocally: Boolean): RemoveFilesDialogFragment {
fun newInstance(files: ArrayList<OCFile>, isAvailableLocallyAndNotAvailableOffline: Boolean): RemoveFilesDialogFragment {
val messageStringId: Int
var containsFolder = false
var containsAvailableOffline = false
for (file in files) {
if (file.isFolder) {
containsFolder = true
}
if (file.isAvailableOffline) {
containsAvailableOffline = true
}
}

messageStringId = if (files.size == 1) {
Expand All @@ -106,7 +104,7 @@ class RemoveFilesDialogFragment : ConfirmationDialogFragment(), ConfirmationDial
R.string.confirmation_remove_files_alert
}
}
val localRemoveButton = if (!containsAvailableOffline && isAvailableLocally) {
val localRemoveButton = if (isAvailableLocallyAndNotAvailableOffline) {
R.string.confirmation_remove_local
} else {
-1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ class ReleaseNotesViewModel(
subtitle = R.string.release_notes_4_3_0_subtitle_show_app_provider_icon_from_endpoint,
type = ReleaseNoteType.ENHANCEMENT
),
ReleaseNote(
title = R.string.release_notes_4_3_0_title_av_offline_files_removed_locally_with_local_only_option,
subtitle = R.string.release_notes_4_3_0_subtitle_av_offline_files_removed_locally_with_local_only_option,
type = ReleaseNoteType.BUGFIX
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
* @author Bartek Przybylski
* @author David A. Velasco
* @author David González Verdugo
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2011 Bartek Przybylski
* Copyright (C) 2020 ownCloud GmbH.
* Copyright (C) 2024 ownCloud GmbH.
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -231,7 +233,8 @@ private void synchronizeFolder(OCFile folder) {
folder.getRemotePath(),
folder.getOwner(),
folder.getSpaceId(),
SynchronizeFolderUseCase.SyncFolderMode.REFRESH_FOLDER_RECURSIVELY);
SynchronizeFolderUseCase.SyncFolderMode.REFRESH_FOLDER_RECURSIVELY,
false);
UseCaseResult<Unit> useCaseResult;

useCaseResult = synchronizeFolderUseCase.getValue().invoke(params);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
* @author David González Verdugo
* @author Shashvat Kedia
* @author David Crespo Rios
* Copyright (C) 2022 ownCloud GmbH.
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2024 ownCloud GmbH.
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -196,7 +198,8 @@ public void syncFile(OCFile file) {
file.getRemotePath(),
file.getOwner(),
file.getSpaceId(),
SynchronizeFolderUseCase.SyncFolderMode.SYNC_FOLDER_RECURSIVELY)
SynchronizeFolderUseCase.SyncFolderMode.SYNC_FOLDER_RECURSIVELY,
false)
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* ownCloud Android client application
*
* @author Abel García de Prada
* @author Aitor Ballesteros Pavón
*
* Copyright (C) 2022 ownCloud GmbH.
* Copyright (C) 2024 ownCloud GmbH.
* <p>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
Expand Down Expand Up @@ -39,7 +40,8 @@ class SynchronizeFolderUseCase(
val folderContent = fileRepository.refreshFolder(
remotePath = remotePath,
accountName = accountName,
spaceId = params.spaceId
spaceId = params.spaceId,
isActionSetFolderAvailableOfflineOrSynchronize = params.isActionSetFolderAvailableOfflineOrSynchronize,
)

folderContent.forEach { ocFile ->
Expand All @@ -51,6 +53,7 @@ class SynchronizeFolderUseCase(
accountName = accountName,
spaceId = ocFile.spaceId,
syncMode = params.syncMode,
isActionSetFolderAvailableOfflineOrSynchronize = params.isActionSetFolderAvailableOfflineOrSynchronize,
)
)
}
Expand All @@ -75,6 +78,7 @@ class SynchronizeFolderUseCase(
val accountName: String,
val spaceId: String? = null,
val syncMode: SyncFolderMode,
val isActionSetFolderAvailableOfflineOrSynchronize: Boolean = false,
)

/**
Expand Down
2 changes: 2 additions & 0 deletions owncloudApp/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,8 @@
<string name="release_notes_4_3_0_subtitle_accessibility_improvements">Some improvements to make the application more accessible</string>
<string name="release_notes_4_3_0_title_show_app_provider_icon_from_endpoint">New icons in "Open in (web)" option on the operations menu</string>
<string name="release_notes_4_3_0_subtitle_show_app_provider_icon_from_endpoint">More appropriate icons have been added to the "Open in (web)" option on the operations menu</string>
<string name="release_notes_4_3_0_title_av_offline_files_removed_locally_with_local_only_option">Displayed \"Local only\" option properly and prevented deletion of available offline files in the remove dialog</string>
<string name="release_notes_4_3_0_subtitle_av_offline_files_removed_locally_with_local_only_option">\"Local only\" option in remove dialog will be shown when the selected folder contains at least one downloaded file, ignoring those available offline. If the "Local only" option is displayed and clicked, available offline files will not be deleted</string>

<!-- Open in web -->
<string name="ic_action_open_in_web">Open in web</string>
Expand Down
Loading

0 comments on commit 8af346d

Please sign in to comment.