Fix storage getting full while moving multiple items to the recycle bin at once

This commit is contained in:
Mino260806 2022-09-04 23:41:21 +01:00
parent 07fb21a775
commit 9f152ea01d
6 changed files with 66 additions and 15 deletions

View File

@ -656,9 +656,10 @@ class MainActivity : SimpleActivity(), DirectoryOperationsListener {
val pathsToDelete = ArrayList<String>() val pathsToDelete = ArrayList<String>()
itemsToDelete.mapTo(pathsToDelete) { it.path } itemsToDelete.mapTo(pathsToDelete) { it.path }
movePathsInRecycleBin(pathsToDelete) { movePathsInRecycleBin(pathsToDelete) { wasSuccess, range ->
if (it) { if (wasSuccess) {
deleteFilteredFileDirItems(itemsToDelete, folders) val itemsInRange = itemsToDelete.subList(range.first, range.second)
deleteFilteredFileDirItems(ArrayList(itemsInRange), ArrayList())
} else { } else {
toast(R.string.unknown_error_occurred) toast(R.string.unknown_error_occurred)
} }

View File

@ -887,9 +887,10 @@ class MediaActivity : SimpleActivity(), MediaOperationsListener {
val movingItems = resources.getQuantityString(R.plurals.moving_items_into_bin, filtered.size, filtered.size) val movingItems = resources.getQuantityString(R.plurals.moving_items_into_bin, filtered.size, filtered.size)
toast(movingItems) toast(movingItems)
movePathsInRecycleBin(filtered.map { it.path } as ArrayList<String>) { movePathsInRecycleBin(filtered.map { it.path } as ArrayList<String>) { wasSuccess, range ->
if (it) { if (wasSuccess) {
deleteFilteredFiles(filtered) val itemsInRange = filtered.subList(range.first, range.second)
deleteFilteredFiles(ArrayList(itemsInRange))
} else { } else {
toast(R.string.unknown_error_occurred) toast(R.string.unknown_error_occurred)
} }

View File

@ -267,9 +267,10 @@ class SearchActivity : SimpleActivity(), MediaOperationsListener {
val movingItems = resources.getQuantityString(R.plurals.moving_items_into_bin, filtered.size, filtered.size) val movingItems = resources.getQuantityString(R.plurals.moving_items_into_bin, filtered.size, filtered.size)
toast(movingItems) toast(movingItems)
movePathsInRecycleBin(filtered.map { it.path } as ArrayList<String>) { movePathsInRecycleBin(filtered.map { it.path } as ArrayList<String>) { wasSuccess, range ->
if (it) { if (wasSuccess) {
deleteFilteredFiles(filtered) val itemsInRange = filtered.subList(range.first, range.second)
deleteFilteredFiles(ArrayList(itemsInRange))
} else { } else {
toast(R.string.unknown_error_occurred) toast(R.string.unknown_error_occurred)
} }

View File

@ -1161,8 +1161,8 @@ class ViewPagerActivity : SimpleActivity(), ViewPager.OnPageChangeListener, View
onPageSelected(0) onPageSelected(0)
} }
movePathsInRecycleBin(arrayListOf(path)) { movePathsInRecycleBin(arrayListOf(path)) { wasSuccess, _ ->
if (it) { if (wasSuccess) {
tryDeleteFileDirItem(fileDirItem, false, false) { tryDeleteFileDirItem(fileDirItem, false, false) {
mIgnoredPaths.remove(fileDirItem.path) mIgnoredPaths.remove(fileDirItem.path)
if (media.isEmpty()) { if (media.isEmpty()) {

View File

@ -44,6 +44,7 @@ import com.squareup.picasso.Picasso
import java.io.* import java.io.*
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.collections.ArrayList
fun Activity.sharePath(path: String) { fun Activity.sharePath(path: String) {
sharePathIntent(path, BuildConfig.APPLICATION_ID) sharePathIntent(path, BuildConfig.APPLICATION_ID)
@ -313,18 +314,45 @@ fun BaseSimpleActivity.tryDeleteFileDirItem(
} }
} }
fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, callback: ((wasSuccess: Boolean) -> Unit)?) { fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, callback: ((wasSuccess: Boolean, movedRange: Pair<Int, Int>) -> Unit)?) {
ensureBackgroundThread { ensureBackgroundThread {
var pathsCnt = paths.size var pathsCnt = paths.size
val OTGPath = config.OTGPath val OTGPath = config.OTGPath
for (source in paths) { var availableSize = getAvailableInternalMemorySize().toLong()
// If available size is more than 200MB, keep a safe distance from
// exceeding the remaining size
if (availableSize > 1024 * 1024 * 200)
availableSize -= 1024 * 1024 * 50
var totalCopiedSize = 0L
var lastDeletedIndex = -1
val ensureSizeIsAvailable = { index: Int, size: Long ->
// Try to delete already moved files if space if not sufficient
if (size <= availableSize && totalCopiedSize > 0 && totalCopiedSize + size > availableSize) {
// Return true in wasSuccess for now, callback will be called
// at the end of the function with the correct parameter
callback?.invoke(true, Pair(lastDeletedIndex + 1, index + 1))
availableSize = getAvailableInternalMemorySize()
totalCopiedSize = 0L
lastDeletedIndex = index
}
size <= availableSize
}
for ((index, source) in paths.withIndex()) {
if (OTGPath.isNotEmpty() && source.startsWith(OTGPath)) { if (OTGPath.isNotEmpty() && source.startsWith(OTGPath)) {
var inputStream: InputStream? = null var inputStream: InputStream? = null
var out: OutputStream? = null var out: OutputStream? = null
try { try {
val destination = "$recycleBinPath/$source" val destination = "$recycleBinPath/$source"
val fileDocument = getSomeDocumentFile(source) val fileDocument = getSomeDocumentFile(source)
val originalSize = fileDocument?.getItemSize(true)!!
if (!ensureSizeIsAvailable(index, originalSize))
continue
inputStream = applicationContext.contentResolver.openInputStream(fileDocument?.uri!!) inputStream = applicationContext.contentResolver.openInputStream(fileDocument?.uri!!)
out = getFileOutputStreamSync(destination, source.getMimeType()) out = getFileOutputStreamSync(destination, source.getMimeType())
@ -339,9 +367,11 @@ fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, callback:
out?.flush() out?.flush()
if (fileDocument.getItemSize(true) == copiedSize && getDoesFilePathExist(destination)) { if (originalSize == copiedSize && getDoesFilePathExist(destination)) {
mediaDB.updateDeleted("$RECYCLE_BIN$source", System.currentTimeMillis(), source) mediaDB.updateDeleted("$RECYCLE_BIN$source", System.currentTimeMillis(), source)
pathsCnt-- pathsCnt--
totalCopiedSize += copiedSize
} }
} catch (e: Exception) { } catch (e: Exception) {
showErrorToast(e) showErrorToast(e)
@ -352,6 +382,10 @@ fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, callback:
} }
} else { } else {
val file = File(source) val file = File(source)
val originalSize = file.length()
if (!ensureSizeIsAvailable(index, originalSize))
continue
val internalFile = File(recycleBinPath, source) val internalFile = File(recycleBinPath, source)
val lastModified = file.lastModified() val lastModified = file.lastModified()
try { try {
@ -362,6 +396,8 @@ fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, callback:
if (config.keepLastModified && lastModified != 0L) { if (config.keepLastModified && lastModified != 0L) {
internalFile.setLastModified(lastModified) internalFile.setLastModified(lastModified)
} }
totalCopiedSize += originalSize
} }
} catch (e: Exception) { } catch (e: Exception) {
showErrorToast(e) showErrorToast(e)
@ -369,7 +405,7 @@ fun BaseSimpleActivity.movePathsInRecycleBin(paths: ArrayList<String>, callback:
} }
} }
} }
callback?.invoke(pathsCnt == 0) callback?.invoke(pathsCnt == 0, Pair(lastDeletedIndex + 1, paths.size))
} }
} }

View File

@ -8,7 +8,9 @@ import android.database.Cursor
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.drawable.PictureDrawable import android.graphics.drawable.PictureDrawable
import android.media.AudioManager import android.media.AudioManager
import android.os.Environment
import android.os.Process import android.os.Process
import android.os.StatFs
import android.provider.MediaStore.Files import android.provider.MediaStore.Files
import android.provider.MediaStore.Images import android.provider.MediaStore.Images
import android.widget.ImageView import android.widget.ImageView
@ -43,6 +45,7 @@ import java.nio.ByteBuffer
import java.nio.channels.FileChannel import java.nio.channels.FileChannel
import kotlin.collections.set import kotlin.collections.set
val Context.audioManager get() = getSystemService(Context.AUDIO_SERVICE) as AudioManager val Context.audioManager get() = getSystemService(Context.AUDIO_SERVICE) as AudioManager
fun Context.getHumanizedFilename(path: String): String { fun Context.getHumanizedFilename(path: String): String {
@ -1103,3 +1106,12 @@ fun Context.getFileDateTaken(path: String): Long {
return 0L return 0L
} }
// https://stackoverflow.com/questions/8133417/android-get-free-size-of-internal-external-memory
fun Context.getAvailableInternalMemorySize(): Long {
val path: File = filesDir
val stat = StatFs(path.path)
val blockSize = stat.blockSizeLong
val availableBlocks = stat.availableBlocksLong
return availableBlocks * blockSize
}