Repository: thunderbird/thunderbird-android
File: core/logging/impl-file/src/androidMain/kotlin/net/thunderbird/core/logging/file/AndroidFileLogSink.kt (Kotlin)

Code before:
            mutex.withLock {
                content = accumulatedLogs.joinToString("\n", postfix = "\n")
                accumulatedLogs.clear()
            }
            val buffer = Buffer()
            val contentBytes = content.toByteArray(Charsets.UTF_8)
            buffer.write(contentBytes)
            sink.write(buffer, buffer.size)

            sink.flush()
        } catch (e: Exception) {
            throw e
        } finally {
            sink.close()
            outputStream.close()
        }
    }

    override suspend fun flushAndCloseBuffer() {
        if (accumulatedLogs.isNotEmpty()) {
            writeToLogFile()
        }
    }

    override fun export(uriString: String) {
        runBlocking {
            if (accumulatedLogs.isNotEmpty()) {
                writeToLogFile()
            }
            try {
                val sink = fileSystemManager.openSink(uriString, "wt")
                    ?: error("Error opening contentUri for writing")

                copyInternalFileToExternal(sink)

                // Clear the log file after export
                val outputStream = FileOutputStream(logFile)
                val clearSink = outputStream.asSink()

                try {
                    // Write empty string to clear the file
                    val buffer = Buffer()
                    clearSink.write(buffer, 0)
                    clearSink.flush()
                } finally {
                    clearSink.close()
                    outputStream.close()
                }
            } catch (e: Exception) {
                throw e
            }

Review comment (bug):
Instead of using `runBlocking`, we should turn the `export` function into a suspend function.

Currently the export function is being used in two scenarios:
1. Within `GeneralSettingsViewModel`, which can launch a coroutine
2. Within `syncDebugWorker`, which could launch a coroutine if extending from `CoroutineWorker` instead of `Worker`

Code after:
            buffer.write(contentBytes)
            sink.write(buffer, buffer.size)

            sink.flush()
        } finally {
            sink.close()
            outputStream.close()
        }
    }

    override suspend fun flushAndCloseBuffer() {
        if (accumulatedLogs.isNotEmpty()) {
            writeToLogFile()
        }
    }

    override suspend fun export(uriString: String) {
        if (accumulatedLogs.isNotEmpty()) {
            writeToLogFile()
        }
        val sink = fileSystemManager.openSink(uriString, "wt")
            ?: error("Error opening contentUri for writing")

        copyInternalFileToExternal(sink)

        // Clear the log file after export
        val outputStream = FileOutputStream(logFile)
        val clearSink = outputStream.asSink()

        try {
            // Write empty string to clear the file
            val buffer = Buffer()
            clearSink.write(buffer, 0)
            clearSink.flush()
        } finally {
            clearSink.close()
            outputStream.close()
        }
    }

    private fun copyInternalFileToExternal(sink: RawSink) {
        val inputStream = FileInputStream(logFile)

        try {
            val buffer = Buffer()
            val byteArray = ByteArray(BUFFER_SIZE)
            var bytesRead: Int

            while (inputStream.read(byteArray).also { bytesRead = it } != -1) {
                buffer.write(byteArray, 0, bytesRead)
                sink.write(buffer, buffer.size)