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 (bug):
Showing Java exception messages is bad, and not useful to the user at all. Either enumerate all the possible failure points each with a proper error string, or show a generic failure message.

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"))
            }