tauri-plugin-medialibrary 0.12.0

A tauri plugin to access the systems media library (e.g. the android medialibrary)
Documentation
package de.universalappfactory.medialibrary

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.RecoverableSecurityException
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.activity.result.ActivityResult
import androidx.activity.result.IntentSenderRequest
import app.tauri.annotation.ActivityCallback
import app.tauri.annotation.Command
import app.tauri.annotation.Permission
import app.tauri.annotation.PermissionCallback
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.Invoke
import app.tauri.plugin.JSArray
import app.tauri.plugin.JSObject
import app.tauri.plugin.Plugin

private const val EXTERNAL_STORAGE_ALIAS = "externalStorage"
private const val WRITE_EXTERNAL_STORAGE_ALIAS = "writeExternalStorage"
private const val MEDIA_IMAGES_ALIAS = "readMediaImages"

@TauriPlugin(
        permissions =
                [
                        Permission(
                                strings = [Manifest.permission.READ_EXTERNAL_STORAGE],
                                alias = EXTERNAL_STORAGE_ALIAS
                        ),
                        Permission(
                                strings = [Manifest.permission.WRITE_EXTERNAL_STORAGE],
                                alias = WRITE_EXTERNAL_STORAGE_ALIAS
                        ),
                        Permission(
                                strings = ["android.permission.READ_MEDIA_IMAGES"],
                                alias = MEDIA_IMAGES_ALIAS
                        )]
)
class MediaLibraryPlugin(private val activity: Activity) : Plugin(activity) {

    private var requestPermissionResponse: JSObject? = null
    private var deleteRequestInvoke: Invoke? = null

    companion object {
        private const val DELETE_REQUEST_CODE = 1001
        private const val RECOVERABLE_DELETE_REQUEST_CODE = 1002
    }

    @Command
    fun getImages(invoke: Invoke) {
        val args = invoke.parseArgs(GetImagesArgs::class.java)

        val mediaLibaray = MediaLibrary(activity.contentResolver, activity)

        val ret = JSObject()
        ret.put("items", JSArray(mediaLibaray.getAllImages(args)))
        invoke.resolve(ret)
    }

    @Command
    fun getImage(invoke: Invoke) {
        val args = invoke.parseArgs(GetImageArgs::class.java)

        val mediaLibaray = MediaLibrary(activity.contentResolver, activity)
        val ret = mediaLibaray.getImage(args.uri)
        invoke.resolve(ret)
    }

    @Command
    fun executeRecoverableDeleteRequest(invoke: Invoke) {
        val args = invoke.parseArgs(DeleteImageArgs::class.java)
        try {
            val uri = Uri.parse(args.uri)

            try {
                val mediaLibaray = MediaLibrary(activity.contentResolver, activity)
                val result = mediaLibaray.deleteImage(args.uri)
                invoke.resolve(result)
            } catch (securityException: RecoverableSecurityException) {
                val urisToDelete = listOf(uri)
                val pendingIntent =
                        MediaStore.createDeleteRequest(activity.contentResolver, urisToDelete)

                val request = IntentSenderRequest.Builder(pendingIntent.getIntentSender()).build()
                startIntentSenderForResult(invoke, request, "deleteActivityResult")
            }
        } catch (e: Exception) {
            invoke.reject("Failed to handle recoverable delete request: ${e.message}")
        }
    }

    @ActivityCallback
    fun deleteActivityResult(invoke: Invoke, result: ActivityResult) {
        val resultCode = result.resultCode
        if (resultCode == Activity.RESULT_CANCELED) {
            invoke.reject(
                    "deleteActivityResult canceled",
            )
        } else {
            invoke.resolve()
        }
    }

    @Command
    fun getThumbnailAsBase64(invoke: Invoke) {
        val args = invoke.parseArgs(GetThumbnailArgs::class.java)

        // requestPermissions(invoke)
        val mediaLibaray = MediaLibrary(activity.contentResolver, activity)

        val uri = Uri.parse(args.uri)
        val content = mediaLibaray.getThumbnailAsBase64(uri)

        val ret = JSObject()
        ret.put("content", content ?: "")
        invoke.resolve(ret)
    }

    @PermissionCallback
    private fun storagePermissionCallback(invoke: Invoke) {
        val permissionsResultJSON = JSObject()
        permissionsResultJSON.put("postNotification", getPermissionState(EXTERNAL_STORAGE_ALIAS))
        invoke.resolve(permissionsResultJSON)
    }

    @SuppressLint("ObsoleteSdkInt")
    @Command
    override fun requestPermissions(invoke: Invoke) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
            requestPermissionForAlias(EXTERNAL_STORAGE_ALIAS, invoke, "storagePermissionCallback")
        } else {
            requestPermissionForAlias(MEDIA_IMAGES_ALIAS, invoke, "storagePermissionCallback")
        }
    }
}