rewrite some file operations to work with FileDirItem instead of files
This commit is contained in:
parent
f73c3c36a6
commit
78c1578b89
7 changed files with 140 additions and 102 deletions
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)) {
|
||||
handleSAFDialog(file) {
|
||||
fileDeleted = tryFastDocumentDelete(file, allowDeleteFolder)
|
||||
if (!fileDeleted) {
|
||||
val document = getFileDocument(file.absolutePath)
|
||||
if (document != null && (file.isDirectory == document.isDirectory)) {
|
||||
fileDeleted = (document.isFile == true || allowDeleteFolder) && DocumentsContract.deleteDocument(applicationContext.contentResolver, document.uri)
|
||||
}
|
||||
}
|
||||
|
||||
if (fileDeleted) {
|
||||
rescanDeletedFile(file) {
|
||||
callback?.invoke(true)
|
||||
}
|
||||
if (!fileDeleted) {
|
||||
if (isPathOnSD(fileDirItem.path)) {
|
||||
handleSAFDialog(file) {
|
||||
trySAFFileDelete(fileDirItem, allowDeleteFolder, callback)
|
||||
}
|
||||
} else if (isPathOnOTG(fileDirItem.path)) {
|
||||
trySAFFileDelete(fileDirItem, allowDeleteFolder, callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseSimpleActivity.rescanDeletedFile(file: File, callback: (() -> Unit)? = null) {
|
||||
if (deleteFromMediaStore(file)) {
|
||||
@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(fileDirItem.path)
|
||||
if (document != null && (fileDirItem.isDirectory == document.isDirectory)) {
|
||||
fileDeleted = (document.isFile == true || allowDeleteFolder) && DocumentsContract.deleteDocument(applicationContext.contentResolver, document.uri)
|
||||
}
|
||||
|
||||
if (fileDeleted) {
|
||||
rescanDeletedFile(fileDirItem) {
|
||||
callback?.invoke(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.graphics.Bitmap
|
|||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Point
|
||||
import android.media.MediaMetadataRetriever
|
||||
import com.simplemobiletools.commons.models.FileDirItem
|
||||
import java.io.File
|
||||
|
||||
fun File.isImageVideoGif() = absolutePath.isImageFast() || absolutePath.isVideoFast() || absolutePath.isGif()
|
||||
|
@ -147,3 +148,5 @@ private fun getDirectoryFileCount(dir: File, countHiddenItems: Boolean): Int {
|
|||
}
|
||||
return count
|
||||
}
|
||||
|
||||
fun File.toFileDirItem() = FileDirItem(absolutePath, name, isDirectory, 0, 0L)
|
||||
|
|
|
@ -10,6 +10,8 @@ fun String.getFilenameFromPath() = substring(lastIndexOf("/") + 1)
|
|||
|
||||
fun String.getFilenameExtension() = substring(lastIndexOf(".") + 1)
|
||||
|
||||
fun String.getMimeType() = getMimeTypeFromPath()
|
||||
|
||||
fun String.getBasePath(context: Context): String {
|
||||
return if (startsWith(context.internalStoragePath)) {
|
||||
context.internalStoragePath
|
||||
|
@ -47,10 +49,13 @@ fun String.isDng() = endsWith(".dng", true)
|
|||
|
||||
// fast extension checks, not guaranteed to be accurate
|
||||
fun String.isVideoFast() = videoExtensions.any { endsWith(it, true) }
|
||||
|
||||
fun String.isImageFast() = photoExtensions.any { endsWith(it, true) }
|
||||
fun String.isAudioFast() = audioExtensions.any { endsWith(it, true) }
|
||||
|
||||
fun String.isImageSlow() = isImageFast() || getMimeType().startsWith("image")
|
||||
fun String.isVideoSlow() = isVideoFast() || getMimeType().startsWith("video")
|
||||
fun String.isAudioSlow() = isAudioFast() || getMimeType().startsWith("audio")
|
||||
|
||||
fun String.areDigitsOnly() = matches(Regex("[0-9]+"))
|
||||
|
||||
fun String.getExifProperties(exif: ExifInterface): String {
|
||||
|
|
|
@ -5,7 +5,7 @@ import com.simplemobiletools.commons.extensions.formatSize
|
|||
import com.simplemobiletools.commons.helpers.*
|
||||
import java.io.File
|
||||
|
||||
data class FileDirItem(val path: String, val name: String, val isDirectory: Boolean, var children: Int, var size: Long) :
|
||||
data class FileDirItem(val path: String, val name: String = "", val isDirectory: Boolean = false, var children: Int = 0, var size: Long = 0L) :
|
||||
Comparable<FileDirItem> {
|
||||
companion object {
|
||||
var sorting: Int = 0
|
||||
|
|
Loading…
Reference in a new issue