@@ -12,10 +12,13 @@ import com.nextcloud.client.database.entity.model.ShareeKey
1212import com.nextcloud.client.database.entity.toOCCapability
1313import com.owncloud.android.datamodel.FileDataStorageManager
1414import com.owncloud.android.datamodel.OCFile
15+ import com.owncloud.android.lib.common.OwnCloudClient
1516import com.owncloud.android.lib.common.utils.Log_OC
17+ import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation
1618import com.owncloud.android.lib.resources.files.model.RemoteFile
1719import com.owncloud.android.lib.resources.shares.OCShare
1820import com.owncloud.android.lib.resources.status.OCCapability
21+ import com.owncloud.android.operations.upload.RemoteFileExistence
1922import com.owncloud.android.utils.FileStorageUtils
2023import com.owncloud.android.utils.MimeTypeUtil
2124import kotlinx.coroutines.Dispatchers
@@ -185,6 +188,49 @@ fun FileDataStorageManager.moveFiles(ocFile: OCFile?, targetPath: String, target
185188 }
186189}
187190
191+ /* *
192+ * Finds a suitable file name to resolve a conflict.
193+ * Tries to concatenate a number to the name until it finds a non-existent one.
194+ * E.g. for "file.txt" it will propose "file (2).txt". If that exists, then "file (3).txt" and so on.
195+ * E.g. for "folder" it will propose "folder (2)/". If that exists, then "folder (3)/" and so on.
196+ *
197+ * @return the new remote path, or null if the user is unauthorized in the provided path
198+ */
199+ fun getRemotePathForConflictResolution (client : OwnCloudClient , remotePath : String , fileName : String ): String? {
200+ val newName = generateFileNameForConflictResolution(fileName)
201+ val newPath = " $remotePath$newName "
202+
203+ // Check if new name exists
204+ val operation = ExistenceCheckRemoteOperation (newPath, false )
205+ val existence = RemoteFileExistence .fromExistenceCheck(operation.execute(client))
206+ if (existence == RemoteFileExistence .UNAUTHORIZED )
207+ return null
208+ if (existence == RemoteFileExistence .DOES_NOT_EXIST )
209+ return newPath
210+ return getRemotePathForConflictResolution(client, remotePath, newName)
211+ }
212+
213+ fun generateFileNameForConflictResolution (fileName : String ): String {
214+ val isFolder = fileName.endsWith(OCFile .PATH_SEPARATOR )
215+ val separator = if (isFolder) OCFile .PATH_SEPARATOR else " ."
216+ var nameFirstPart = fileName.substringBeforeLast(separator)
217+ var nameLastPart = fileName.substringAfterLast(separator, " " ) // Extension or path separator
218+ if (nameLastPart.isNotEmpty()) nameLastPart = " $separator$nameLastPart "
219+ val regex = Regex (""" (.*)\((\d+)\)$""" , RegexOption .MULTILINE )
220+ if (regex.matches(nameFirstPart)) {
221+ // Already a resolved conflict (i.e. "file (1).txt"). Update the number.
222+ nameFirstPart = regex.replace(nameFirstPart, transform = { m ->
223+ val baseName = m.groups[1 ]?.value
224+ val number = m.groups[2 ]?.value?.toInt() ? : 0
225+ " $baseName (${number + 1 } )"
226+ })
227+ } else {
228+ // Add the number
229+ nameFirstPart = " $nameFirstPart (1)"
230+ }
231+ return " $nameFirstPart$nameLastPart "
232+ }
233+
188234@Suppress(" ReturnCount" )
189235private fun moveLocalFiles (accountName : String , ocFile : OCFile , defaultSavePath : String , targetPath : String ): Boolean {
190236 val localFile = File (FileStorageUtils .getDefaultSavePathFor(accountName, ocFile))
0 commit comments