Fix duplicate files being created when saving pages on Android 10+ with separate folders setting enabled
Fixes #9943
This commit is contained in:
parent
d4290f6f59
commit
77a8a4229c
1 changed files with 41 additions and 28 deletions
|
@ -1,6 +1,5 @@
|
||||||
package eu.kanade.tachiyomi.data.saver
|
package eu.kanade.tachiyomi.data.saver
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.ContentUris
|
import android.content.ContentUris
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
@ -28,30 +27,59 @@ class ImageSaver(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
|
||||||
fun save(image: Image): Uri {
|
fun save(image: Image): Uri {
|
||||||
val data = image.data
|
val data = image.data
|
||||||
|
|
||||||
val type = ImageUtil.findImageType(data) ?: throw Exception("Not an image")
|
val type = ImageUtil.findImageType(data) ?: throw IllegalArgumentException("Not an image")
|
||||||
val filename = DiskUtil.buildValidFilename("${image.name}.${type.extension}")
|
val filename = DiskUtil.buildValidFilename("${image.name}.${type.extension}")
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || image.location !is Location.Pictures) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || image.location !is Location.Pictures) {
|
||||||
return save(data(), image.location.directory(context), filename)
|
return save(data(), image.location.directory(context), filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return saveApi29(image, type, filename, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun save(inputStream: InputStream, directory: File, filename: String): Uri {
|
||||||
|
directory.mkdirs()
|
||||||
|
|
||||||
|
val destFile = File(directory, filename)
|
||||||
|
|
||||||
|
inputStream.use { input ->
|
||||||
|
destFile.outputStream().use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DiskUtil.scanMedia(context, destFile.toUri())
|
||||||
|
|
||||||
|
return destFile.getUriCompat(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
|
private fun saveApi29(
|
||||||
|
image: Image,
|
||||||
|
type: ImageUtil.ImageType,
|
||||||
|
filename: String,
|
||||||
|
data: () -> InputStream,
|
||||||
|
): Uri {
|
||||||
val pictureDir =
|
val pictureDir =
|
||||||
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||||
|
|
||||||
val folderRelativePath = "${Environment.DIRECTORY_PICTURES}/${context.getString(R.string.app_name)}/"
|
|
||||||
val imageLocation = (image.location as Location.Pictures).relativePath
|
val imageLocation = (image.location as Location.Pictures).relativePath
|
||||||
|
val relativePath = listOf(
|
||||||
|
Environment.DIRECTORY_PICTURES,
|
||||||
|
context.getString(R.string.app_name),
|
||||||
|
imageLocation,
|
||||||
|
).joinToString(File.separator)
|
||||||
|
|
||||||
val contentValues = contentValuesOf(
|
val contentValues = contentValuesOf(
|
||||||
|
MediaStore.Images.Media.RELATIVE_PATH to relativePath,
|
||||||
MediaStore.Images.Media.DISPLAY_NAME to image.name,
|
MediaStore.Images.Media.DISPLAY_NAME to image.name,
|
||||||
MediaStore.Images.Media.MIME_TYPE to type.mime,
|
MediaStore.Images.Media.MIME_TYPE to type.mime,
|
||||||
MediaStore.Images.Media.RELATIVE_PATH to folderRelativePath + imageLocation,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val picture = findUriOrDefault(folderRelativePath, "$imageLocation$filename") {
|
val picture = findUriOrDefault(relativePath, filename) {
|
||||||
context.contentResolver.insert(
|
context.contentResolver.insert(
|
||||||
pictureDir,
|
pictureDir,
|
||||||
contentValues,
|
contentValues,
|
||||||
|
@ -74,49 +102,34 @@ class ImageSaver(
|
||||||
return picture
|
return picture
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun save(inputStream: InputStream, directory: File, filename: String): Uri {
|
|
||||||
directory.mkdirs()
|
|
||||||
|
|
||||||
val destFile = File(directory, filename)
|
|
||||||
|
|
||||||
inputStream.use { input ->
|
|
||||||
destFile.outputStream().use { output ->
|
|
||||||
input.copyTo(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DiskUtil.scanMedia(context, destFile.toUri())
|
|
||||||
|
|
||||||
return destFile.getUriCompat(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.Q)
|
@RequiresApi(Build.VERSION_CODES.Q)
|
||||||
private fun findUriOrDefault(relativePath: String, imagePath: String, default: () -> Uri): Uri {
|
private fun findUriOrDefault(path: String, filename: String, default: () -> Uri): Uri {
|
||||||
val projection = arrayOf(
|
val projection = arrayOf(
|
||||||
MediaStore.MediaColumns._ID,
|
MediaStore.MediaColumns._ID,
|
||||||
MediaStore.MediaColumns.DISPLAY_NAME,
|
MediaStore.MediaColumns.DISPLAY_NAME,
|
||||||
MediaStore.Images.Media.MIME_TYPE,
|
|
||||||
MediaStore.MediaColumns.RELATIVE_PATH,
|
MediaStore.MediaColumns.RELATIVE_PATH,
|
||||||
MediaStore.MediaColumns.DATE_MODIFIED,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
val selection = "${MediaStore.MediaColumns.RELATIVE_PATH}=? AND ${MediaStore.MediaColumns.DISPLAY_NAME}=?"
|
val selection = "${MediaStore.MediaColumns.RELATIVE_PATH}=? AND ${MediaStore.MediaColumns.DISPLAY_NAME}=?"
|
||||||
|
|
||||||
|
// Need to make sure it ends with the separator
|
||||||
|
val normalizedPath = "${path.removeSuffix(File.separator)}${File.separator}"
|
||||||
|
|
||||||
context.contentResolver.query(
|
context.contentResolver.query(
|
||||||
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||||
projection,
|
projection,
|
||||||
selection,
|
selection,
|
||||||
arrayOf(relativePath, imagePath),
|
arrayOf(normalizedPath, filename),
|
||||||
null,
|
null,
|
||||||
).use { cursor ->
|
).use { cursor ->
|
||||||
if (cursor != null && cursor.count >= 1) {
|
if (cursor != null && cursor.count >= 1) {
|
||||||
cursor.moveToFirst().let {
|
if (cursor.moveToFirst()) {
|
||||||
val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
|
val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
|
||||||
|
|
||||||
return ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
|
return ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return default()
|
return default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue