Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 78c1578b authored by tibbi's avatar tibbi
Browse files

rewrite some file operations to work with FileDirItem instead of files

parent f73c3c36
Loading
Loading
Loading
Loading
+17 −17
Original line number Diff line number Diff line
@@ -14,7 +14,6 @@ import android.support.v4.app.ActivityCompat
import android.support.v4.util.Pair
import android.support.v7.app.AppCompatActivity
import android.text.Html
import android.util.Log
import android.view.MenuItem
import android.view.WindowManager
import com.simplemobiletools.commons.R
@@ -25,6 +24,7 @@ import com.simplemobiletools.commons.dialogs.WritePermissionDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.interfaces.CopyMoveListener
import com.simplemobiletools.commons.models.FileDirItem
import java.io.File
import java.util.*

@@ -168,7 +168,6 @@ open class BaseSimpleActivity : AppCompatActivity() {

    fun handleSAFDialog(file: File, callback: () -> Unit): Boolean {
        return if (isShowingSAFDialog(file, baseConfig.treeUri, OPEN_DOCUMENT_TREE)) {
            Log.e("DEBUG", "SAF dialog")
            funAfterSAFPermission = callback
            true
        } else {
@@ -177,7 +176,7 @@ open class BaseSimpleActivity : AppCompatActivity() {
        }
    }

    fun copyMoveFilesTo(files: ArrayList<File>, source: String, destination: String, isCopyOperation: Boolean, copyPhotoVideoOnly: Boolean,
    fun copyMoveFilesTo(fileDirItems: ArrayList<FileDirItem>, source: String, destination: String, isCopyOperation: Boolean, copyPhotoVideoOnly: Boolean,
                        copyHidden: Boolean, callback: () -> Unit) {
        if (source == destination) {
            toast(R.string.source_and_destination_same)
@@ -193,31 +192,32 @@ open class BaseSimpleActivity : AppCompatActivity() {
        handleSAFDialog(destinationFolder) {
            copyMoveCallback = callback
            if (isCopyOperation) {
                startCopyMove(files, destinationFolder, isCopyOperation, copyPhotoVideoOnly, copyHidden)
                startCopyMove(fileDirItems, destinationFolder, isCopyOperation, copyPhotoVideoOnly, copyHidden)
            } else {
                if (isPathOnSD(source) || isPathOnSD(destination) || files.first().isDirectory || isNougatPlus()) {
                if (isPathOnSD(source) || isPathOnSD(destination) || fileDirItems.first().isDirectory || isNougatPlus()) {
                    handleSAFDialog(File(source)) {
                        startCopyMove(files, destinationFolder, isCopyOperation, copyPhotoVideoOnly, copyHidden)
                        startCopyMove(fileDirItems, destinationFolder, isCopyOperation, copyPhotoVideoOnly, copyHidden)
                    }
                } else {
                    toast(R.string.moving)
                    val updatedFiles = ArrayList<File>(files.size * 2)
                    updatedFiles.addAll(files)
                    val updatedFiles = ArrayList<FileDirItem>(fileDirItems.size * 2)
                    updatedFiles.addAll(fileDirItems)
                    try {
                        for (oldFile in files) {
                            val newFile = File(destinationFolder, oldFile.name)
                            if (!newFile.exists() && oldFile.renameTo(newFile)) {
                        for (oldFileDirItem in fileDirItems) {
                            val newFile = File(destinationFolder, oldFileDirItem.name)
                            if (!newFile.exists() && File(oldFileDirItem.path).renameTo(newFile)) {
                                if (!baseConfig.keepLastModified) {
                                    newFile.setLastModified(System.currentTimeMillis())
                                }
                                updateInMediaStore(oldFile, newFile)
                                updatedFiles.add(newFile)
                                updateInMediaStore(oldFileDirItem.path, newFile.absolutePath)
                                updatedFiles.add(newFile.toFileDirItem())
                            }
                        }

                        scanFiles(updatedFiles) {
                        val updatedPaths = updatedFiles.map { it.path } as ArrayList<String>
                        scanPaths(updatedPaths) {
                            runOnUiThread {
                                copyMoveListener.copySucceeded(false, files.size * 2 == updatedFiles.size)
                                copyMoveListener.copySucceeded(false, fileDirItems.size * 2 == updatedFiles.size)
                            }
                        }
                    } catch (e: Exception) {
@@ -228,7 +228,7 @@ open class BaseSimpleActivity : AppCompatActivity() {
        }
    }

    private fun startCopyMove(files: ArrayList<File>, destinationFolder: File, isCopyOperation: Boolean, copyPhotoVideoOnly: Boolean, copyHidden: Boolean) {
    private fun startCopyMove(files: ArrayList<FileDirItem>, destinationFolder: File, isCopyOperation: Boolean, copyPhotoVideoOnly: Boolean, copyHidden: Boolean) {
        checkConflict(files, destinationFolder, 0, LinkedHashMap()) {
            toast(if (isCopyOperation) R.string.copying else R.string.moving)
            val pair = Pair(files, destinationFolder)
@@ -236,7 +236,7 @@ open class BaseSimpleActivity : AppCompatActivity() {
        }
    }

    private fun checkConflict(files: ArrayList<File>, destinationFolder: File, index: Int, conflictResolutions: LinkedHashMap<String, Int>,
    private fun checkConflict(files: ArrayList<FileDirItem>, destinationFolder: File, index: Int, conflictResolutions: LinkedHashMap<String, Int>,
                              callback: (resolutions: LinkedHashMap<String, Int>) -> Unit) {
        if (index == files.size) {
            callback(conflictResolutions)
+24 −22
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.CONFLICT_OVERWRITE
import com.simplemobiletools.commons.helpers.CONFLICT_SKIP
import com.simplemobiletools.commons.interfaces.CopyMoveListener
import com.simplemobiletools.commons.models.FileDirItem
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
@@ -24,14 +25,14 @@ import java.lang.ref.WeakReference
import java.util.*

class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = false, val copyMediaOnly: Boolean, val conflictResolutions: LinkedHashMap<String, Int>,
                   listener: CopyMoveListener, val copyHidden: Boolean) : AsyncTask<Pair<ArrayList<File>, File>, Void, Boolean>() {
                   listener: CopyMoveListener, val copyHidden: Boolean) : AsyncTask<Pair<ArrayList<FileDirItem>, File>, Void, Boolean>() {
    private val INITIAL_PROGRESS_DELAY = 3000L
    private val PROGRESS_RECHECK_INTERVAL = 500L

    private var mListener: WeakReference<CopyMoveListener>? = null
    private var mMovedFiles: ArrayList<File> = ArrayList()
    private var mMovedFiles: ArrayList<FileDirItem> = ArrayList()
    private var mDocuments = LinkedHashMap<String, DocumentFile?>()
    private lateinit var mFiles: ArrayList<File>
    private lateinit var mFiles: ArrayList<FileDirItem>
    private var mFileCountToCopy = 0

    // progress indication
@@ -49,7 +50,7 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
        mNotificationBuilder = NotificationCompat.Builder(activity)
    }

    override fun doInBackground(vararg params: Pair<ArrayList<File>, File>): Boolean? {
    override fun doInBackground(vararg params: Pair<ArrayList<FileDirItem>, File>): Boolean? {
        if (params.isEmpty()) {
            return false
        }
@@ -62,7 +63,7 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
        for (file in mFiles) {
            val newFile = File(pair.second, file.name)
            if (!newFile.exists() || getConflictResolution(newFile) != CONFLICT_SKIP) {
                mMaxSize += (file.getProperSize(copyHidden) / 1000).toInt()
                mMaxSize += (File(file.path).getProperSize(copyHidden) / 1000).toInt()
            }
        }

@@ -80,7 +81,7 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
                        mFileCountToCopy--
                        continue
                    } else if (resolution == CONFLICT_OVERWRITE) {
                        activity.deleteFilesBg(arrayListOf(newFile), true)
                        activity.deleteFileBg(newFile.toFileDirItem(), true)
                    }
                }

@@ -95,7 +96,8 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
            activity.deleteFiles(mMovedFiles) {}
        }

        activity.scanFiles(mFiles) {}
        val paths = mFiles.map { it.path } as ArrayList<String>
        activity.scanPaths(paths) {}
        return true
    }

@@ -151,7 +153,7 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
        }
    }

    private fun copy(source: File, destination: File) {
    private fun copy(source: FileDirItem, destination: File) {
        if (source.isDirectory) {
            copyDirectory(source, destination)
        } else {
@@ -159,29 +161,29 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
        }
    }

    private fun copyDirectory(source: File, destination: File) {
    private fun copyDirectory(source: FileDirItem, destination: File) {
        if (!activity.createDirectorySync(destination)) {
            val error = String.format(activity.getString(R.string.could_not_create_folder), destination.absolutePath)
            activity.showErrorToast(error)
            return
        }

        val children = source.list()
        val children = File(source.path).list()
        for (child in children) {
            val newFile = File(destination, child)
            if (newFile.exists()) {
                continue
            }

            val oldFile = File(source, child)
            copy(oldFile, newFile)
            val oldFile = File(source.path, child)
            copy(oldFile.toFileDirItem(), newFile)
        }
        mMovedFiles.add(source)
    }

    private fun copyFile(source: File, destination: File) {
        if (copyMediaOnly && !source.absolutePath.isImageVideoGif()) {
            mCurrentProgress += source.length()
    private fun copyFile(source: FileDirItem, destination: File) {
        if (copyMediaOnly && !source.path.isImageVideoGif()) {
            mCurrentProgress += source.size
            return
        }

@@ -189,7 +191,7 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
        if (!activity.createDirectorySync(directory)) {
            val error = String.format(activity.getString(R.string.could_not_create_folder), directory.absolutePath)
            activity.showErrorToast(error)
            mCurrentProgress += source.length()
            mCurrentProgress += source.size
            return
        }

@@ -201,9 +203,9 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
                mDocuments[destination.parent] = activity.getFileDocument(destination.parent)
            }

            out = activity.getFileOutputStreamSync(destination.absolutePath, source.getMimeType(), mDocuments[destination.parent])
            out = activity.getFileOutputStreamSync(destination.absolutePath, source.path.getMimeType(), mDocuments[destination.parent])

            inputStream = FileInputStream(source)
            inputStream = FileInputStream(File(source.path))

            val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
            var bytes = inputStream.read(buffer)
@@ -213,10 +215,10 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
                bytes = inputStream.read(buffer)
            }

            if (source.length() == destination.length()) {
            if (source.size == destination.length()) {
                mMovedFiles.add(source)
                if (activity.baseConfig.keepLastModified) {
                    copyOldLastModified(source, destination)
                    copyOldLastModified(source.path, destination)
                } else {
                    activity.scanFile(destination) {}
                }
@@ -229,13 +231,13 @@ class CopyMoveTask(val activity: BaseSimpleActivity, val copyOnly: Boolean = fal
        }
    }

    private fun copyOldLastModified(source: File, destination: File) {
    private fun copyOldLastModified(sourcePath: String, destination: File) {
        val projection = arrayOf(
                MediaStore.Images.Media.DATE_TAKEN,
                MediaStore.Images.Media.DATE_MODIFIED)
        val uri = MediaStore.Files.getContentUri("external")
        val selection = "${MediaStore.MediaColumns.DATA} = ?"
        var selectionArgs = arrayOf(source.absolutePath)
        var selectionArgs = arrayOf(sourcePath)
        val cursor = activity.applicationContext.contentResolver.query(uri, projection, selection, selectionArgs, null)

        cursor?.use {
+51 −38
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import com.simplemobiletools.commons.dialogs.SecurityDialog
import com.simplemobiletools.commons.dialogs.WhatsNewDialog
import com.simplemobiletools.commons.dialogs.WritePermissionDialog
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.FileDirItem
import com.simplemobiletools.commons.models.Release
import com.simplemobiletools.commons.models.SharedTheme
import com.simplemobiletools.commons.views.MyTextView
@@ -297,7 +298,7 @@ fun BaseSimpleActivity.checkWhatsNew(releases: List<Release>, currVersion: Int)
    baseConfig.lastVersion = currVersion
}

fun BaseSimpleActivity.deleteFolders(folders: ArrayList<File>, deleteMediaOnly: Boolean = true, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
fun BaseSimpleActivity.deleteFolders(folders: ArrayList<FileDirItem>, deleteMediaOnly: Boolean = true, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
    if (Looper.myLooper() == Looper.getMainLooper()) {
        Thread {
            deleteFoldersBg(folders, deleteMediaOnly, callback)
@@ -307,12 +308,12 @@ fun BaseSimpleActivity.deleteFolders(folders: ArrayList<File>, deleteMediaOnly:
    }
}

fun BaseSimpleActivity.deleteFoldersBg(folders: ArrayList<File>, deleteMediaOnly: Boolean = true, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
fun BaseSimpleActivity.deleteFoldersBg(folders: ArrayList<FileDirItem>, deleteMediaOnly: Boolean = true, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
    var wasSuccess = false
    var needPermissionForPath = ""
    for (file in folders) {
        if (needsStupidWritePermissions(file.absolutePath) && baseConfig.treeUri.isEmpty()) {
            needPermissionForPath = file.absolutePath
    for (folder in folders) {
        if (needsStupidWritePermissions(folder.path) && baseConfig.treeUri.isEmpty()) {
            needPermissionForPath = folder.path
            break
        }
    }
@@ -331,7 +332,7 @@ fun BaseSimpleActivity.deleteFoldersBg(folders: ArrayList<File>, deleteMediaOnly
    }
}

fun BaseSimpleActivity.deleteFolder(folder: File, deleteMediaOnly: Boolean = true, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
fun BaseSimpleActivity.deleteFolder(folder: FileDirItem, deleteMediaOnly: Boolean = true, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
    if (Looper.myLooper() == Looper.getMainLooper()) {
        Thread {
            deleteFolderBg(folder, deleteMediaOnly, callback)
@@ -341,7 +342,8 @@ fun BaseSimpleActivity.deleteFolder(folder: File, deleteMediaOnly: Boolean = tru
    }
}

fun BaseSimpleActivity.deleteFolderBg(folder: File, deleteMediaOnly: Boolean = true, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
fun BaseSimpleActivity.deleteFolderBg(fileDirItem: FileDirItem, deleteMediaOnly: Boolean = true, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
    val folder = File(fileDirItem.path)
    if (folder.exists()) {
        val filesArr = folder.listFiles()
        if (filesArr == null) {
@@ -352,17 +354,17 @@ fun BaseSimpleActivity.deleteFolderBg(folder: File, deleteMediaOnly: Boolean = t
        val filesList = (filesArr as Array).toList()
        val files = filesList.filter { !deleteMediaOnly || it.isImageVideoGif() }
        for (file in files) {
            deleteFileBg(file, false) { }
            deleteFileBg(file.toFileDirItem(), false) { }
        }

        if (folder.listFiles()?.isEmpty() == true) {
            deleteFileBg(folder, true) { }
            deleteFileBg(fileDirItem, true) { }
        }
    }
    callback?.invoke(true)
}

fun BaseSimpleActivity.deleteFiles(files: ArrayList<File>, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
fun BaseSimpleActivity.deleteFiles(files: ArrayList<FileDirItem>, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
    if (Looper.myLooper() == Looper.getMainLooper()) {
        Thread {
            deleteFilesBg(files, allowDeleteFolder, callback)
@@ -372,18 +374,19 @@ fun BaseSimpleActivity.deleteFiles(files: ArrayList<File>, allowDeleteFolder: Bo
    }
}

fun BaseSimpleActivity.deleteFilesBg(files: ArrayList<File>, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
fun BaseSimpleActivity.deleteFilesBg(files: ArrayList<FileDirItem>, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
    if (files.isEmpty()) {
        callback?.invoke(true)
        return
    }

    var wasSuccess = false
    handleSAFDialog(files[0]) {
    handleSAFDialog(File(files[0].path)) {
        files.forEachIndexed { index, file ->
            deleteFileBg(file, allowDeleteFolder) {
                if (it)
                if (it) {
                    wasSuccess = true
                }

                if (index == files.size - 1) {
                    callback?.invoke(wasSuccess)
@@ -393,21 +396,22 @@ fun BaseSimpleActivity.deleteFilesBg(files: ArrayList<File>, allowDeleteFolder:
    }
}

fun BaseSimpleActivity.deleteFile(file: File, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
fun BaseSimpleActivity.deleteFile(fileDirItem: FileDirItem, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
    if (Looper.myLooper() == Looper.getMainLooper()) {
        Thread {
            deleteFileBg(file, allowDeleteFolder, callback)
            deleteFileBg(fileDirItem, allowDeleteFolder, callback)
        }.start()
    } else {
        deleteFileBg(file, allowDeleteFolder, callback)
        deleteFileBg(fileDirItem, allowDeleteFolder, callback)
    }
}

@SuppressLint("NewApi")
fun BaseSimpleActivity.deleteFileBg(file: File, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
    var fileDeleted = !file.exists() || file.delete()
fun BaseSimpleActivity.deleteFileBg(fileDirItem: FileDirItem, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
    val file = File(fileDirItem.path)
    var fileDeleted = !isPathOnOTG(fileDirItem.path) && (!file.exists() || file.delete())
    if (fileDeleted) {
        rescanDeletedFile(file) {
        rescanDeletedFile(fileDirItem) {
            callback?.invoke(true)
        }
    } else {
@@ -415,31 +419,40 @@ fun BaseSimpleActivity.deleteFileBg(file: File, allowDeleteFolder: Boolean = fal
            fileDeleted = deleteRecursively(file)
        }

        if (!fileDeleted && isPathOnSD(file.absolutePath)) {
        if (!fileDeleted) {
            if (isPathOnSD(fileDirItem.path)) {
                handleSAFDialog(file) {
                fileDeleted = tryFastDocumentDelete(file, allowDeleteFolder)
                    trySAFFileDelete(fileDirItem, allowDeleteFolder, callback)
                }
            } else if (isPathOnOTG(fileDirItem.path)) {
                trySAFFileDelete(fileDirItem, allowDeleteFolder, callback)
            }
        }
    }
}

@SuppressLint("NewApi")
fun BaseSimpleActivity.trySAFFileDelete(fileDirItem: FileDirItem, allowDeleteFolder: Boolean = false, callback: ((wasSuccess: Boolean) -> Unit)? = null) {
    var fileDeleted = tryFastDocumentDelete(fileDirItem, allowDeleteFolder)
    if (!fileDeleted) {
                    val document = getFileDocument(file.absolutePath)
                    if (document != null && (file.isDirectory == document.isDirectory)) {
        val document = getFileDocument(fileDirItem.path)
        if (document != null && (fileDirItem.isDirectory == document.isDirectory)) {
            fileDeleted = (document.isFile == true || allowDeleteFolder) && DocumentsContract.deleteDocument(applicationContext.contentResolver, document.uri)
        }
                }

        if (fileDeleted) {
                    rescanDeletedFile(file) {
            rescanDeletedFile(fileDirItem) {
                callback?.invoke(true)
            }
        }
    }
}
    }
}

fun BaseSimpleActivity.rescanDeletedFile(file: File, callback: (() -> Unit)? = null) {
    if (deleteFromMediaStore(file)) {
fun BaseSimpleActivity.rescanDeletedFile(fileDirItem: FileDirItem, callback: (() -> Unit)? = null) {
    if (deleteFromMediaStore(fileDirItem.path)) {
        callback?.invoke()
    } else {
        MediaScannerConnection.scanFile(applicationContext, arrayOf(file.absolutePath), null, { s, uri ->
        MediaScannerConnection.scanFile(applicationContext, arrayOf(fileDirItem.path), null, { s, uri ->
            try {
                applicationContext.contentResolver.delete(uri, null, null)
            } catch (e: Exception) {
@@ -493,7 +506,7 @@ fun BaseSimpleActivity.renameFile(oldFile: File, newFile: File, callback: ((succ
            try {
                val uri = DocumentsContract.renameDocument(applicationContext.contentResolver, document.uri, newFile.name)
                if (document.uri != uri) {
                    updateInMediaStore(oldFile, newFile)
                    updateInMediaStore(oldFile.absolutePath, newFile.absolutePath)
                    scanFiles(arrayListOf(oldFile, newFile)) {
                        if (!baseConfig.keepLastModified) {
                            updateLastModified(newFile, System.currentTimeMillis())
@@ -510,7 +523,7 @@ fun BaseSimpleActivity.renameFile(oldFile: File, newFile: File, callback: ((succ
        }
    } else if (oldFile.renameTo(newFile)) {
        if (newFile.isDirectory) {
            deleteFromMediaStore(oldFile)
            deleteFromMediaStore(oldFile.path)
            scanFile(newFile) {
                callback?.invoke(true)
            }
@@ -518,7 +531,7 @@ fun BaseSimpleActivity.renameFile(oldFile: File, newFile: File, callback: ((succ
            if (!baseConfig.keepLastModified) {
                newFile.setLastModified(System.currentTimeMillis())
            }
            updateInMediaStore(oldFile, newFile)
            updateInMediaStore(oldFile.absolutePath, newFile.absolutePath)
            scanFile(newFile) {
                callback?.invoke(true)
            }
+37 −22
Original line number Diff line number Diff line
@@ -109,6 +109,8 @@ fun Context.isPathOnSD(path: String) = sdCardPath.isNotEmpty() && path.startsWit

fun Context.isPathOnOTG(path: String) = path.startsWith(OTG_PATH)

fun Context.isFileDirItemOnOTG(fileDirItem: FileDirItem) = fileDirItem.path.startsWith(OTG_PATH)

fun Context.needsStupidWritePermissions(path: String) = isPathOnSD(path) && isLollipopPlus()

@SuppressLint("NewApi")
@@ -134,8 +136,8 @@ fun Context.getMyFileUri(file: File): Uri {
}

@SuppressLint("NewApi")
fun Context.tryFastDocumentDelete(file: File, allowDeleteFolder: Boolean): Boolean {
    val document = getFastDocument(file)
fun Context.tryFastDocumentDelete(fileDirItem: FileDirItem, allowDeleteFolder: Boolean): Boolean {
    val document = getFastDocument(fileDirItem)
    return if (document?.isFile == true || allowDeleteFolder) {
        DocumentsContract.deleteDocument(contentResolver, document?.uri)
    } else {
@@ -144,13 +146,23 @@ fun Context.tryFastDocumentDelete(file: File, allowDeleteFolder: Boolean): Boole
}

@SuppressLint("NewApi")
fun Context.getFastDocument(file: File): DocumentFile? {
    if (!isLollipopPlus() || baseConfig.sdCardPath.isEmpty())
fun Context.getFastDocument(fileDirItem: FileDirItem): DocumentFile? {
    if (!isLollipopPlus()) {
        return null
    }

    val relativePath = Uri.encode(file.absolutePath.substring(baseConfig.sdCardPath.length).trim('/'))
    val sdCardPathPart = baseConfig.sdCardPath.split("/").filter(String::isNotEmpty).last().trim('/')
    val fullUri = "${baseConfig.treeUri}/document/$sdCardPathPart%3A$relativePath"
    val isOTG = isFileDirItemOnOTG(fileDirItem)
    if (!isOTG && baseConfig.sdCardPath.isEmpty()) {
        return null
    }

    val startString = if (isOTG) OTG_PATH else baseConfig.sdCardPath
    val basePath = if (isOTG) baseConfig.OTGBasePath else baseConfig.sdCardPath
    val treeUri = if (isOTG) baseConfig.OTGTreeUri else baseConfig.treeUri

    val relativePath = Uri.encode(fileDirItem.path.substring(startString.length).trim('/'))
    val externalPathPart = basePath.split("/").last(String::isNotEmpty).trim('/')
    val fullUri = "$treeUri/document/$externalPathPart%3A$relativePath"
    return DocumentFile.fromSingleUri(this, Uri.parse(fullUri))
}

@@ -198,33 +210,33 @@ fun getPaths(file: File): ArrayList<String> {
    return paths
}

fun Context.getFileUri(file: File) = when {
    file.isImageSlow() -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    file.isVideoSlow() -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
fun Context.getFileUri(path: String) = when {
    path.isImageSlow() -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    path.isVideoSlow() -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI
    else -> MediaStore.Files.getContentUri("external")
}

// these functions update the mediastore instantly, MediaScannerConnection.scanFile takes some time to really get applied
fun Context.deleteFromMediaStore(file: File): Boolean {
fun Context.deleteFromMediaStore(path: String): Boolean {
    return try {
        val where = "${MediaStore.MediaColumns.DATA} = ?"
        val args = arrayOf(file.absolutePath)
        contentResolver.delete(getFileUri(file), where, args) == 1
        val args = arrayOf(path)
        contentResolver.delete(getFileUri(path), where, args) == 1
    } catch (e: Exception) {
        false
    }
}

fun Context.updateInMediaStore(oldFile: File, newFile: File) {
fun Context.updateInMediaStore(oldPath: String, newPath: String) {
    Thread {
        val values = ContentValues().apply {
            put(MediaStore.MediaColumns.DATA, newFile.absolutePath)
            put(MediaStore.MediaColumns.DISPLAY_NAME, newFile.name)
            put(MediaStore.MediaColumns.TITLE, newFile.name)
            put(MediaStore.MediaColumns.DATA, newPath)
            put(MediaStore.MediaColumns.DISPLAY_NAME, newPath.getFilenameFromPath())
            put(MediaStore.MediaColumns.TITLE, newPath.getFilenameFromPath())
        }
        val uri = getFileUri(oldFile)
        val uri = getFileUri(oldPath)
        val selection = "${MediaStore.MediaColumns.DATA} = ?"
        val selectionArgs = arrayOf(oldFile.absolutePath)
        val selectionArgs = arrayOf(oldPath)

        try {
            contentResolver.update(uri, values, selection, selectionArgs)
@@ -238,7 +250,7 @@ fun Context.updateLastModified(file: File, lastModified: Long) {
        put(MediaStore.MediaColumns.DATE_MODIFIED, lastModified)
    }
    file.setLastModified(lastModified)
    val uri = getFileUri(file)
    val uri = getFileUri(file.absolutePath)
    val selection = "${MediaStore.MediaColumns.DATA} = ?"
    val selectionArgs = arrayOf(file.absolutePath)

@@ -271,12 +283,15 @@ fun Context.getOTGItems(path: String, callback: (ArrayList<FileDirItem>) -> Unit
        if (first != null) {
            val fullPath = first.uri.toString()
            val nameStartIndex = fullPath.lastIndexOf(first.name)
            val basePath = fullPath.substring(0, nameStartIndex)
            var basePath = fullPath.substring(0, nameStartIndex)
            if (basePath.endsWith("%3A")) {
                basePath = basePath.substring(0, basePath.length - 3)
            }
            baseConfig.OTGBasePath = basePath
        }
    }

    val basePath = baseConfig.OTGBasePath
    val basePath = "${baseConfig.OTGBasePath}%3A"
    for (file in files) {
        if (file.exists()) {
            val filePath = file.uri.toString().substring(basePath.length)
+3 −0

File changed.

Preview size limit exceeded, changes collapsed.

Loading