diff --git a/android/src/main/kotlin/com/ko2ic/imagedownloader/ImageDownloaderPermissionListener.kt b/android/src/main/kotlin/com/ko2ic/imagedownloader/ImageDownloaderPermissionListener.kt index 92ffc45..f773c36 100644 --- a/android/src/main/kotlin/com/ko2ic/imagedownloader/ImageDownloaderPermissionListener.kt +++ b/android/src/main/kotlin/com/ko2ic/imagedownloader/ImageDownloaderPermissionListener.kt @@ -6,10 +6,25 @@ import android.content.pm.PackageManager import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import io.flutter.plugin.common.PluginRegistry +import android.os.Build class ImageDownloaderPermissionListener(private val activity: Activity) : PluginRegistry.RequestPermissionsResultListener { + private val storagePermissions = mutableListOf().apply { + val readStoragePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + Manifest.permission.READ_MEDIA_IMAGES + } else { + Manifest.permission.READ_EXTERNAL_STORAGE + } + + this.add(readStoragePermission) + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + this.add(Manifest.permission.WRITE_EXTERNAL_STORAGE) + } + } + private val permissionRequestId: Int = 2578166 var callback: Callback? = null @@ -18,7 +33,7 @@ class ImageDownloaderPermissionListener(private val activity: Activity) : requestCode: Int, permissions: Array, grantResults: IntArray ): Boolean { - if (!isPermissionGranted(permissions)) { + if (!isStoragePermissionGranted()) { // when select deny. callback?.denied() return false @@ -37,21 +52,19 @@ class ImageDownloaderPermissionListener(private val activity: Activity) : } fun alreadyGranted(): Boolean { - val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) - - if (!isPermissionGranted(permissions)) { + if (!isStoragePermissionGranted()) { // Request authorization. User is not yet authorized. - ActivityCompat.requestPermissions(activity, permissions, permissionRequestId) + ActivityCompat.requestPermissions(activity, storagePermissions.toTypedArray(), permissionRequestId) return false } // User already has authorization. Or below Android6.0 return true } - private fun isPermissionGranted(permissions: Array) = permissions.none { - ContextCompat.checkSelfPermission( - activity, it - ) != PackageManager.PERMISSION_GRANTED + fun isStoragePermissionGranted(): Boolean { + return storagePermissions.all { + activity.checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED + } } interface Callback { diff --git a/android/src/main/kotlin/com/ko2ic/imagedownloader/ImageDownloaderPlugin.kt b/android/src/main/kotlin/com/ko2ic/imagedownloader/ImageDownloaderPlugin.kt index 87473a5..e640927 100644 --- a/android/src/main/kotlin/com/ko2ic/imagedownloader/ImageDownloaderPlugin.kt +++ b/android/src/main/kotlin/com/ko2ic/imagedownloader/ImageDownloaderPlugin.kt @@ -33,6 +33,7 @@ import java.io.FileInputStream import java.net.URLConnection import java.text.SimpleDateFormat import java.util.* +import kotlin.random.Random class ImageDownloaderPlugin : FlutterPlugin, ActivityAware, MethodCallHandler { companion object { @@ -289,9 +290,12 @@ class ImageDownloaderPlugin : FlutterPlugin, ActivityAware, MethodCallHandler { val inPublicDir = call.argument("inPublicDir") ?: true val directoryType = call.argument("directory") ?: "DIRECTORY_DOWNLOADS" val subDirectory = call.argument("subDirectory") - val tempSubDirectory = subDirectory ?: SimpleDateFormat( - "yyyy-MM-dd.HH.mm.sss", Locale.getDefault() - ).format(Date()) + + // add a random number to avoid conflicts, because when i downloaded multiple files so fast, the last file overwrites the previous ones + val tempSubDirectory = subDirectory ?: "${SimpleDateFormat( + "yyyy-MM-dd.HH.mm.sss", + Locale.getDefault() + ).format(Date())}${Random.nextInt(1000)}" val directory = convertToDirectory(directoryType) @@ -334,6 +338,10 @@ class ImageDownloaderPlugin : FlutterPlugin, ActivityAware, MethodCallHandler { channel.invokeMethod("onProgressUpdate", args) } } + // fix error + // 'when' expression must be exhaustive, add necessary 'is Pending', 'is Successful' branches or 'else' branch instead + is Downloader.DownloadStatus.Successful -> Log.d(LOGGER_TAG, "Successful") + is Downloader.DownloadStatus.Pending -> Log.d(LOGGER_TAG, "Pending") else -> throw AssertionError() } @@ -354,6 +362,15 @@ class ImageDownloaderPlugin : FlutterPlugin, ActivityAware, MethodCallHandler { null ) } else { + if (!file.canRead()) { + // it appears that the file is not readable, might the device is busy ( this happens when I try downloaded multiple files so fast) + result.error( + "read_failed", + "Couldn't read ${file.absolutePath ?: tempSubDirectory} ", + null + ) + return@execute // return early + } val stream = BufferedInputStream(FileInputStream(file)) val mimeType = outputMimeType ?: URLConnection.guessContentTypeFromStream(stream) @@ -377,6 +394,12 @@ class ImageDownloaderPlugin : FlutterPlugin, ActivityAware, MethodCallHandler { .getMimeTypeFromExtension(newFile.extension) ?: "" val imageId = saveToDatabase(newFile, mimeType ?: newMimeType, inPublicDir) + if (imageId == "") { + // in case the file was still downloaded but not saved to the gallery content provider, i don't know how to handle this case, someone might help :)) + + // result.error("save_to_database_failed", "Couldn't save the file to the database.", null) + // return@execute + } result.success(imageId) } }) @@ -420,8 +443,12 @@ class ImageDownloaderPlugin : FlutterPlugin, ActivityAware, MethodCallHandler { null ).use { checkNotNull(it) { "${file.absolutePath} is not found." } - it.moveToFirst() - it.getString(it.getColumnIndex(MediaStore.Images.Media._ID)) + if (it.moveToFirst()) { + it.getString(it.getColumnIndex(MediaStore.Images.Media._ID)) + } else { + // it appears that the cursor is empty + "" + } } } else { val db = TemporaryDatabase(context)