|
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.model.ShareeKey |
11 | 12 | import com.nextcloud.client.database.entity.toOCCapability |
12 | 13 | import com.owncloud.android.datamodel.FileDataStorageManager |
13 | 14 | import com.owncloud.android.datamodel.OCFile |
| 15 | +import com.owncloud.android.lib.common.utils.Log_OC |
14 | 16 | import com.owncloud.android.lib.resources.files.model.RemoteFile |
15 | 17 | import com.owncloud.android.lib.resources.shares.OCShare |
16 | 18 | import com.owncloud.android.lib.resources.status.OCCapability |
| 19 | +import com.owncloud.android.utils.FileStorageUtils |
| 20 | +import com.owncloud.android.utils.MimeTypeUtil |
17 | 21 | import kotlinx.coroutines.Dispatchers |
18 | 22 | import kotlinx.coroutines.withContext |
| 23 | +import java.io.File |
19 | 24 |
|
20 | 25 | private const val SHARE_PATH_QUERY_CHUNK_SIZE = 400 |
21 | 26 |
|
@@ -116,3 +121,132 @@ fun FileDataStorageManager.getNonEncryptedSubfolders(id: Long, accountName: Stri |
116 | 121 |
|
117 | 122 | suspend fun FileDataStorageManager.getCapabilitiesByAccountName(accountName: String): OCCapability = |
118 | 123 | capabilityDao.getByAccountName(accountName).toOCCapability() |
| 124 | + |
| 125 | +@Suppress("ReturnCount") |
| 126 | +fun FileDataStorageManager.moveFiles(ocFile: OCFile?, targetPath: String, targetParentPath: String) { |
| 127 | + Log_OC.d( |
| 128 | + FileDataStorageManager.TAG, |
| 129 | + ( |
| 130 | + "moveLocalFile ==> ocFile: " + |
| 131 | + (ocFile?.remotePath) + |
| 132 | + " targetPath: " + |
| 133 | + targetPath + |
| 134 | + " targetParentPath: " + |
| 135 | + targetParentPath |
| 136 | + ) |
| 137 | + ) |
| 138 | + |
| 139 | + if (ocFile == null) { |
| 140 | + Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: file is null, skipping") |
| 141 | + return |
| 142 | + } |
| 143 | + |
| 144 | + if (!ocFile.fileExists()) { |
| 145 | + Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: file does not exist, skipping") |
| 146 | + return |
| 147 | + } |
| 148 | + |
| 149 | + if (OCFile.ROOT_PATH == ocFile.fileName) { |
| 150 | + Log_OC.w(FileDataStorageManager.TAG, "moveLocalFile: cannot move root path") |
| 151 | + return |
| 152 | + } |
| 153 | + |
| 154 | + if (ocFile.remotePath == targetPath) { |
| 155 | + Log_OC.w(FileDataStorageManager.TAG, "moveLocalFile: source and target paths are identical, skipping") |
| 156 | + return |
| 157 | + } |
| 158 | + |
| 159 | + val targetParent = getFileByPath(targetParentPath) |
| 160 | + if (targetParent == null) { |
| 161 | + Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: target parent folder not found: $targetParentPath") |
| 162 | + return |
| 163 | + } |
| 164 | + |
| 165 | + if (!targetParent.isFolder) { |
| 166 | + Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: target parent is not a folder: $targetParentPath") |
| 167 | + return |
| 168 | + } |
| 169 | + |
| 170 | + val oldPath: String = ocFile.remotePath |
| 171 | + val accountName = user.accountName |
| 172 | + val defaultSavePath = FileStorageUtils.getSavePath(accountName) |
| 173 | + |
| 174 | + val moved = moveLocalFiles(accountName, ocFile, defaultSavePath, targetPath) |
| 175 | + if (!moved) return |
| 176 | + |
| 177 | + val originalMediaPaths = |
| 178 | + fileDao.moveFilesInDb(oldPath, targetPath, defaultSavePath, targetParent.fileId, accountName) |
| 179 | + |
| 180 | + for (originalMediaPath in originalMediaPaths) { |
| 181 | + deleteFileInMediaScan(originalMediaPath) |
| 182 | + val newMediaPath = defaultSavePath + targetPath + originalMediaPath.substring( |
| 183 | + (defaultSavePath + oldPath).length |
| 184 | + ) |
| 185 | + FileDataStorageManager.triggerMediaScan(newMediaPath) |
| 186 | + } |
| 187 | +} |
| 188 | + |
| 189 | +@Suppress("ReturnCount") |
| 190 | +private fun moveLocalFiles(accountName: String, ocFile: OCFile, defaultSavePath: String, targetPath: String): Boolean { |
| 191 | + val localFile = File(FileStorageUtils.getDefaultSavePathFor(accountName, ocFile)) |
| 192 | + if (!localFile.exists()) { |
| 193 | + Log_OC.d(FileDataStorageManager.TAG, "moveLocalFile: no local file to move at " + localFile.absolutePath) |
| 194 | + return false |
| 195 | + } |
| 196 | + |
| 197 | + val targetFile = File(defaultSavePath + targetPath) |
| 198 | + val targetFolder = targetFile.getParentFile() |
| 199 | + if (targetFolder != null && !targetFolder.exists() && !targetFolder.mkdirs()) { |
| 200 | + Log_OC.e( |
| 201 | + FileDataStorageManager.TAG, |
| 202 | + "moveLocalFile: failed to create parent folder " + targetFolder.absolutePath |
| 203 | + ) |
| 204 | + } |
| 205 | + |
| 206 | + if (!localFile.renameTo(targetFile)) { |
| 207 | + Log_OC.e( |
| 208 | + FileDataStorageManager.TAG, |
| 209 | + ( |
| 210 | + "moveLocalFile: failed to rename " + localFile.absolutePath + |
| 211 | + " to " + targetFile.absolutePath |
| 212 | + ) |
| 213 | + ) |
| 214 | + return false |
| 215 | + } |
| 216 | + |
| 217 | + return true |
| 218 | +} |
| 219 | + |
| 220 | +private fun FileDao.moveFilesInDb( |
| 221 | + oldPath: String, |
| 222 | + targetPath: String, |
| 223 | + defaultSavePath: String, |
| 224 | + targetParentId: Long, |
| 225 | + accountName: String |
| 226 | +): List<String> { |
| 227 | + val entities = getFolderWithDescendants("$oldPath%", accountName) |
| 228 | + val oldStoragePrefix = defaultSavePath + oldPath |
| 229 | + val newStoragePrefix = defaultSavePath + targetPath |
| 230 | + |
| 231 | + val originalMediaPaths = entities |
| 232 | + .filter { MimeTypeUtil.isMedia(it.contentType) && it.storagePath?.startsWith(oldStoragePrefix) == true } |
| 233 | + .mapNotNull { it.storagePath } |
| 234 | + |
| 235 | + val updated = entities.map { entity -> |
| 236 | + val currentPath = entity.path.orEmpty() |
| 237 | + val newPath = targetPath + currentPath.substring(oldPath.length) |
| 238 | + entity.copy( |
| 239 | + path = newPath, |
| 240 | + pathDecrypted = if (entity.isEncrypted == 0) newPath else entity.pathDecrypted, |
| 241 | + storagePath = if (entity.storagePath?.startsWith(oldStoragePrefix) == true) { |
| 242 | + newStoragePrefix + entity.storagePath.substring(oldStoragePrefix.length) |
| 243 | + } else { |
| 244 | + entity.storagePath |
| 245 | + }, |
| 246 | + parent = if (currentPath == oldPath) targetParentId else entity.parent |
| 247 | + ) |
| 248 | + } |
| 249 | + |
| 250 | + updateAll(updated) |
| 251 | + return originalMediaPaths |
| 252 | +} |
0 commit comments