Checklist
Affected app version
1.6.1 (Latest)
Affected Android/Custom ROM version
Android 13
Affected device model
Samsung Galaxy S20
How did you install the app?
None
Steps to reproduce the bug
Scenario: Copy blocked despite skippable duplicates
- Have a device/emulator with 30 GB free space
- Create 5 files (A, B, C, D, E), each 10 GB
- Place A, B, C in Folder1
- Place A, B, C, D, E in Folder2 (identical A, B, C files)
- Select all files in Folder2 → Copy to → select Folder1
- Expected: Conflict dialog appears for A, B, C. If user selects "Skip" + "Apply to all", only D and E (20 GB) are copied → fits in 30 GB free space
- Actual: "Not enough space." error. Conflict dialog never appears
Expected behavior
Copy operations should not calculate transfer size before conflict resolution — The space check should not use the full resource size before the user has a chance to resolve conflicts (skip/overwrite/keep both). Files to be skipped should not be included in the space calculation beforehand.
Actual behavior
Copy operations calculate transfer size before conflict resolution — The space check uses the full source size before the user has a chance to resolve conflicts (skip/overwrite/keep both). Files that would be skipped are still counted toward the required space.
Screenshots/Screen recordings
Since I reset my phone some time after identifying this problem, I currently have plenty of free space, and unfortunately, I cannot reproduce the issue. I apologize for not having a screenshot.
Additional information
Root Cause Analysis
The bug originates in the Fossify Commons library, specifically in the startCopyMove() function inside BaseSimpleActivity.kt.
The sumToCopy is calculated unconditionally as the total size of all source files:
private fun startCopyMove(...) {
val availableSpace = destinationPath.getAvailableStorageB()
val sumToCopy = files.sumByLong { it.getProperSize(applicationContext, copyHidden) }
// ❌ Space check happens BEFORE checkConflicts
if (availableSpace == -1L || sumToCopy < availableSpace) {
checkConflicts(files, destinationPath, 0, LinkedHashMap()) { conflictResolutions ->
// ...
}
} else {
val text = String.format(getString(R.string.no_space), ...)
toast(text, Toast.LENGTH_LONG)
}
}
Because the space check happens before checkConflicts() is called, if the raw total exceeds available space, the operation is rejected immediately.
The correct flow should be:
- Call
checkConflicts() to let the user choose how to handle duplicate files.
- Calculate the effective transfer size based on the user's choices (e.g. skipped files should not be included in the total).
- Compare the effective size against the available space.
Suggested Fix
The fix should be applied in BaseSimpleActivity.startCopyMove() in the Fossify Commons library. The space check logic needs to be moved inside the checkConflicts callback:
private fun startCopyMove(...) {
// Step 1: Always resolve conflicts first
checkConflicts(files, destinationPath, 0, LinkedHashMap()) { conflictResolutions ->
// Step 2: Calculate effective transfer size based on conflict resolutions
var effectiveSize = 0L
for (file in files) {
val newPath = "$destinationPath/${file.name}"
val resolution = getConflictResolution(conflictResolutions, newPath)
val fileExists = getDoesFilePathExist(newPath)
when {
fileExists && resolution == CONFLICT_SKIP -> { /* exclude skipped files */ }
fileExists && resolution == CONFLICT_OVERWRITE -> { /* exclude overwritten bytes */ }
else -> effectiveSize += file.getProperSize(applicationContext, copyHidden)
}
}
val availableSpace = destinationPath.getAvailableStorageB()
// Step 3: Check space using the effective size
if (availableSpace != -1L && effectiveSize >= availableSpace) {
val text = String.format(
getString(R.string.no_space),
effectiveSize.formatSize(),
availableSpace.formatSize()
)
toast(text, Toast.LENGTH_LONG)
return@checkConflicts
}
// Step 4: Proceed with operation
toast(if (isCopyOperation) R.string.copying else R.string.moving)
val pair = Pair(files, destinationPath)
CopyMoveTask(
activity = this,
isCopyOperation = isCopyOperation,
copyPhotoVideoOnly = copyPhotoVideoOnly,
conflictResolutions = conflictResolutions,
listener = copyMoveListener,
copyHidden = copyHidden
).execute(pair)
}
}
Related Files
| File |
Repository |
Role |
BaseSimpleActivity.kt |
FossifyOrg/commons |
Contains startCopyMove() where the order of operations is flawed |
CopyMoveTask.kt |
FossifyOrg/commons |
AsyncTask that performs the actual copy/move (which correctly respects resolutions, but is blocked from running) |
Checklist
Affected app version
1.6.1 (Latest)
Affected Android/Custom ROM version
Android 13
Affected device model
Samsung Galaxy S20
How did you install the app?
None
Steps to reproduce the bug
Scenario: Copy blocked despite skippable duplicates
Expected behavior
Copy operations should not calculate transfer size before conflict resolution — The space check should not use the full resource size before the user has a chance to resolve conflicts (skip/overwrite/keep both). Files to be skipped should not be included in the space calculation beforehand.
Actual behavior
Copy operations calculate transfer size before conflict resolution — The space check uses the full source size before the user has a chance to resolve conflicts (skip/overwrite/keep both). Files that would be skipped are still counted toward the required space.
Screenshots/Screen recordings
Since I reset my phone some time after identifying this problem, I currently have plenty of free space, and unfortunately, I cannot reproduce the issue. I apologize for not having a screenshot.
Additional information
Root Cause Analysis
The bug originates in the Fossify Commons library, specifically in the
startCopyMove()function insideBaseSimpleActivity.kt.The
sumToCopyis calculated unconditionally as the total size of all source files:Because the space check happens before
checkConflicts()is called, if the raw total exceeds available space, the operation is rejected immediately.The correct flow should be:
checkConflicts()to let the user choose how to handle duplicate files.Suggested Fix
The fix should be applied in
BaseSimpleActivity.startCopyMove()in the Fossify Commons library. The space check logic needs to be moved inside thecheckConflictscallback:Related Files
BaseSimpleActivity.ktstartCopyMove()where the order of operations is flawedCopyMoveTask.ktAsyncTaskthat performs the actual copy/move (which correctly respects resolutions, but is blocked from running)