Skip to content

performance: move local file#17082

Open
alperozturk96 wants to merge 5 commits into
masterfrom
performance/move-local-file
Open

performance: move local file#17082
alperozturk96 wants to merge 5 commits into
masterfrom
performance/move-local-file

Conversation

@alperozturk96
Copy link
Copy Markdown
Collaborator

@alperozturk96 alperozturk96 commented May 22, 2026

Issue

Renaming folder too slow

Changes

  • Improves performance
  • Adds target and source check before move operation
  • Adds tests

How to test?

  • Rename folder or move files

Demo

Screen.Recording.2026-05-22.at.12.11.42.mp4

Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
@alperozturk96 alperozturk96 requested a review from Copilot May 22, 2026 09:15
@alperozturk96 alperozturk96 added 3. to review performance 🚀 Performance improvement opportunities (non-crash related) labels May 22, 2026
@alperozturk96 alperozturk96 added this to the Nextcloud App 34.0.0 milestone May 22, 2026
Signed-off-by: alperozturk96 <alper_ozturk@proton.me>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors FileDataStorageManager.moveLocalFile(...) to a Kotlin extension-based implementation intended to improve performance/clarity, and adds unit tests around DB updates, filesystem moves, and media scan behavior when moving files/folders.

Changes:

  • Replaced the inlined Java moveLocalFile implementation with a delegation to FileDataStorageManagerExtensions.moveFiles.
  • Added a Room DAO bulk update method (FileDao.updateAll) and moved descendant path/storagePath update logic into DAO-backed Kotlin code.
  • Added a new unit test suite covering guard conditions, entity updates, hierarchy updates, filesystem rename behavior, and media scan triggering.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java Delegates moveLocalFile to the Kotlin extension and exposes TAG for shared logging.
app/src/main/java/com/nextcloud/utils/extensions/FileDataStorageManagerExtensions.kt Introduces moveFiles, filesystem move helper, and DB update logic for moved descendants + media scan handling.
app/src/main/java/com/nextcloud/client/database/dao/FileDao.kt Adds updateAll(List<FileEntity>) for bulk updates used by the move logic.
app/src/test/java/com/owncloud/android/datamodel/MoveFilesTestBase.kt Shared mocking/setup utilities for the new move-local-file test suite.
app/src/test/java/com/owncloud/android/datamodel/MoveFilesGuardTest.kt Tests early-return guard behavior (null/nonexistent/root/invalid parent, etc.).
app/src/test/java/com/owncloud/android/datamodel/MoveFileEntitiesUpdateTest.kt Tests DB entity field updates (path/pathDecrypted/storagePath/parent) for moves.
app/src/test/java/com/owncloud/android/datamodel/MoveFilesFilesystemAndMediaTest.kt Tests filesystem rename and media scan behavior for moved media vs non-media files.
app/src/test/java/com/owncloud/android/datamodel/MoveFilesHierarchyTest.kt Tests descendant updates (paths/parents/storagePath) when moving folders with hierarchies.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +26 to +33
fun testMoveLocalFileWhenFileDoesNotExistShouldReturnEarlyWithoutInteractingWithDatabase() {
val file = OCFile(OLD_PATH).apply { fileId = 0 }

manager.moveLocalFile(file, TARGET_PATH, TARGET_PARENT_PATH)

verify(exactly = 0) { mockFileDao.getFolderWithDescendants(any(), any()) }
verify(exactly = 0) { mockFileDao.updateAll(any()) }
}
Comment on lines +116 to +121
val originalMediaPaths =
fileDao.moveFilesInDb(oldPath, targetPath, defaultSavePath, targetParent.fileId, accountName)

val moved = moveLocalFiles(accountName, ocFile, defaultSavePath, targetPath)
if (!moved) return

Comment on lines +87 to +108
Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: file does not exist, skipping")
return
}

if (OCFile.ROOT_PATH == ocFile.fileName) {
Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: cannot move root path")
return
}

if (ocFile.remotePath == targetPath) {
Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: source and target paths are identical, skipping")
return
}

val targetParent = getFileByPath(targetParentPath)
if (targetParent == null) {
Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: target parent folder not found: $targetParentPath")
return
}

if (!targetParent.isFolder) {
Log_OC.e(FileDataStorageManager.TAG, "moveLocalFile: target parent is not a folder: $targetParentPath")
@github-actions
Copy link
Copy Markdown

APK file: https://github.com/nextcloud/android/actions/runs/26279321751/artifacts/7157181307
To test this change/fix you can simply download above APK file and install and test it in parallel to your existing Nextcloud app.
qrcode (please click on link to get QR code displayed)

@github-actions
Copy link
Copy Markdown

Codacy

SpotBugs

CategoryBaseNew
Bad practice3535
Correctness6767
Dodgy code216216
Experimental11
Internationalization77
Malicious code vulnerability22
Multithreaded correctness3434
Performance4242
Security1616
Total420420

@github-actions
Copy link
Copy Markdown

blue-Light-Screenshot test failed, but no output was generated. Maybe a preliminary stage failed.

@Suppress("ReturnCount")
private fun moveLocalFiles(accountName: String, ocFile: OCFile, defaultSavePath: String, targetPath: String): Boolean {
val localFile = File(FileStorageUtils.getDefaultSavePathFor(accountName, ocFile))
if (!localFile.exists()) {

val targetFile = File(defaultSavePath + targetPath)
val targetFolder = targetFile.getParentFile()
if (targetFolder != null && !targetFolder.exists() && !targetFolder.mkdirs()) {

val targetFile = File(defaultSavePath + targetPath)
val targetFolder = targetFile.getParentFile()
if (targetFolder != null && !targetFolder.exists() && !targetFolder.mkdirs()) {
)
}

if (!localFile.renameTo(targetFile)) {
)
}

if (!localFile.renameTo(targetFile)) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

3. to review performance 🚀 Performance improvement opportunities (non-crash related)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants