Renaming on SDK 30

- handle renaming files with SAF except files in the Download directory
- handle renaming files in the Download directory using MediaStore.createWrite request, then duplicating the file with the new name
- handle renaming files in the root of internal storage using MediaStore.createWrite request, then using contentResolver to update the display name
- this methods do not work for files in the root of SD card and all files in OTG media
This commit is contained in:
darthpaul 2022-03-19 18:09:54 +00:00
parent 557d5bee02
commit dc0ca635c5
8 changed files with 339 additions and 99 deletions

View file

@ -207,6 +207,15 @@ class FilePickerDialog(
if ((pickFile && fileDocument.isFile) || (!pickFile && fileDocument.isDirectory)) { if ((pickFile && fileDocument.isFile) || (!pickFile && fileDocument.isDirectory)) {
sendSuccess() sendSuccess()
} }
} else if (activity.isAccessibleWithSAFSdk30(currPath)) {
activity.handleSAFDialogSdk30(currPath) {
if (it) {
val document = activity.getSomeDocumentSdk30(currPath) ?: return@handleSAFDialogSdk30
if ((pickFile && document.isFile) || (!pickFile && document.isDirectory)) {
sendSuccess()
}
}
}
} else { } else {
val file = File(currPath) val file = File(currPath)
if ((pickFile && file.isFile) || (!pickFile && file.isDirectory)) { if ((pickFile && file.isFile) || (!pickFile && file.isDirectory)) {

View file

@ -0,0 +1,74 @@
package com.simplemobiletools.commons.extensions
import android.content.ContentValues
import android.provider.MediaStore
import com.simplemobiletools.commons.R
import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.models.FileDirItem
import java.io.File
import java.io.InputStream
import java.io.OutputStream
fun BaseSimpleActivity.copySingleFileSdk30(source: FileDirItem, destination: FileDirItem): Boolean {
val directory = destination.getParentPath()
if (!createDirectorySync(directory)) {
val error = String.format(getString(R.string.could_not_create_folder), directory)
showErrorToast(error)
return false
}
var inputStream: InputStream? = null
var out: OutputStream? = null
try {
out = getFileOutputStreamSync(destination.path, source.path.getMimeType())
inputStream = getFileInputStreamSync(source.path)!!
var copiedSize = 0L
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytes = inputStream.read(buffer)
while (bytes >= 0) {
out!!.write(buffer, 0, bytes)
copiedSize += bytes
bytes = inputStream.read(buffer)
}
out?.flush()
return if (source.size == copiedSize && getDoesFilePathExist(destination.path)) {
if (baseConfig.keepLastModified) {
copyOldLastModified(source.path, destination.path)
File(destination.path).setLastModified(File(source.path).lastModified())
}
true
} else {
false
}
} finally {
inputStream?.close()
out?.close()
}
}
fun BaseSimpleActivity.copyOldLastModified(sourcePath: String, destinationPath: String) {
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(sourcePath)
val cursor = applicationContext.contentResolver.query(uri, projection, selection, selectionArgs, null)
cursor?.use {
if (cursor.moveToFirst()) {
val dateTaken = cursor.getLongValue(MediaStore.Images.Media.DATE_TAKEN)
val dateModified = cursor.getIntValue(MediaStore.Images.Media.DATE_MODIFIED)
val values = ContentValues().apply {
put(MediaStore.Images.Media.DATE_TAKEN, dateTaken)
put(MediaStore.Images.Media.DATE_MODIFIED, dateModified)
}
selectionArgs = arrayOf(destinationPath)
applicationContext.contentResolver.update(uri, values, selection, selectionArgs)
}
}
}

View file

@ -834,26 +834,49 @@ fun BaseSimpleActivity.renameFile(
oldPath: String, oldPath: String,
newPath: String, newPath: String,
isRenamingMultipleFiles: Boolean, isRenamingMultipleFiles: Boolean,
callback: ((success: Boolean, useAndroid30Way: Boolean) -> Unit)? = null callback: ((success: Boolean, android30RenameFormat: Android30RenameFormat) -> Unit)? = null
) { ) {
if (isRestrictedSAFOnlyRoot(oldPath)) { if (isRestrictedSAFOnlyRoot(oldPath)) {
handleAndroidSAFDialog(oldPath) { handleAndroidSAFDialog(oldPath) {
if (!it) { if (!it) {
runOnUiThread { runOnUiThread {
callback?.invoke(false, false) callback?.invoke(false, Android30RenameFormat.NONE)
} }
return@handleAndroidSAFDialog return@handleAndroidSAFDialog
} }
try { try {
val success = renameAndroidSAFDocument(oldPath, newPath) ensureBackgroundThread {
runOnUiThread { val success = renameAndroidSAFDocument(oldPath, newPath)
callback?.invoke(success, false) runOnUiThread {
callback?.invoke(success, Android30RenameFormat.NONE)
}
} }
} catch (e: Exception) { } catch (e: Exception) {
showErrorToast(e) showErrorToast(e)
runOnUiThread { runOnUiThread {
callback?.invoke(false, false) callback?.invoke(false, Android30RenameFormat.NONE)
}
}
}
} else if (isAccessibleWithSAFSdk30(oldPath)) {
handleSAFDialogSdk30(oldPath) {
if (!it) {
return@handleSAFDialogSdk30
}
try {
ensureBackgroundThread {
val success = renameDocumentSdk30(oldPath, newPath)
runOnUiThread {
callback?.invoke(success, Android30RenameFormat.NONE)
}
}
} catch (e: Exception) {
showErrorToast(e)
runOnUiThread {
callback?.invoke(false, Android30RenameFormat.NONE)
} }
} }
} }
@ -866,7 +889,7 @@ fun BaseSimpleActivity.renameFile(
val document = getSomeDocumentFile(oldPath) val document = getSomeDocumentFile(oldPath)
if (document == null || (File(oldPath).isDirectory != document.isDirectory)) { if (document == null || (File(oldPath).isDirectory != document.isDirectory)) {
runOnUiThread { runOnUiThread {
callback?.invoke(false, false) callback?.invoke(false, Android30RenameFormat.NONE)
} }
return@handleSAFDialog return@handleSAFDialog
} }
@ -879,7 +902,7 @@ fun BaseSimpleActivity.renameFile(
// FileNotFoundException is thrown in some weird cases, but renaming works just fine // FileNotFoundException is thrown in some weird cases, but renaming works just fine
} catch (e: Exception) { } catch (e: Exception) {
showErrorToast(e) showErrorToast(e)
callback?.invoke(false, false) callback?.invoke(false, Android30RenameFormat.NONE)
return@ensureBackgroundThread return@ensureBackgroundThread
} }
@ -890,14 +913,14 @@ fun BaseSimpleActivity.renameFile(
} }
deleteFromMediaStore(oldPath) deleteFromMediaStore(oldPath)
runOnUiThread { runOnUiThread {
callback?.invoke(true, false) callback?.invoke(true, Android30RenameFormat.NONE)
} }
} }
} }
} catch (e: Exception) { } catch (e: Exception) {
showErrorToast(e) showErrorToast(e)
runOnUiThread { runOnUiThread {
callback?.invoke(false, false) callback?.invoke(false, Android30RenameFormat.NONE)
} }
} }
} }
@ -910,7 +933,7 @@ fun BaseSimpleActivity.renameFile(
if (isRPlus() && exception is java.nio.file.FileSystemException) { if (isRPlus() && exception is java.nio.file.FileSystemException) {
// if we are renaming multiple files at once, we should give the Android 30+ permission dialog all uris together, not one by one // if we are renaming multiple files at once, we should give the Android 30+ permission dialog all uris together, not one by one
if (isRenamingMultipleFiles) { if (isRenamingMultipleFiles) {
callback?.invoke(false, true) callback?.invoke(false, Android30RenameFormat.CONTENT_RESOLVER)
} else { } else {
val fileUris = getFileUrisFromFileDirItems(arrayListOf(File(oldPath).toFileDirItem(this))).second val fileUris = getFileUrisFromFileDirItems(arrayListOf(File(oldPath).toFileDirItem(this))).second
updateSDK30Uris(fileUris) { success -> updateSDK30Uris(fileUris) { success ->
@ -921,19 +944,19 @@ fun BaseSimpleActivity.renameFile(
try { try {
contentResolver.update(fileUris.first(), values, null, null) contentResolver.update(fileUris.first(), values, null, null)
callback?.invoke(true, false) callback?.invoke(true, Android30RenameFormat.NONE)
} catch (e: Exception) { } catch (e: Exception) {
showErrorToast(e) showErrorToast(e)
callback?.invoke(false, false) callback?.invoke(false, Android30RenameFormat.NONE)
} }
} else { } else {
callback?.invoke(false, false) callback?.invoke(false, Android30RenameFormat.NONE)
} }
} }
} }
} else { } else {
showErrorToast(exception) showErrorToast(exception)
callback?.invoke(false, false) callback?.invoke(false, Android30RenameFormat.NONE)
} }
return return
} }
@ -945,7 +968,7 @@ fun BaseSimpleActivity.renameFile(
updateInMediaStore(oldPath, newPath) updateInMediaStore(oldPath, newPath)
rescanPath(newPath) { rescanPath(newPath) {
runOnUiThread { runOnUiThread {
callback?.invoke(true, false) callback?.invoke(true, Android30RenameFormat.NONE)
} }
deleteFromMediaStore(oldPath) deleteFromMediaStore(oldPath)
scanPathRecursively(newPath) scanPathRecursively(newPath)
@ -958,14 +981,48 @@ fun BaseSimpleActivity.renameFile(
scanPathsRecursively(arrayListOf(newPath)) { scanPathsRecursively(arrayListOf(newPath)) {
deleteFromMediaStore(oldPath) deleteFromMediaStore(oldPath)
runOnUiThread { runOnUiThread {
callback?.invoke(true, false) callback?.invoke(true, Android30RenameFormat.NONE)
} }
} }
} }
} else { } else {
tempFile.delete() tempFile.delete()
runOnUiThread { newFile.delete()
callback?.invoke(false, false) if (isRPlus()) {
// if we are renaming multiple files at once, we should give the Android 30+ permission dialog all uris together, not one by one
if (isRenamingMultipleFiles) {
callback?.invoke(false, Android30RenameFormat.SAF)
} else {
val fileUris = getFileUrisFromFileDirItems(arrayListOf(File(oldPath).toFileDirItem(this))).second
updateSDK30Uris(fileUris) { success ->
if (!success) {
return@updateSDK30Uris
}
try {
val sourceFile = File(oldPath).toFileDirItem(this)
val destinationFile = sourceFile.copy(path = newPath, name = newPath.getFilenameFromPath())
val copySuccessful = copySingleFileSdk30(sourceFile, destinationFile)
if (copySuccessful) {
if (!baseConfig.keepLastModified) {
newFile.setLastModified(System.currentTimeMillis())
}
contentResolver.delete(fileUris.first(), null)
updateInMediaStore(oldPath, newPath)
scanPathsRecursively(arrayListOf(newPath)) {
runOnUiThread {
callback?.invoke(true, Android30RenameFormat.NONE)
}
}
}
} catch (e: Exception) {
showErrorToast(e)
callback?.invoke(false, Android30RenameFormat.NONE)
}
}
}
} else {
callback?.invoke(false, Android30RenameFormat.NONE)
} }
} }
} }

View file

@ -153,6 +153,18 @@ fun Context.deleteDocumentWithSAFSdk30(fileDirItem: FileDirItem, allowDeleteFold
} }
} }
fun Context.renameDocumentSdk30(oldPath: String, newPath: String): Boolean {
return try {
val treeUri = createFirstParentTreeUri(oldPath)
val documentId = getSAFDocumentId(oldPath)
val parentUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, documentId)
DocumentsContract.renameDocument(contentResolver, parentUri, newPath.getFilenameFromPath()) != null
} catch (e: IllegalStateException) {
showErrorToast(e)
false
}
}
fun Context.hasProperStoredDocumentUriSdk30(path: String): Boolean { fun Context.hasProperStoredDocumentUriSdk30(path: String): Boolean {
val documentUri = buildDocumentUriSdk30(path) val documentUri = buildDocumentUriSdk30(path)
return contentResolver.persistedUriPermissions.any { it.uri.toString() == documentUri.toString() } return contentResolver.persistedUriPermissions.any { it.uri.toString() == documentUri.toString() }

View file

@ -0,0 +1,7 @@
package com.simplemobiletools.commons.models
enum class Android30RenameFormat {
SAF,
CONTENT_RESOLVER,
NONE
}

View file

@ -7,7 +7,7 @@ import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.* import com.simplemobiletools.commons.helpers.*
import java.io.File import java.io.File
open class FileDirItem( data class FileDirItem(
val path: String, val path: String,
val name: String = "", val name: String = "",
var isDirectory: Boolean = false, var isDirectory: Boolean = false,

View file

@ -12,6 +12,7 @@ import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.isNougatPlus import com.simplemobiletools.commons.helpers.isNougatPlus
import com.simplemobiletools.commons.interfaces.RenameTab import com.simplemobiletools.commons.interfaces.RenameTab
import com.simplemobiletools.commons.models.Android30RenameFormat
import kotlinx.android.synthetic.main.dialog_rename_items_pattern.view.* import kotlinx.android.synthetic.main.dialog_rename_items_pattern.view.*
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -50,8 +51,9 @@ class RenamePatternTab(context: Context, attrs: AttributeSet) : RelativeLayout(c
} }
val validPaths = paths.filter { activity?.getDoesFilePathExist(it) == true } val validPaths = paths.filter { activity?.getDoesFilePathExist(it) == true }
val sdFilePath = validPaths.firstOrNull { activity?.isPathOnSD(it) == true } ?: validPaths.firstOrNull() val firstPath = validPaths.firstOrNull()
if (sdFilePath == null) { val sdFilePath = validPaths.firstOrNull { activity?.isPathOnSD(it) == true } ?: firstPath
if (firstPath == null || sdFilePath == null) {
activity?.toast(R.string.unknown_error_occurred) activity?.toast(R.string.unknown_error_occurred)
return return
} }
@ -62,39 +64,44 @@ class RenamePatternTab(context: Context, attrs: AttributeSet) : RelativeLayout(c
return@handleSAFDialog return@handleSAFDialog
} }
ignoreClicks = true activity?.handleSAFDialogSdk30(firstPath) {
var pathsCnt = validPaths.size if (!it) {
numbersCnt = pathsCnt.toString().length return@handleSAFDialogSdk30
for (path in validPaths) {
if (stopLooping) {
return@handleSAFDialog
} }
try { ignoreClicks = true
val newPath = getNewPath(path, useMediaFileExtension) ?: continue var pathsCnt = validPaths.size
activity?.renameFile(path, newPath, true) { success, useAndroid30Way -> numbersCnt = pathsCnt.toString().length
if (success) { for (path in validPaths) {
pathsCnt-- if (stopLooping) {
if (pathsCnt == 0) { return@handleSAFDialogSdk30
callback(true) }
}
} else { try {
ignoreClicks = false val newPath = getNewPath(path, useMediaFileExtension) ?: continue
if (useAndroid30Way) { activity?.renameFile(path, newPath, true) { success, android30Format ->
currentIncrementalNumber = 1 if (success) {
stopLooping = true pathsCnt--
renameAllFiles(validPaths, useMediaFileExtension, callback) if (pathsCnt == 0) {
callback(true)
}
} else { } else {
activity?.toast(R.string.unknown_error_occurred) ignoreClicks = false
if (android30Format != Android30RenameFormat.NONE) {
currentIncrementalNumber = 1
stopLooping = true
renameAllFiles(validPaths, useMediaFileExtension, android30Format, callback)
} else {
activity?.toast(R.string.unknown_error_occurred)
}
} }
} }
} catch (e: Exception) {
activity?.showErrorToast(e)
} }
} catch (e: Exception) {
activity?.showErrorToast(e)
} }
stopLooping = false
} }
stopLooping = false
} }
} }
@ -167,26 +174,59 @@ class RenamePatternTab(context: Context, attrs: AttributeSet) : RelativeLayout(c
} }
} }
private fun renameAllFiles(paths: List<String>, useMediaFileExtension: Boolean, callback: (success: Boolean) -> Unit) { private fun renameAllFiles(
paths: List<String>,
useMediaFileExtension: Boolean,
android30Format: Android30RenameFormat,
callback: (success: Boolean) -> Unit
) {
val fileDirItems = paths.map { File(it).toFileDirItem(context) } val fileDirItems = paths.map { File(it).toFileDirItem(context) }
val uriPairs = context.getFileUrisFromFileDirItems(fileDirItems) val uriPairs = context.getFileUrisFromFileDirItems(fileDirItems)
val validPaths = uriPairs.first val validPaths = uriPairs.first
val uris = uriPairs.second val uris = uriPairs.second
val activity = activity
activity?.updateSDK30Uris(uris) { success -> activity?.updateSDK30Uris(uris) { success ->
if (success) { if (success) {
try { try {
uris.forEachIndexed { index, uri -> uris.forEachIndexed { index, uri ->
val path = validPaths[index] val path = validPaths[index]
val newFileName = getNewPath(path, useMediaFileExtension)?.getFilenameFromPath() ?: return@forEachIndexed val newFileName = getNewPath(path, useMediaFileExtension)?.getFilenameFromPath() ?: return@forEachIndexed
val values = ContentValues().apply { when (android30Format) {
put(MediaStore.Images.Media.DISPLAY_NAME, newFileName) Android30RenameFormat.SAF -> {
val sourceFile = File(path).toFileDirItem(context)
val newPath = "${path.getParentPath()}/$newFileName"
val destinationFile = sourceFile.copy(path = newPath, name = newFileName)
if (activity.copySingleFileSdk30(sourceFile, destinationFile)) {
if (!activity.baseConfig.keepLastModified) {
File(newPath).setLastModified(System.currentTimeMillis())
}
activity.contentResolver.delete(uri, null)
activity.updateInMediaStore(path, newPath)
activity.scanPathsRecursively(arrayListOf(newPath))
}
}
Android30RenameFormat.CONTENT_RESOLVER -> {
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, newFileName)
}
context.contentResolver.update(uri, values, null, null)
}
Android30RenameFormat.NONE -> {
activity.runOnUiThread {
callback(true)
}
return@forEachIndexed
}
} }
context.contentResolver.update(uri, values, null, null)
} }
callback(true) activity.runOnUiThread {
callback(true)
}
} catch (e: Exception) { } catch (e: Exception) {
callback(false) activity.runOnUiThread {
activity.showErrorToast(e)
callback(false)
}
} }
} }
} }

View file

@ -9,6 +9,7 @@ import com.simplemobiletools.commons.R
import com.simplemobiletools.commons.activities.BaseSimpleActivity import com.simplemobiletools.commons.activities.BaseSimpleActivity
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.interfaces.RenameTab import com.simplemobiletools.commons.interfaces.RenameTab
import com.simplemobiletools.commons.models.Android30RenameFormat
import kotlinx.android.synthetic.main.tab_rename_simple.view.* import kotlinx.android.synthetic.main.tab_rename_simple.view.*
import java.io.File import java.io.File
@ -44,8 +45,9 @@ class RenameSimpleTab(context: Context, attrs: AttributeSet) : RelativeLayout(co
} }
val validPaths = paths.filter { activity?.getDoesFilePathExist(it) == true } val validPaths = paths.filter { activity?.getDoesFilePathExist(it) == true }
val sdFilePath = validPaths.firstOrNull { activity?.isPathOnSD(it) == true } ?: validPaths.firstOrNull() val firstPath = validPaths.firstOrNull()
if (sdFilePath == null) { val sdFilePath = validPaths.firstOrNull { activity?.isPathOnSD(it) == true } ?: firstPath
if (firstPath == null || sdFilePath == null) {
activity?.toast(R.string.unknown_error_occurred) activity?.toast(R.string.unknown_error_occurred)
return return
} }
@ -55,61 +57,73 @@ class RenameSimpleTab(context: Context, attrs: AttributeSet) : RelativeLayout(co
return@handleSAFDialog return@handleSAFDialog
} }
ignoreClicks = true activity?.handleSAFDialogSdk30(firstPath) {
var pathsCnt = validPaths.size if (!it) {
for (path in validPaths) { return@handleSAFDialogSdk30
if (stopLooping) {
return@handleSAFDialog
} }
val fullName = path.getFilenameFromPath() ignoreClicks = true
var dotAt = fullName.lastIndexOf(".") var pathsCnt = validPaths.size
if (dotAt == -1) { for (path in validPaths) {
dotAt = fullName.length if (stopLooping) {
} return@handleSAFDialogSdk30
}
val name = fullName.substring(0, dotAt) val fullName = path.getFilenameFromPath()
val extension = if (fullName.contains(".")) ".${fullName.getFilenameExtension()}" else "" var dotAt = fullName.lastIndexOf(".")
if (dotAt == -1) {
dotAt = fullName.length
}
val newName = if (append) { val name = fullName.substring(0, dotAt)
"$name$valueToAdd$extension" val extension = if (fullName.contains(".")) ".${fullName.getFilenameExtension()}" else ""
} else {
"$valueToAdd$fullName"
}
val newPath = "${path.getParentPath()}/$newName" val newName = if (append) {
"$name$valueToAdd$extension"
if (activity?.getDoesFilePathExist(newPath) == true) {
continue
}
activity?.renameFile(path, newPath, true) { success, useAndroid30Way ->
if (success) {
pathsCnt--
if (pathsCnt == 0) {
callback(true)
}
} else { } else {
ignoreClicks = false "$valueToAdd$fullName"
if (useAndroid30Way) { }
stopLooping = true
renameAllFiles(validPaths, append, valueToAdd, callback) val newPath = "${path.getParentPath()}/$newName"
if (activity?.getDoesFilePathExist(newPath) == true) {
continue
}
activity?.renameFile(path, newPath, true) { success, android30Format ->
if (success) {
pathsCnt--
if (pathsCnt == 0) {
callback(true)
}
} else { } else {
activity?.toast(R.string.unknown_error_occurred) ignoreClicks = false
if (android30Format != Android30RenameFormat.NONE) {
stopLooping = true
renameAllFiles(validPaths, append, valueToAdd, android30Format, callback)
} else {
activity?.toast(R.string.unknown_error_occurred)
}
} }
} }
} }
stopLooping = false
} }
stopLooping = false
} }
} }
private fun renameAllFiles(paths: List<String>, appendString: Boolean, stringToAdd: String, callback: (success: Boolean) -> Unit) { private fun renameAllFiles(
paths: List<String>,
appendString: Boolean,
stringToAdd: String,
android30Format: Android30RenameFormat,
callback: (success: Boolean) -> Unit
) {
val fileDirItems = paths.map { File(it).toFileDirItem(context) } val fileDirItems = paths.map { File(it).toFileDirItem(context) }
val uriPairs = context.getFileUrisFromFileDirItems(fileDirItems) val uriPairs = context.getFileUrisFromFileDirItems(fileDirItems)
val validPaths = uriPairs.first val validPaths = uriPairs.first
val uris = uriPairs.second val uris = uriPairs.second
val activity = activity
activity?.updateSDK30Uris(uris) { success -> activity?.updateSDK30Uris(uris) { success ->
if (success) { if (success) {
try { try {
@ -131,15 +145,42 @@ class RenameSimpleTab(context: Context, attrs: AttributeSet) : RelativeLayout(co
"$stringToAdd$fullName" "$stringToAdd$fullName"
} }
val values = ContentValues().apply { when (android30Format) {
put(MediaStore.Images.Media.DISPLAY_NAME, newName) Android30RenameFormat.SAF -> {
val sourceFile = File(path).toFileDirItem(activity)
val newPath = "${path.getParentPath()}/$newName"
val destinationFile = sourceFile.copy(path = newPath, name = newName)
if (activity.copySingleFileSdk30(sourceFile, destinationFile)) {
if (!activity.baseConfig.keepLastModified) {
File(newPath).setLastModified(System.currentTimeMillis())
}
activity.contentResolver.delete(uri, null)
activity.updateInMediaStore(path, newPath)
activity.scanPathsRecursively(arrayListOf(newPath))
}
}
Android30RenameFormat.CONTENT_RESOLVER -> {
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, newName)
}
context.contentResolver.update(uri, values, null, null)
}
Android30RenameFormat.NONE -> {
activity.runOnUiThread {
callback(true)
}
return@forEachIndexed
}
} }
context.contentResolver.update(uri, values, null, null)
} }
callback(true) activity.runOnUiThread {
callback(true)
}
} catch (e: Exception) { } catch (e: Exception) {
callback(false) activity.runOnUiThread {
activity.showErrorToast(e)
callback(false)
}
} }
} }
} }