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

Code before:

        val saveFolderPath = savesFolderRoot + item.titleId
        val saveExists = File(saveFolderPath).exists()

        binding.deleteSave.isEnabled = saveExists
        binding.deleteSave.setOnClickListener {
            AlertDialog.Builder(requireContext())
                .setTitle(getString(R.string.delete_save_confirmation_message))
                .setMessage(getString(R.string.action_irreversible))
                .setPositiveButton(getString(R.string.yes)) { dialogInterface, _ ->
                    File(saveFolderPath).deleteRecursively()
                    binding.deleteSave.isEnabled = false
                    binding.exportSave.isEnabled = false
                    dialogInterface.dismiss()
                }.setNegativeButton(getString(R.string.no)) { dialogInterface, _ ->
                    dialogInterface.cancel()
                }.show()
        }

        binding.importSave.setOnClickListener {
            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) {

Review comment (suggestion):
CoroutineScope(Dispatchers.IO).launch(Dispatchers.IO) resolves warnings about potential thread starvation

Code after:
            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()
                true
            } else {
                false
            }
        }
    }

    /**
     * Zips the save file located in the given folder path and creates a new zip file with the name and version of the game, and the current date and time.
     * @param saveFolderPath The path to the folder containing the save file to zip.
     * @return true if the zip file is successfully created, false otherwise.
     */
    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.