Skip to content

Commit

Permalink
Merge pull request #1311 from Infomaniak/imp/unified-file-duplication
Browse files Browse the repository at this point in the history
Can now select destination when duplicating a single file
  • Loading branch information
TommyDL-Infomaniak authored May 23, 2024
2 parents c84e67e + 306d32e commit 8e11fd2
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 89 deletions.
27 changes: 20 additions & 7 deletions app/src/androidTest/java/com/infomaniak/drive/ApiRepositoryTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -370,21 +370,18 @@ class ApiRepositoryTest : KDriveTest() {
}

@Test
@DisplayName("Copy the test file to root folder")
@DisplayName("Duplicate the test file to root folder")
fun duplicateFile() {
val copyName = "testCopy-$randomSuffix"
val copyFile = duplicateFile(testFile, copyName).let {
val copyFile = duplicateFile(testFile, testFile.parentId).let {
assertApiResponseData(it)
assertEquals(copyName, it.data?.name, "The copy name should be equal to $copyName")
assertNotEquals(testFile.id, it.data?.id, "The id should be different from the original file")
assertEquals(testFile.driveColor, it.data?.driveColor)
it.data!!
}

// Duplicate one more time with same name and location
with(duplicateFile(testFile, copyName)) {
// Duplicate one more time with location
with(duplicateFile(testFile, testFile.parentId)) {
assertApiResponseData(this)
assertEquals("$copyName (1)", data?.name, "The copy name should be equal to $copyName (1)")
deleteTestFile(data!!)
}

Expand Down Expand Up @@ -568,6 +565,22 @@ class ApiRepositoryTest : KDriveTest() {
}
}

@Test
@DisplayName("Duplicate the test file to another folder")
fun duplicateFileToAnotherFolder() {
val file = createFileForTest()

val copyFile = duplicateFile(file, file.parentId).let {
assertApiResponseData(it)
assertNotEquals(file.id, it.data?.id, "The id should be different from the original file")
assertEquals(file.driveColor, it.data?.driveColor)
it.data!!
}

// Delete the copy
deleteTestFile(copyFile)
}

@Test
@DisplayName("Create a folder then convert it to dropbox")
fun createDropboxFromFolder() {
Expand Down
10 changes: 2 additions & 8 deletions app/src/main/java/com/infomaniak/drive/data/api/ApiRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,8 @@ object ApiRepository : ApiRepositoryCore() {
return callApi(ApiRoutes.updateFolderColor(file), POST, mapOf("color" to color))
}

fun copyFile(file: File, copyName: String?, destinationId: Int): ApiResponse<File> {
val body = if (copyName == null) mapOf() else mapOf("name" to copyName)
return callApi(ApiRoutes.copyFile(file, destinationId), POST, body)
}

fun duplicateFile(file: File, duplicateName: String?): ApiResponse<File> {
val body = if (duplicateName == null) mapOf() else mapOf("name" to duplicateName)
return callApi(ApiRoutes.duplicateFile(file), POST, body)
fun duplicateFile(file: File, destinationId: Int): ApiResponse<File> {
return callApi(ApiRoutes.duplicateFile(file, destinationId), POST)
}

fun moveFile(file: File, newParent: File): ApiResponse<CancellableAction> {
Expand Down
4 changes: 1 addition & 3 deletions app/src/main/java/com/infomaniak/drive/data/api/ApiRoutes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,7 @@ object ApiRoutes {

fun moveFile(file: File, newParentId: Int) = "${fileURL(file)}/move/$newParentId"

fun duplicateFile(file: File) = "${fileURL(file)}/duplicate?$fileWithQuery"

fun copyFile(file: File, destinationId: Int) = "${fileURL(file)}/copy/$destinationId?$fileWithQuery"
fun duplicateFile(file: File, destinationId: Int) = "${fileURL(file)}/copy/$destinationId?$fileWithQuery"

fun renameFile(file: File) = "${fileURL(file)}/rename"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,10 +411,9 @@ class CloudStorageProvider : DocumentsProvider() {
val fileId = getFileIdFromDocumentId(sourceDocumentId)
val file =
FileController.getFileProxyById(fileId, customRealm = realm) ?: throw IllegalStateException("File not found")
val copyName = file.name
val targetParentFileId = getFileIdFromDocumentId(targetParentDocumentId)

val apiResponse = ApiRepository.copyFile(file, copyName, targetParentFileId)
val apiResponse = ApiRepository.duplicateFile(file, targetParentFileId)

if (apiResponse.isSuccess()) {

Expand Down
16 changes: 2 additions & 14 deletions app/src/main/java/com/infomaniak/drive/ui/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -334,24 +334,12 @@ class MainViewModel(
}
}

fun copyFile(
file: File,
destinationId: Int? = null,
copyName: String?,
onSuccess: ((apiResponse: ApiResponse<File>) -> Unit)? = null,
) = liveData(Dispatchers.IO) {
ApiRepository.copyFile(file, copyName, destinationId ?: Utils.ROOT_ID).let { apiResponse ->
if (apiResponse.isSuccess()) onSuccess?.invoke(apiResponse)
emit(apiResponse)
}
}

fun duplicateFile(
file: File,
copyName: String?,
destinationId: Int? = null,
onSuccess: ((apiResponse: ApiResponse<File>) -> Unit)? = null,
) = liveData(Dispatchers.IO) {
ApiRepository.duplicateFile(file, copyName).let { apiResponse ->
ApiRepository.duplicateFile(file, destinationId ?: Utils.ROOT_ID).let { apiResponse ->
if (apiResponse.isSuccess()) onSuccess?.invoke(apiResponse)
emit(apiResponse)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,21 +259,16 @@ class FileInfoActionsBottomSheetDialog : BottomSheetDialogFragment(), FileInfoAc
mainViewModel.updateOfflineFile.value = currentFile.id
}

override fun onDuplicateFile(result: String, onApiResponse: () -> Unit) {
if (isResumed) {
mainViewModel.duplicateFile(currentFile, result).observe(viewLifecycleOwner) { apiResponse ->
if (apiResponse.isSuccess()) {
apiResponse.data?.let {
mainViewModel.refreshActivities.value = true
transmitActionAndPopBack(getString(R.string.allFileDuplicate, currentFile.name))
}
} else {
transmitActionAndPopBack(getString(R.string.errorDuplicate))
}
onApiResponse()
override fun onDuplicateFile(destinationFolder: File) {
mainViewModel.duplicateFile(currentFile, destinationFolder.id).observe(viewLifecycleOwner) { apiResponse ->
val snackbarMessage = if (apiResponse.isSuccess()) {
mainViewModel.refreshActivities.value = true
getString(R.string.allFileDuplicate, currentFile.name)
} else {
getString(R.string.errorDuplicate)
}
} else {
onApiResponse()

transmitActionAndPopBack(snackbarMessage)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import com.infomaniak.drive.ui.fileList.multiSelect.MultiSelectManager.MultiSele
import com.infomaniak.drive.utils.*
import com.infomaniak.drive.utils.BulkOperationsUtils.launchBulkOperationWorker
import com.infomaniak.drive.utils.NotificationUtils.buildGeneralNotification
import com.infomaniak.drive.utils.Utils.duplicateFilesClicked
import com.infomaniak.drive.utils.Utils.moveFileClicked
import com.infomaniak.lib.core.utils.ApiErrorCode.Companion.translateError
import com.infomaniak.lib.core.utils.capitalizeFirstChar
Expand Down Expand Up @@ -185,17 +186,7 @@ abstract class MultiSelectFragment(private val matomoCategory: String) : Fragmen
}

fun duplicateFiles() {
Intent(requireContext(), SelectFolderActivity::class.java).apply {
putExtras(
SelectFolderActivityArgs(
userId = AccountUtils.currentUserId,
driveId = AccountUtils.currentDriveId,
folderId = mainViewModel.currentFolder.value?.id ?: -1,
customArgs = bundleOf(BULK_OPERATION_CUSTOM_TAG to BulkOperationType.COPY)
).toBundle()
)
selectFolderResultLauncher.launch(this)
}
requireContext().duplicateFilesClicked(selectFolderResultLauncher, mainViewModel)
}

fun restoreIn() {
Expand Down Expand Up @@ -350,10 +341,9 @@ abstract class MultiSelectFragment(private val matomoCategory: String) : Fragmen
}
BulkOperationType.COPY -> {
mediator.addSource(
copyFile(
duplicateFile(
file = file,
destinationId = destinationFolder!!.id,
copyName = file.name,
onSuccess = { it.data?.let { file -> onIndividualActionSuccess(type, file) } },
),
updateMultiSelectMediator(mediator),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ class PreviewPDFActivity : AppCompatActivity(), OnItemClickListener {
override fun manageCategoriesClicked(fileId: Int) = Unit
override fun onCacheAddedToOffline() = Unit
override fun onDeleteFile(onApiResponse: () -> Unit) = Unit
override fun onDuplicateFile(result: String, onApiResponse: () -> Unit) = Unit
override fun onDuplicateFile(destinationFolder: File) = Unit
override fun onLeaveShare(onApiResponse: () -> Unit) = Unit
override fun onMoveFile(destinationFolder: File) = Unit
override fun onRenameFile(newName: String, onApiResponse: () -> Unit) = Unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,20 +398,22 @@ class PreviewSliderFragment : Fragment(), FileInfoActionsView.OnItemClickListene
)
}

override fun onDuplicateFile(result: String, onApiResponse: () -> Unit) {
mainViewModel.duplicateFile(currentFile, result).observe(viewLifecycleOwner) { apiResponse ->
override fun onDuplicateFile(destinationFolder: File) {
mainViewModel.duplicateFile(currentFile, destinationFolder.id).observe(viewLifecycleOwner) { apiResponse ->
if (apiResponse.isSuccess()) {
apiResponse.data?.let { file ->
mainViewModel.currentPreviewFileList[file.id] = file
previewSliderAdapter.addFile(file)
if (currentFile.parentId == destinationFolder.id) {
mainViewModel.currentPreviewFileList[file.id] = file
previewSliderAdapter.addFile(file)
}

showSnackbar(getString(R.string.allFileDuplicate, currentFile.name))
toggleBottomSheet(shouldShow = true)
}
} else {
showSnackbar(R.string.errorDuplicate)
toggleBottomSheet(shouldShow = true)
}
onApiResponse()
}
}

Expand Down
22 changes: 22 additions & 0 deletions app/src/main/java/com/infomaniak/drive/utils/SingleOperation.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2024 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.drive.utils

enum class SingleOperation {
COPY, MOVE
}
32 changes: 27 additions & 5 deletions app/src/main/java/com/infomaniak/drive/utils/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import com.infomaniak.drive.ui.fileList.multiSelect.MultiSelectFragment
import com.infomaniak.drive.ui.fileList.preview.PreviewPDFActivity
import com.infomaniak.drive.ui.fileList.preview.PreviewSliderFragmentArgs
import com.infomaniak.drive.utils.SyncUtils.uploadFolder
import com.infomaniak.drive.views.FileInfoActionsView.Companion.SINGLE_OPERATION_CUSTOM_TAG
import com.infomaniak.lib.core.utils.DownloadManagerUtils
import com.infomaniak.lib.core.utils.showKeyboard
import com.infomaniak.lib.core.utils.showToast
Expand Down Expand Up @@ -222,7 +223,7 @@ object Utils {
fun Context.moveFileClicked(
disabledFolderId: Int?,
selectFolderResultLauncher: ActivityResultLauncher<Intent>,
mainViewModel: MainViewModel
mainViewModel: MainViewModel,
) {
mainViewModel.ignoreSyncOffline = true
Intent(this, SelectFolderActivity::class.java).apply {
Expand All @@ -232,11 +233,32 @@ object Utils {
driveId = AccountUtils.currentDriveId,
folderId = disabledFolderId ?: -1,
disabledFolderId = disabledFolderId ?: -1,
customArgs = bundleOf(MultiSelectFragment.BULK_OPERATION_CUSTOM_TAG to BulkOperationType.MOVE)
).toBundle()
customArgs = bundleOf(
MultiSelectFragment.BULK_OPERATION_CUSTOM_TAG to BulkOperationType.MOVE,
SINGLE_OPERATION_CUSTOM_TAG to SingleOperation.MOVE.name,
),
).toBundle(),
)
selectFolderResultLauncher.launch(this)
}
}.also(selectFolderResultLauncher::launch)
}

fun Context.duplicateFilesClicked(
selectFolderResultLauncher: ActivityResultLauncher<Intent>,
mainViewModel: MainViewModel,
) {
Intent(this, SelectFolderActivity::class.java).apply {
putExtras(
SelectFolderActivityArgs(
userId = AccountUtils.currentUserId,
driveId = AccountUtils.currentDriveId,
folderId = mainViewModel.currentFolder.value?.id ?: -1,
customArgs = bundleOf(
MultiSelectFragment.BULK_OPERATION_CUSTOM_TAG to BulkOperationType.COPY,
SINGLE_OPERATION_CUSTOM_TAG to SingleOperation.COPY.name,
),
).toBundle(),
)
}.also(selectFolderResultLauncher::launch)
}

fun Context.openWith(uri: Uri, type: String?, flags: Int) {
Expand Down
34 changes: 17 additions & 17 deletions app/src/main/java/com/infomaniak/drive/views/FileInfoActionsView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import com.infomaniak.drive.databinding.ViewFileInfoActionsBinding
import com.infomaniak.drive.ui.MainViewModel
import com.infomaniak.drive.ui.fileList.SelectFolderActivityArgs
import com.infomaniak.drive.utils.*
import com.infomaniak.drive.utils.Utils.duplicateFilesClicked
import com.infomaniak.drive.utils.Utils.moveFileClicked
import com.infomaniak.lib.core.utils.ApiErrorCode.Companion.translateError
import com.infomaniak.lib.core.utils.DownloadManagerUtils
Expand Down Expand Up @@ -239,7 +240,7 @@ class FileInfoActionsView @JvmOverloads constructor(
moveFile.setOnClickListener {
onItemClickListener.moveFileClicked(currentFile.parentId, selectFolderResultLauncher, mainViewModel)
}
duplicateFile.setOnClickListener { onItemClickListener.duplicateFileClicked() }
duplicateFile.setOnClickListener { onItemClickListener.duplicateFileClicked(selectFolderResultLauncher, mainViewModel) }
renameFile.setOnClickListener { onItemClickListener.renameFileClicked() }
deleteFile.setOnClickListener { onItemClickListener.deleteFileClicked() }
goToFolder.setOnClickListener { onItemClickListener.goToFolder() }
Expand Down Expand Up @@ -486,8 +487,8 @@ class FileInfoActionsView @JvmOverloads constructor(
fun manageCategoriesClicked(fileId: Int)
fun onCacheAddedToOffline()
fun onDeleteFile(onApiResponse: () -> Unit)
fun onDuplicateFile(result: String, onApiResponse: () -> Unit)
fun onLeaveShare(onApiResponse: () -> Unit)
fun onDuplicateFile(destinationFolder: File)
fun onMoveFile(destinationFolder: File)
fun onRenameFile(newName: String, onApiResponse: () -> Unit)
fun removeOfflineFile(offlineLocalPath: IOFile, cacheFile: IOFile)
Expand All @@ -508,7 +509,12 @@ class FileInfoActionsView @JvmOverloads constructor(
fun onSelectFolderResult(data: Intent?) {
data?.extras?.let { bundle ->
SelectFolderActivityArgs.fromBundle(bundle).apply {
onMoveFile(File(id = folderId, name = folderName, driveId = AccountUtils.currentDriveId))
val file = File(id = folderId, name = folderName, driveId = AccountUtils.currentDriveId)
when (customArgs?.getString(SINGLE_OPERATION_CUSTOM_TAG)) {
SingleOperation.COPY.name -> onDuplicateFile(file)
SingleOperation.MOVE.name -> onMoveFile(file)
else -> Unit
}
}
}
}
Expand Down Expand Up @@ -546,20 +552,10 @@ class FileInfoActionsView @JvmOverloads constructor(
}

@CallSuper
fun duplicateFileClicked() = currentFile?.apply {
Utils.createPromptNameDialog(
context = currentContext,
title = R.string.buttonDuplicate,
fieldName = R.string.fileInfoInputDuplicateFile,
positiveButton = R.string.buttonCopy,
fieldValue = name,
selectedRange = getFileName().length
) { dialog, name ->
onDuplicateFile(name) {
trackFileActionEvent("copy")
dialog.dismiss()
}
}
fun duplicateFileClicked(selectFolderResultLauncher: ActivityResultLauncher<Intent>, mainViewModel: MainViewModel) {
trackFileActionEvent("copy")
mainViewModel.ignoreSyncOffline = true
currentContext.duplicateFilesClicked(selectFolderResultLauncher, mainViewModel)
}

@CallSuper
Expand Down Expand Up @@ -604,4 +600,8 @@ class FileInfoActionsView @JvmOverloads constructor(
}
}
}

companion object {
const val SINGLE_OPERATION_CUSTOM_TAG = "single_operation"
}
}

0 comments on commit 8e11fd2

Please sign in to comment.