|
7 | 7 |
|
8 | 8 | package com.nextcloud.utils.extensions |
9 | 9 |
|
| 10 | +import com.nextcloud.client.database.dao.FileDao |
10 | 11 | import com.nextcloud.client.database.entity.toOCCapability |
11 | 12 | import com.owncloud.android.datamodel.FileDataStorageManager |
12 | 13 | import com.owncloud.android.datamodel.OCFile |
| 14 | +import com.owncloud.android.lib.common.utils.Log_OC |
13 | 15 | import com.owncloud.android.lib.resources.shares.OCShare |
14 | 16 | import com.owncloud.android.lib.resources.status.OCCapability |
| 17 | +import com.owncloud.android.utils.FileStorageUtils |
| 18 | +import com.owncloud.android.utils.MimeTypeUtil |
15 | 19 | import kotlinx.coroutines.Dispatchers |
16 | 20 | import kotlinx.coroutines.withContext |
| 21 | +import java.io.File |
17 | 22 |
|
18 | 23 | suspend fun FileDataStorageManager.saveShares(shares: List<OCShare>, accountName: String) { |
19 | 24 | withContext(Dispatchers.IO) { |
@@ -58,3 +63,119 @@ fun FileDataStorageManager.getNonEncryptedSubfolders(id: Long, accountName: Stri |
58 | 63 |
|
59 | 64 | suspend fun FileDataStorageManager.getCapabilitiesByAccountName(accountName: String): OCCapability = |
60 | 65 | capabilityDao.getByAccountName(accountName).toOCCapability() |
| 66 | + |
| 67 | +fun FileDataStorageManager.moveLocalFile(ocFile: OCFile?, targetPath: String, targetParentPath: String) { |
| 68 | + Log_OC.d( |
| 69 | + FileDataStorageManager.TAG, |
| 70 | + ("moveLocalFile ==> ocFile: " |
| 71 | + + (ocFile?.remotePath) |
| 72 | + + " targetPath: " |
| 73 | + + targetPath |
| 74 | + + " targetParentPath: " |
| 75 | + + targetParentPath) |
| 76 | + ) |
| 77 | + |
| 78 | + if (ocFile == null) { |
| 79 | + Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: file is null, skipping") |
| 80 | + return |
| 81 | + } |
| 82 | + |
| 83 | + if (!ocFile.fileExists()) { |
| 84 | + Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: file does not exist, skipping") |
| 85 | + return |
| 86 | + } |
| 87 | + |
| 88 | + if (OCFile.ROOT_PATH == ocFile.fileName) { |
| 89 | + Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: cannot move root path") |
| 90 | + return |
| 91 | + } |
| 92 | + |
| 93 | + if (ocFile.remotePath == targetPath) { |
| 94 | + Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: source and target paths are identical, skipping") |
| 95 | + return |
| 96 | + } |
| 97 | + |
| 98 | + val targetParent = getFileByPath(targetParentPath) |
| 99 | + if (targetParent == null) { |
| 100 | + Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: target parent folder not found: $targetParentPath") |
| 101 | + return |
| 102 | + } |
| 103 | + |
| 104 | + if (!targetParent.isFolder) { |
| 105 | + Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: target parent is not a folder: $targetParentPath") |
| 106 | + return |
| 107 | + } |
| 108 | + |
| 109 | + val oldPath: String = ocFile.remotePath |
| 110 | + val accountName = user.accountName |
| 111 | + val defaultSavePath = FileStorageUtils.getSavePath(accountName) |
| 112 | + |
| 113 | + val originalMediaPaths = |
| 114 | + fileDao.moveFileEntities(oldPath, targetPath, defaultSavePath, targetParent.getFileId(), accountName) |
| 115 | + |
| 116 | + val localFile = File(FileStorageUtils.getDefaultSavePathFor(accountName, ocFile)) |
| 117 | + if (!localFile.exists()) { |
| 118 | + Log_OC.d(FileDataStorageManager.TAG, "moveLocalFile: no local file to move at " + localFile.getAbsolutePath()) |
| 119 | + return |
| 120 | + } |
| 121 | + |
| 122 | + val targetFile = File(defaultSavePath + targetPath) |
| 123 | + val targetFolder = targetFile.getParentFile() |
| 124 | + if (targetFolder != null && !targetFolder.exists() && !targetFolder.mkdirs()) { |
| 125 | + Log_OC.e( |
| 126 | + FileDataStorageManager.TAG, |
| 127 | + "moveLocalFile: failed to create parent folder " + targetFolder.absolutePath |
| 128 | + ) |
| 129 | + } |
| 130 | + |
| 131 | + if (!localFile.renameTo(targetFile)) { |
| 132 | + Log_OC.e( |
| 133 | + FileDataStorageManager.TAG, ("moveLocalFile: failed to rename " + localFile.absolutePath |
| 134 | + + " to " + targetFile.absolutePath) |
| 135 | + ) |
| 136 | + return |
| 137 | + } |
| 138 | + |
| 139 | + for (originalMediaPath in originalMediaPaths) { |
| 140 | + deleteFileInMediaScan(originalMediaPath) |
| 141 | + val newMediaPath = defaultSavePath + targetPath + originalMediaPath.substring( |
| 142 | + (defaultSavePath + oldPath).length |
| 143 | + ) |
| 144 | + FileDataStorageManager.triggerMediaScan(newMediaPath) |
| 145 | + } |
| 146 | +} |
| 147 | + |
| 148 | + |
| 149 | +private fun FileDao.moveFileEntities( |
| 150 | + oldPath: String, |
| 151 | + targetPath: String, |
| 152 | + defaultSavePath: String, |
| 153 | + targetParentId: Long, |
| 154 | + accountName: String |
| 155 | +): List<String> { |
| 156 | + val entities = getFolderWithDescendants("$oldPath%", accountName) |
| 157 | + val oldStoragePrefix = defaultSavePath + oldPath |
| 158 | + val newStoragePrefix = defaultSavePath + targetPath |
| 159 | + |
| 160 | + val originalMediaPaths = entities |
| 161 | + .filter { MimeTypeUtil.isMedia(it.contentType) && it.storagePath?.startsWith(oldStoragePrefix) == true } |
| 162 | + .mapNotNull { it.storagePath } |
| 163 | + |
| 164 | + val updated = entities.map { entity -> |
| 165 | + val currentPath = entity.path.orEmpty() |
| 166 | + val newPath = targetPath + currentPath.substring(oldPath.length) |
| 167 | + entity.copy( |
| 168 | + path = newPath, |
| 169 | + pathDecrypted = if (entity.isEncrypted == 0) newPath else entity.pathDecrypted, |
| 170 | + storagePath = if (entity.storagePath?.startsWith(oldStoragePrefix) == true) { |
| 171 | + newStoragePrefix + entity.storagePath.substring(oldStoragePrefix.length) |
| 172 | + } else { |
| 173 | + entity.storagePath |
| 174 | + }, |
| 175 | + parent = if (currentPath == oldPath) targetParentId else entity.parent |
| 176 | + ) |
| 177 | + } |
| 178 | + |
| 179 | + updateAll(updated) |
| 180 | + return originalMediaPaths |
| 181 | +} |
0 commit comments