diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt index 2ff34dd99..2355d9dcb 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsDataScreen.kt @@ -52,6 +52,7 @@ import eu.kanade.presentation.util.relativeTimeSpanString import eu.kanade.tachiyomi.data.backup.create.BackupCreateJob import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob import eu.kanade.tachiyomi.data.cache.ChapterCache +import eu.kanade.tachiyomi.data.export.LibraryExporter import eu.kanade.tachiyomi.util.system.DeviceUtil import eu.kanade.tachiyomi.util.system.toast import kotlinx.collections.immutable.persistentListOf @@ -338,28 +339,20 @@ object SettingsDataScreen : SearchableSettings { val favoritesFlow = remember { flow { emit(getFavorites.await()) } } val favoritesState by favoritesFlow.collectAsState(emptyList()) + val libraryExporter = LibraryExporter() + val saveFileLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.CreateDocument("text/csv"), ) { uri -> uri?.let { - coroutineScope.launch { - context.contentResolver.openOutputStream(uri)?.use { outputStream -> - val csvData = buildString { - favoritesState.forEach { manga -> - val title = if (titleSelected) escapeCsvField(manga.title) else "" - val author = if (authorSelected) escapeCsvField(manga.author ?: "") else "" - val artist = if (artistSelected) escapeCsvField(manga.artist ?: "") else "" - val row = listOf(title, author, artist).filter { - it.isNotEmpty() - }.joinToString(",") { "\"$it\"" } - appendLine(row) - } - } - outputStream.write(csvData.toByteArray()) - outputStream.flush() - - context.toast(MR.strings.library_exported) - } + libraryExporter.exportToCsv( + context, + it, + favoritesState, + LibraryExporter.ExportOptions(titleSelected, authorSelected, artistSelected), + coroutineScope + ) { + context.toast(MR.strings.library_exported) } } } @@ -390,13 +383,6 @@ object SettingsDataScreen : SearchableSettings { ) } - private fun escapeCsvField(field: String): String { - return field - .replace("\"", "\"\"") - .replace("\r\n", "\n") - .replace("\r", "\n") - } - @Composable private fun ColumnSelectionDialog( onDismissRequest: () -> Unit, diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/export/LibraryExporter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/export/LibraryExporter.kt new file mode 100644 index 000000000..82a3ef9dd --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/data/export/LibraryExporter.kt @@ -0,0 +1,55 @@ +package eu.kanade.tachiyomi.data.export + +import android.content.Context +import android.net.Uri +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import tachiyomi.domain.manga.model.Manga + +class LibraryExporter { + + data class ExportOptions( + val includeTitle: Boolean, + val includeAuthor: Boolean, + val includeArtist: Boolean + ) + + fun exportToCsv( + context: Context, + uri: Uri, + favorites: List, + options: ExportOptions, + coroutineScope: CoroutineScope, + onExportComplete: () -> Unit + ) { + coroutineScope.launch { + context.contentResolver.openOutputStream(uri)?.use { outputStream -> + val csvData = generateCsvData(favorites, options) + outputStream.write(csvData.toByteArray()) + outputStream.flush() + onExportComplete() + } + } + } + + private fun generateCsvData(favorites: List, options: ExportOptions): String { + val stringBuilder = StringBuilder() + favorites.forEach { manga -> + val row = mutableListOf() + if (options.includeTitle) row.add(escapeCsvField(manga.title)) + if (options.includeAuthor) row.add(escapeCsvField(manga.author ?: "")) + if (options.includeArtist) row.add(escapeCsvField(manga.artist ?: "")) + if (row.isNotEmpty()) { + stringBuilder.appendLine(row.joinToString(",") { "\"$it\"" }) + } + } + return stringBuilder.toString() + } + + private fun escapeCsvField(field: String): String { + return field + .replace("\"", "\"\"") + .replace("\r\n", "\n") + .replace("\r", "\n") + } +}