mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-04 08:08:55 +01:00 
			
		
		
		
	Fix duplicate files being created when saving pages on Android 10+ with separate folders setting enabled
Fixes #9943
This commit is contained in:
		@@ -1,6 +1,5 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.saver
 | 
			
		||||
 | 
			
		||||
import android.annotation.SuppressLint
 | 
			
		||||
import android.content.ContentUris
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.graphics.Bitmap
 | 
			
		||||
@@ -28,30 +27,59 @@ class ImageSaver(
 | 
			
		||||
    val context: Context,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    @SuppressLint("InlinedApi")
 | 
			
		||||
    fun save(image: Image): Uri {
 | 
			
		||||
        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}")
 | 
			
		||||
 | 
			
		||||
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || image.location !is Location.Pictures) {
 | 
			
		||||
            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 =
 | 
			
		||||
            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 relativePath = listOf(
 | 
			
		||||
            Environment.DIRECTORY_PICTURES,
 | 
			
		||||
            context.getString(R.string.app_name),
 | 
			
		||||
            imageLocation,
 | 
			
		||||
        ).joinToString(File.separator)
 | 
			
		||||
 | 
			
		||||
        val contentValues = contentValuesOf(
 | 
			
		||||
            MediaStore.Images.Media.RELATIVE_PATH to relativePath,
 | 
			
		||||
            MediaStore.Images.Media.DISPLAY_NAME to image.name,
 | 
			
		||||
            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(
 | 
			
		||||
                pictureDir,
 | 
			
		||||
                contentValues,
 | 
			
		||||
@@ -74,49 +102,34 @@ class ImageSaver(
 | 
			
		||||
        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)
 | 
			
		||||
    private fun findUriOrDefault(relativePath: String, imagePath: String, default: () -> Uri): Uri {
 | 
			
		||||
    private fun findUriOrDefault(path: String, filename: String, default: () -> Uri): Uri {
 | 
			
		||||
        val projection = arrayOf(
 | 
			
		||||
            MediaStore.MediaColumns._ID,
 | 
			
		||||
            MediaStore.MediaColumns.DISPLAY_NAME,
 | 
			
		||||
            MediaStore.Images.Media.MIME_TYPE,
 | 
			
		||||
            MediaStore.MediaColumns.RELATIVE_PATH,
 | 
			
		||||
            MediaStore.MediaColumns.DATE_MODIFIED,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        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(
 | 
			
		||||
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
 | 
			
		||||
            projection,
 | 
			
		||||
            selection,
 | 
			
		||||
            arrayOf(relativePath, imagePath),
 | 
			
		||||
            arrayOf(normalizedPath, filename),
 | 
			
		||||
            null,
 | 
			
		||||
        ).use { cursor ->
 | 
			
		||||
            if (cursor != null && cursor.count >= 1) {
 | 
			
		||||
                cursor.moveToFirst().let {
 | 
			
		||||
                if (cursor.moveToFirst()) {
 | 
			
		||||
                    val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
 | 
			
		||||
 | 
			
		||||
                    return ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return default()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user