Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Av. offline files removed locally with the Local only option #4399

Merged
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
isActionSetFolderAvailableOffline = 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,
isActionSetFolderAvailableOffline = true,
)
JuancaG05 marked this conversation as resolved.
Show resolved Hide resolved
)
} else {
fileOperationsViewModel.performOperation(FileOperation.SynchronizeFileOperation(singleFile, singleFile.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,7 @@ 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 isActionSetFolderAvailableOffline: 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 @@ -34,9 +35,9 @@ import com.owncloud.android.domain.availableoffline.usecases.SetFilesAsAvailable
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.IsAnyFileAvailableLocallyAndNotAvailableOfflineUseCase
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.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,
isActionSetFolderAvailableOffline = fileOperation.isActionSetFolderAvailableOffline,
)
)
}
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) {
JuancaG05 marked this conversation as resolved.
Show resolved Hide resolved
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
JuancaG05 marked this conversation as resolved.
Show resolved Hide resolved
* 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,
isActionSetFolderAvailableOffline = params.isActionSetFolderAvailableOffline,
)

folderContent.forEach { ocFile ->
Expand All @@ -51,6 +53,7 @@ class SynchronizeFolderUseCase(
accountName = accountName,
spaceId = ocFile.spaceId,
syncMode = params.syncMode,
isActionSetFolderAvailableOffline = params.isActionSetFolderAvailableOffline,
)
)
}
Expand All @@ -75,6 +78,7 @@ class SynchronizeFolderUseCase(
val accountName: String,
val spaceId: String? = null,
val syncMode: SyncFolderMode,
val isActionSetFolderAvailableOffline: 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
Loading