Repository: skyline-emu/skyline
File: app/src/main/java/emu/skyline/AppDialog.kt (Kotlin)

Code before:
            documentPicker.launch(arrayOf("application/zip"))
        }

        binding.exportSave.isEnabled = saveExists
        binding.exportSave.setOnClickListener {
            CoroutineScope(Dispatchers.IO).launch {
                try {
                    val saveFolder = File(saveFolderPath)
                    val outputZipFile = File("$savesFolderRoot${item.title} (v${binding.gameVersion.text}) [${item.titleId}] - ${LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))}.zip")
                    lastZipCreated?.delete()
                    outputZipFile.createNewFile()
                    ZipOutputStream(BufferedOutputStream(FileOutputStream(outputZipFile))).use { zos ->
                        saveFolder.walkTopDown().forEach { file ->
                            val zipFileName = file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/")
                            if (zipFileName == "") return@forEach
                            val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}")
                            zos.putNextEntry(entry)
                            if (file.isFile) {
                                file.inputStream().use { fis -> fis.copyTo(zos) }
                            }
                        }
                    }
                    lastZipCreated = outputZipFile
                } catch (e: Exception) {
                    withContext(Dispatchers.Main){
                        Snackbar.make(binding.root, e.message as CharSequence, Snackbar.LENGTH_LONG).show()
                    }
                    return@launch
                }

                withContext(Dispatchers.Main) {
                    val file = DocumentFile.fromSingleUri(requireContext(), DocumentsContract.buildDocumentUri(DocumentsProvider.AUTHORITY, "${DocumentsProvider.ROOT_ID}/switch/nand/user/save/0000000000000000/00000000000000000000000000000001/${lastZipCreated!!.name}"))!!
                    val intent = Intent(Intent.ACTION_SEND)
                        .setDataAndType(file.uri, "application/zip")
                        .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                        .putExtra(Intent.EXTRA_STREAM, file.uri)
                    startActivity(Intent.createChooser(intent, "Share save file"))
                }
            }
        }

        binding.gameTitleId.setOnLongClickListener {
            val clipboard = requireActivity().getSystemService(android.content.Context.CLIPBOARD_SERVICE) as android.content.ClipboardManager
            clipboard.setPrimaryClip(android.content.ClipData.newPlainText("Title ID", item.titleId))
            Snackbar.make(binding.root, getString(R.string.copied_to_clipboard), Snackbar.LENGTH_SHORT).show()
            true
        }

        dialog?.setOnKeyListener { _, keyCode, event ->
            if (keyCode == KeyEvent.KEYCODE_BUTTON_B && event.action == KeyEvent.ACTION_UP) {
                dialog?.onBackPressed()

Review comment (performance):
Don't keep a zip file laying around in the `savesFolderPath`, but use the app cache folder, in the same way the import function does. That way you don't need to destroy the file here, but you'd destroy it right after "returning" from the intent. See `startActivityForResult`.

Code after:
    private fun zipSave(saveFolderPath : String) : Boolean {
        try {
            val saveFolder = File(saveFolderPath)
            val outputZipFile = File("$savesFolderRoot${item.title} (v${binding.gameVersion.text}) [${item.titleId}] - ${LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))}.zip")
            lastZipCreated?.delete()
            outputZipFile.createNewFile()
            ZipOutputStream(BufferedOutputStream(FileOutputStream(outputZipFile))).use { zos ->
                saveFolder.walkTopDown().forEach { file ->
                    val zipFileName = file.absolutePath.removePrefix(savesFolderRoot).removePrefix("/")
                    if (zipFileName == "")
                        return@forEach
                    val entry = ZipEntry("$zipFileName${(if (file.isDirectory) "/" else "")}")
                    zos.putNextEntry(entry)
                    if (file.isFile)
                        file.inputStream().use { fis -> fis.copyTo(zos) }
                }
            }
            lastZipCreated = outputZipFile
        } catch (e : Exception) {
            return false
        }
        return true
    }

    private val startForResultExportSave = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
        lastZipCreated?.delete()
    }

    /**
     * Exports the save file located in the given folder path by creating a zip file and sharing it via intent.
     * @param saveFolderPath The path to the folder containing the save file(s) to export.
     */
    private fun exportSave(saveFolderPath : String) {
        CoroutineScope(Dispatchers.IO).launch {
            val wasZipCreated = zipSave(saveFolderPath)
            val lastZipFile = lastZipCreated
            if (!wasZipCreated || lastZipFile == null) {
                withContext(Dispatchers.Main) {
                    Snackbar.make(binding.root, R.string.error, Snackbar.LENGTH_LONG).show()
                }
                return@launch
            }

            withContext(Dispatchers.Main) {
                val file = DocumentFile.fromSingleUri(requireContext(), DocumentsContract.buildDocumentUri(DocumentsProvider.AUTHORITY, "${DocumentsProvider.ROOT_ID}/switch/nand/user/save/0000000000000000/00000000000000000000000000000001/${lastZipFile.name}"))!!
                val intent = Intent(Intent.ACTION_SEND)
                    .setDataAndType(file.uri, "application/zip")
                    .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                    .putExtra(Intent.EXTRA_STREAM, file.uri)
                startForResultExportSave.launch(Intent.createChooser(intent, "Share save file"))
            }