diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/FileParserImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/FileParserImpl.kt index f8547aca..9e274f58 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/FileParserImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/FileParserImpl.kt @@ -2,8 +2,11 @@ package ua.acclorite.book_story.data.parser import ua.acclorite.book_story.data.parser.epub.EpubFileParser import ua.acclorite.book_story.data.parser.fb2.Fb2FileParser +import ua.acclorite.book_story.data.parser.htm.HtmFileParser +import ua.acclorite.book_story.data.parser.html.HtmlFileParser import ua.acclorite.book_story.data.parser.pdf.PdfFileParser import ua.acclorite.book_story.data.parser.txt.TxtFileParser +import ua.acclorite.book_story.data.parser.zip.ZipFileParser import ua.acclorite.book_story.domain.model.Book import ua.acclorite.book_story.domain.util.CoverImage import java.io.File @@ -13,32 +16,48 @@ class FileParserImpl @Inject constructor( private val txtFileParser: TxtFileParser, private val pdfFileParser: PdfFileParser, private val epubFileParser: EpubFileParser, - private val fb2FileParser: Fb2FileParser + private val fb2FileParser: Fb2FileParser, + private val zipFileParser: ZipFileParser, + private val htmlFileParser: HtmlFileParser, + private val htmFileParser: HtmFileParser, ) : FileParser { override suspend fun parse(file: File): Pair? { if (!file.exists()) { return null } - val fileFormat = ".${file.name.substringAfterLast(".")}".lowercase().trim() + val fileFormat = ".${file.extension}".lowercase().trim() + return when (fileFormat) { + ".pdf" -> { + pdfFileParser.parse(file) + } - if (fileFormat == ".pdf") { - return pdfFileParser.parse(file) + ".epub" -> { + epubFileParser.parse(file) + } + + ".txt" -> { + txtFileParser.parse(file) + } + + ".fb2" -> { + fb2FileParser.parse(file) + } + + ".zip" -> { + zipFileParser.parse(file) + } + + ".html" -> { + htmlFileParser.parse(file) + } + + ".htm" -> { + htmFileParser.parse(file) + } + + else -> null } - - if (fileFormat == ".epub") { - return epubFileParser.parse(file) - } - - if (fileFormat == ".txt") { - return txtFileParser.parse(file) - } - - if (fileFormat == ".fb2") { - return fb2FileParser.parse(file) - } - - return null } } diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/TextParserImpl.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/TextParserImpl.kt index ef7d0ad2..f6ee3e48 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/TextParserImpl.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/TextParserImpl.kt @@ -1,10 +1,12 @@ package ua.acclorite.book_story.data.parser import ua.acclorite.book_story.R -import ua.acclorite.book_story.data.parser.epub.EpubTextParser import ua.acclorite.book_story.data.parser.fb2.Fb2TextParser +import ua.acclorite.book_story.data.parser.htm.HtmTextParser +import ua.acclorite.book_story.data.parser.html.HtmlTextParser import ua.acclorite.book_story.data.parser.pdf.PdfTextParser import ua.acclorite.book_story.data.parser.txt.TxtTextParser +import ua.acclorite.book_story.data.parser.zip.ZipTextParser import ua.acclorite.book_story.domain.util.Resource import ua.acclorite.book_story.domain.util.UIText import java.io.File @@ -13,8 +15,11 @@ import javax.inject.Inject class TextParserImpl @Inject constructor( private val txtTextParser: TxtTextParser, private val pdfTextParser: PdfTextParser, - private val epubTextParser: EpubTextParser, - private val fb2TextParser: Fb2TextParser + private val epubTextParser: HtmlTextParser, + private val fb2TextParser: Fb2TextParser, + private val zipTextParser: ZipTextParser, + private val htmlTextParser: HtmlTextParser, + private val htmTextParser: HtmTextParser, ) : TextParser { override suspend fun parse(file: File): Resource> { if (!file.exists()) { @@ -23,25 +28,38 @@ class TextParserImpl @Inject constructor( ) } - val fileFormat = ".${file.name.substringAfterLast(".")}".lowercase().trim() + val fileFormat = ".${file.extension}".lowercase().trim() + return when (fileFormat) { + ".pdf" -> { + pdfTextParser.parse(file) + } - if (fileFormat == ".pdf") { - return pdfTextParser.parse(file) + ".epub" -> { + epubTextParser.parse(file) + } + + ".txt" -> { + txtTextParser.parse(file) + } + + ".fb2" -> { + fb2TextParser.parse(file) + } + + ".zip" -> { + zipTextParser.parse(file) + } + + ".html" -> { + htmlTextParser.parse(file) + } + + ".htm" -> { + htmTextParser.parse(file) + } + + else -> Resource.Error(UIText.StringResource(R.string.error_wrong_file_format)) } - - if (fileFormat == ".epub") { - return epubTextParser.parse(file) - } - - if (fileFormat == ".txt") { - return txtTextParser.parse(file) - } - - if (fileFormat == ".fb2") { - return fb2TextParser.parse(file) - } - - return Resource.Error(UIText.StringResource(R.string.error_wrong_file_format)) } } diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt index 753a8f50..b58393d5 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubFileParser.kt @@ -5,6 +5,7 @@ import android.graphics.BitmapFactory import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import org.jsoup.Jsoup +import ua.acclorite.book_story.R import ua.acclorite.book_story.data.parser.FileParser import ua.acclorite.book_story.domain.model.Book import ua.acclorite.book_story.domain.model.Category @@ -36,30 +37,43 @@ class EpubFileParser @Inject constructor() : FileParser { .use { it.readText() } val document = Jsoup.parse(opfContent) - val title = document.select("metadata > dc|title").text().trim() - val author = UIText.StringValue( - document.select("metadata > dc|creator").text().trim() - ) + val title = document.select("metadata > dc|title").text().trim().run { + ifBlank { + file.nameWithoutExtension.trim() + } + } + + val author = document.select("metadata > dc|creator").text().trim().run { + if (isBlank()) { + UIText.StringResource(R.string.unknown_author) + } else { + UIText.StringValue(this) + } + } + val description = Jsoup.parse( document.select("metadata > dc|description").text() - ).text() + ).text().run { + ifBlank { + null + } + } - var coverImagePath: String? = null - - val coverId = document + val coverImage = document .select("metadata > meta[name=cover]") .attr("content") - if (coverId.isNotBlank()) { - coverImagePath = document - .select("manifest > item[id=$coverId]") - .attr("href") - } + .run { + if (isNotBlank()) { + document + .select("manifest > item[id=$this]") + .attr("href") + .apply { if (isNotBlank()) return@run this } + } - if (coverImagePath.isNullOrBlank()) { - coverImagePath = document - .select("manifest > item[media-type*=image]") - .firstOrNull()?.attr("href") - } + document + .select("manifest > item[media-type*=image]") + .firstOrNull()?.attr("href") + } book = Book( title = title, @@ -73,7 +87,7 @@ class EpubFileParser @Inject constructor() : FileParser { lastOpened = null, category = Category.entries[0], coverImage = null - ) to extractCoverImageBitmap(file, coverImagePath) + ) to extractCoverImageBitmap(file, coverImage) } } return book diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2FileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2FileParser.kt index d03698b4..8fbf2a3f 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2FileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2FileParser.kt @@ -29,7 +29,7 @@ class Fb2FileParser @Inject constructor() : FileParser { } val titleFromFile = extractElementContent(document, "book-title") - val title = titleFromFile ?: file.name.dropLast(4).trim() + val title = titleFromFile ?: file.nameWithoutExtension.trim() val authorFirstName = extractElementContent(document, "first-name") val authorLastName = extractElementContent(document, "last-name") diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/htm/HtmFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/htm/HtmFileParser.kt new file mode 100644 index 00000000..1afb80f4 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/htm/HtmFileParser.kt @@ -0,0 +1,47 @@ +package ua.acclorite.book_story.data.parser.htm + +import org.jsoup.Jsoup +import ua.acclorite.book_story.R +import ua.acclorite.book_story.data.parser.FileParser +import ua.acclorite.book_story.domain.model.Book +import ua.acclorite.book_story.domain.model.Category +import ua.acclorite.book_story.domain.util.CoverImage +import ua.acclorite.book_story.domain.util.UIText +import java.io.File +import javax.inject.Inject + +class HtmFileParser @Inject constructor() : FileParser { + + override suspend fun parse(file: File): Pair? { + if (!file.name.endsWith(".htm", true) || !file.exists()) { + return null + } + + try { + val document = Jsoup.parse(file) + + val title = document.select("head > title").text().trim().run { + ifBlank { + file.nameWithoutExtension.trim() + } + } + + return Book( + title = title, + author = UIText.StringResource(R.string.unknown_author), + description = null, + textPath = "", + scrollIndex = 0, + scrollOffset = 0, + progress = 0f, + filePath = file.path, + lastOpened = null, + category = Category.entries[0], + coverImage = null + ) to null + } catch (e: Exception) { + e.printStackTrace() + return null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/htm/HtmTextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/htm/HtmTextParser.kt new file mode 100644 index 00000000..4d201b33 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/htm/HtmTextParser.kt @@ -0,0 +1,48 @@ +package ua.acclorite.book_story.data.parser.htm + +import org.jsoup.Jsoup +import ua.acclorite.book_story.R +import ua.acclorite.book_story.data.parser.TextParser +import ua.acclorite.book_story.domain.util.Resource +import ua.acclorite.book_story.domain.util.UIText +import java.io.File +import javax.inject.Inject + + +class HtmTextParser @Inject constructor() : TextParser { + + override suspend fun parse(file: File): Resource> { + if (!file.name.endsWith(".htm", true) || !file.exists()) { + return Resource.Error(UIText.StringResource(R.string.error_wrong_file_format)) + } + + try { + val lines = mutableListOf() + + val document = Jsoup.parse(file) + document.select("p").append("\n") + document + .wholeText() + .lines() + .forEach { line -> + if (line.isNotBlank()) { + lines.add(line.trim()) + } + } + + if (lines.isEmpty()) { + return Resource.Error(UIText.StringResource(R.string.error_file_empty)) + } + + return Resource.Success(lines) + } catch (e: Exception) { + e.printStackTrace() + return Resource.Error( + UIText.StringResource( + R.string.error_query, + e.message?.take(40)?.trim() ?: "" + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlFileParser.kt new file mode 100644 index 00000000..be36e80b --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlFileParser.kt @@ -0,0 +1,47 @@ +package ua.acclorite.book_story.data.parser.html + +import org.jsoup.Jsoup +import ua.acclorite.book_story.R +import ua.acclorite.book_story.data.parser.FileParser +import ua.acclorite.book_story.domain.model.Book +import ua.acclorite.book_story.domain.model.Category +import ua.acclorite.book_story.domain.util.CoverImage +import ua.acclorite.book_story.domain.util.UIText +import java.io.File +import javax.inject.Inject + +class HtmlFileParser @Inject constructor() : FileParser { + + override suspend fun parse(file: File): Pair? { + if (!file.name.endsWith(".html", true) || !file.exists()) { + return null + } + + try { + val document = Jsoup.parse(file) + + val title = document.select("head > title").text().trim().run { + ifBlank { + file.nameWithoutExtension.trim() + } + } + + return Book( + title = title, + author = UIText.StringResource(R.string.unknown_author), + description = null, + textPath = "", + scrollIndex = 0, + scrollOffset = 0, + progress = 0f, + filePath = file.path, + lastOpened = null, + category = Category.entries[0], + coverImage = null + ) to null + } catch (e: Exception) { + e.printStackTrace() + return null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlTextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlTextParser.kt new file mode 100644 index 00000000..ed21c264 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/html/HtmlTextParser.kt @@ -0,0 +1,48 @@ +package ua.acclorite.book_story.data.parser.html + +import org.jsoup.Jsoup +import ua.acclorite.book_story.R +import ua.acclorite.book_story.data.parser.TextParser +import ua.acclorite.book_story.domain.util.Resource +import ua.acclorite.book_story.domain.util.UIText +import java.io.File +import javax.inject.Inject + + +class HtmlTextParser @Inject constructor() : TextParser { + + override suspend fun parse(file: File): Resource> { + if (!file.name.endsWith(".html", true) || !file.exists()) { + return Resource.Error(UIText.StringResource(R.string.error_wrong_file_format)) + } + + try { + val lines = mutableListOf() + + val document = Jsoup.parse(file) + document.select("p").append("\n") + document + .wholeText() + .lines() + .forEach { line -> + if (line.isNotBlank()) { + lines.add(line.trim()) + } + } + + if (lines.isEmpty()) { + return Resource.Error(UIText.StringResource(R.string.error_file_empty)) + } + + return Resource.Success(lines) + } catch (e: Exception) { + e.printStackTrace() + return Resource.Error( + UIText.StringResource( + R.string.error_query, + e.message?.take(40)?.trim() ?: "" + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/pdf/PdfFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/pdf/PdfFileParser.kt index c66e5f10..b1b8c032 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/pdf/PdfFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/pdf/PdfFileParser.kt @@ -24,10 +24,11 @@ class PdfFileParser @Inject constructor(private val application: Application) : val document = PDDocument.load(file) - val title = document.documentInformation.title ?: file.name.dropLast(4).trim() - val fileAuthor = document.documentInformation.author - val author = if (fileAuthor != null) UIText.StringValue(fileAuthor) - else UIText.StringResource(R.string.unknown_author) + val title = document.documentInformation.title ?: file.nameWithoutExtension.trim() + val author = document.documentInformation.author.run { + if (isNullOrBlank()) UIText.StringResource(R.string.unknown_author) + else UIText.StringValue(this) + } val description = document.documentInformation.subject ?: null document.close() diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/txt/TxtFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/txt/TxtFileParser.kt index 3588838f..074d4eca 100644 --- a/app/src/main/java/ua/acclorite/book_story/data/parser/txt/TxtFileParser.kt +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/txt/TxtFileParser.kt @@ -17,7 +17,7 @@ class TxtFileParser @Inject constructor() : FileParser { } try { - val title = file.name.trim().dropLast(4) + val title = file.nameWithoutExtension.trim() val author = UIText.StringResource(R.string.unknown_author) return Book( diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/zip/ZipFileParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/zip/ZipFileParser.kt new file mode 100644 index 00000000..6600897f --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/zip/ZipFileParser.kt @@ -0,0 +1,116 @@ +package ua.acclorite.book_story.data.parser.zip + +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.jsoup.Jsoup +import ua.acclorite.book_story.R +import ua.acclorite.book_story.data.parser.FileParser +import ua.acclorite.book_story.domain.model.Book +import ua.acclorite.book_story.domain.model.Category +import ua.acclorite.book_story.domain.util.CoverImage +import ua.acclorite.book_story.domain.util.UIText +import java.io.File +import java.util.zip.ZipFile +import javax.inject.Inject + +class ZipFileParser @Inject constructor() : FileParser { + + override suspend fun parse(file: File): Pair? { + if (!file.name.endsWith(".zip", true) || !file.exists()) { + return null + } + + try { + var book: Pair? = null + + withContext(Dispatchers.IO) { + ZipFile(file).use { zip -> + val opfEntry = zip.entries().asSequence().find { entry -> + entry.name.endsWith(".opf") + } ?: return@withContext + + val opfContent = zip + .getInputStream(opfEntry) + .bufferedReader() + .use { it.readText() } + val document = Jsoup.parse(opfContent) + + val title = document.select("metadata > dc|title").text().trim().run { + ifBlank { + file.nameWithoutExtension.trim() + } + } + + val author = document.select("metadata > dc|creator").text().trim().run { + if (isBlank()) { + UIText.StringResource(R.string.unknown_author) + } else { + UIText.StringValue(this) + } + } + + val description = Jsoup.parse( + document.select("metadata > dc|description").text() + ).text().run { + ifBlank { + null + } + } + + val coverImage = document + .select("metadata > meta[name=cover]") + .attr("content") + .run { + if (isNotBlank()) { + document + .select("manifest > item[id=$this]") + .attr("href") + .apply { if (isNotBlank()) return@run this } + } + + document + .select("manifest > item[media-type*=image]") + .firstOrNull()?.attr("href") + } + + book = Book( + title = title, + author = author, + description = description, + textPath = "", + scrollIndex = 0, + scrollOffset = 0, + progress = 0f, + filePath = file.path, + lastOpened = null, + category = Category.entries[0], + coverImage = null + ) to extractCoverImageBitmap(file, coverImage) + } + } + return book + } catch (e: Exception) { + e.printStackTrace() + return null + } + } +} + +private fun extractCoverImageBitmap(file: File, coverImagePath: String?): Bitmap? { + if (coverImagePath.isNullOrBlank()) { + return null + } + + ZipFile(file).use { zip -> + zip.entries().asSequence().forEach { entry -> + if (entry.name.endsWith(coverImagePath)) { + val imageBytes = zip.getInputStream(entry).readBytes() + return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size) + } + } + } + + return null +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/zip/ZipTextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/zip/ZipTextParser.kt new file mode 100644 index 00000000..791f6aac --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/data/parser/zip/ZipTextParser.kt @@ -0,0 +1,69 @@ +package ua.acclorite.book_story.data.parser.zip + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.jsoup.Jsoup +import ua.acclorite.book_story.R +import ua.acclorite.book_story.data.parser.TextParser +import ua.acclorite.book_story.domain.util.Resource +import ua.acclorite.book_story.domain.util.UIText +import java.io.File +import java.util.zip.ZipFile +import javax.inject.Inject + + +class ZipTextParser @Inject constructor() : TextParser { + + override suspend fun parse(file: File): Resource> { + if (!file.name.endsWith(".zip", true) || !file.exists()) { + return Resource.Error(UIText.StringResource(R.string.error_wrong_file_format)) + } + + try { + val lines = mutableListOf() + + withContext(Dispatchers.IO) { + ZipFile(file).use { zip -> + zip.entries().asSequence().forEach { entry -> + if ( + entry.name.endsWith(".xhtml") + || entry.name.endsWith(".html") + || entry.name.endsWith(".xml") + || entry.name.endsWith(".htm") + ) { + val content = zip.getInputStream(entry).bufferedReader() + .use { + it.readText() + } + + val document = Jsoup.parse(content) + document.select("p").append("\n") + document + .wholeText() + .lines() + .forEach { line -> + if (line.isNotBlank()) { + lines.add(line.trim()) + } + } + } + } + } + } + + if (lines.isEmpty()) { + return Resource.Error(UIText.StringResource(R.string.error_file_empty)) + } + + return Resource.Success(lines) + } catch (e: Exception) { + e.printStackTrace() + return Resource.Error( + UIText.StringResource( + R.string.error_query, + e.message?.take(40)?.trim() ?: "" + ) + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/domain/util/Constants.kt b/app/src/main/java/ua/acclorite/book_story/domain/util/Constants.kt index 158e1233..bb9764be 100644 --- a/app/src/main/java/ua/acclorite/book_story/domain/util/Constants.kt +++ b/app/src/main/java/ua/acclorite/book_story/domain/util/Constants.kt @@ -27,497 +27,521 @@ object Constants { const val MAIN_STATE = "main_state" // Supported file extensions - val EXTENSIONS = listOf(".epub", ".pdf", ".fb2", ".txt") + val EXTENSIONS = provideSupportedExtensions() // Supported languages - val LANGUAGES = listOf( - Pair("en", "English"), - Pair("uk", "Українська"), - ) + val LANGUAGES = provideLanguages() // Navigation items for NavigationBars. - val NAVIGATION_ITEMS = listOf( - NavigationItem( - screen = Screen.Library, - title = R.string.library_screen, - tooltip = R.string.library_content_desc, - selectedIcon = R.drawable.library_screen_filled, - unselectedIcon = R.drawable.library_screen_outlined - ), - NavigationItem( - screen = Screen.History, - title = R.string.history_screen, - tooltip = R.string.history_content_desc, - selectedIcon = R.drawable.history_screen_filled, - unselectedIcon = R.drawable.history_screen_outlined - ), - NavigationItem( - screen = Screen.Browse, - title = R.string.browse_screen, - tooltip = R.string.browse_content_desc, - selectedIcon = R.drawable.browse_screen_filled, - unselectedIcon = R.drawable.browse_screen_outlined - ) - ) + val NAVIGATION_ITEMS = provideNavigationItems() // Supported themes - val THEMES = listOf( - Pair(Theme.DYNAMIC, UIText.StringResource(R.string.dynamic_theme)), - Pair(Theme.BLUE, UIText.StringResource(R.string.blue_theme)), - Pair(Theme.GREEN, UIText.StringResource(R.string.green_theme)), - Pair(Theme.RED, UIText.StringResource(R.string.red_theme)), - Pair(Theme.PURPLE, UIText.StringResource(R.string.purple_theme)), - Pair(Theme.PINK, UIText.StringResource(R.string.pink_theme)), - Pair(Theme.YELLOW, UIText.StringResource(R.string.yellow_theme)), - Pair(Theme.AQUA, UIText.StringResource(R.string.aqua_theme)), - ) + val THEMES = provideThemes() // Credits - val CREDITS = listOf( - Credit( - name = "Tachiyomi (Mihon)", - source = "GitHub", - credits = listOf( - UIText.StringResource(R.string.credits_design), - UIText.StringValue("Readme") - ), - website = "https://www.github.com/mihonapp/mihon" - ), - Credit( - name = "Kitsune", - source = "GitHub", - credits = listOf( - UIText.StringResource(R.string.credits_updates) - ), - website = "https://www.github.com/Drumber/Kitsune" - ), - Credit( - name = "Material Design Icons", - source = "Google Fonts", - credits = listOf( - UIText.StringResource(R.string.credits_icon) - ), - website = "https://fonts.google.com/icons" - ), - ) + val CREDITS = provideCredits() // Help Tips - val HELP_TIPS = listOf( - HelpTip( - title = R.string.help_title_how_to_find_books, - description = { _, _ -> - append(stringResource(id = R.string.help_desc_how_to_find_books)) - }, - customContent = { onHelpEvent -> - HelpFindBooksContent(onEvent = onHelpEvent) - } - ), - - HelpTip( - title = R.string.help_title_how_to_add_books, - description = { onNavigate, fromStart -> - append(stringResource(id = R.string.help_desc_how_to_add_books_1) + " ") - - HelpAnnotation( - onClick = { - if (!fromStart) { - onNavigate { - navigate(Screen.Browse, useBackAnimation = true) - } - } - } - ) { - append(stringResource(id = R.string.help_desc_how_to_add_books_2)) - } - append(". ") - - append(stringResource(id = R.string.help_desc_how_to_add_books_3) + " ") - - HelpAnnotation( - onClick = { - if (!fromStart) { - onNavigate { - navigate(Screen.Library, useBackAnimation = true) - } - } - } - ) { - append(stringResource(id = R.string.help_desc_how_to_add_books_4)) - } - append(".") - } - ), - - HelpTip( - title = R.string.help_title_how_to_customize_app, - description = { onNavigate, fromStart -> - append(stringResource(id = R.string.help_desc_how_to_customize_app_1) + " ") - - HelpAnnotation( - onClick = { - if (!fromStart) { - onNavigate { - navigate(Screen.Settings, useBackAnimation = true) - } - } - } - ) { - append(stringResource(id = R.string.help_desc_how_to_customize_app_2)) - } - append(". ") - - append(stringResource(id = R.string.help_desc_how_to_customize_app_3)) - } - ), - - HelpTip( - title = R.string.help_title_how_to_move_or_delete_books, - description = { onNavigate, fromStart -> - append(stringResource(id = R.string.help_desc_how_to_move_or_delete_books_1) + " ") - - HelpAnnotation( - onClick = { - if (!fromStart) { - onNavigate { - navigate(Screen.Library, useBackAnimation = true) - } - } - } - ) { - append(stringResource(id = R.string.help_desc_how_to_move_or_delete_books_2)) - } - append(". ") - - append(stringResource(id = R.string.help_desc_how_to_move_or_delete_books_3)) - } - ), - - HelpTip( - title = R.string.help_title_how_to_edit_book, - description = { onNavigate, fromStart -> - append(stringResource(id = R.string.help_desc_how_to_edit_book_1) + " ") - - HelpAnnotation( - onClick = { - if (!fromStart) { - onNavigate { - navigate(Screen.Library, useBackAnimation = true) - } - } - } - ) { - append(stringResource(id = R.string.help_desc_how_to_edit_book_2)) - } - append(" ") - - append(stringResource(id = R.string.help_desc_how_to_edit_book_3)) - } - ), - - HelpTip( - title = R.string.help_title_how_to_read_book, - description = { onNavigate, fromStart -> - append(stringResource(id = R.string.help_desc_how_to_read_book_1) + " ") - - HelpAnnotation( - onClick = { - if (!fromStart) { - onNavigate { - navigate(Screen.Library, useBackAnimation = true) - } - } - } - ) { - append(stringResource(id = R.string.help_desc_how_to_read_book_2)) - } - append(". ") - - append(stringResource(id = R.string.help_desc_how_to_read_book_3)) - } - ), - - HelpTip( - title = R.string.help_title_how_to_customize_reader, - description = { onNavigate, fromStart -> - append(stringResource(id = R.string.help_desc_how_to_customize_reader_1) + " ") - - HelpAnnotation( - onClick = { - if (!fromStart) { - onNavigate { - navigate(Screen.Settings, useBackAnimation = true) - } - } - } - ) { - append(stringResource(id = R.string.help_desc_how_to_customize_reader_2)) - } - append(" ") - - append(stringResource(id = R.string.help_desc_how_to_customize_reader_3)) - } - ), - - HelpTip( - title = R.string.help_title_how_to_update_book, - description = { onNavigate, fromStart -> - append(stringResource(id = R.string.help_desc_how_to_update_book_1) + " ") - - HelpAnnotation( - onClick = { - if (!fromStart) { - onNavigate { - navigate(Screen.Library, useBackAnimation = true) - } - } - } - ) { - append(stringResource(id = R.string.help_desc_how_to_update_book_2)) - } - append(" ") - - append(stringResource(id = R.string.help_desc_how_to_update_book_3)) - } - ), - - HelpTip( - title = R.string.help_title_how_to_manage_history, - description = { onNavigate, fromStart -> - append(stringResource(id = R.string.help_desc_how_to_manage_history_1) + " ") - - HelpAnnotation( - onClick = { - if (!fromStart) { - onNavigate { - navigate(Screen.History, useBackAnimation = true) - } - } - } - ) { - append(stringResource(id = R.string.help_desc_how_to_manage_history_2)) - } - append(". ") - - append(stringResource(id = R.string.help_desc_how_to_manage_history_3)) - } - ), - - HelpTip( - title = R.string.help_title_how_to_use_tooltip, - description = { _, _ -> - append(stringResource(id = R.string.help_desc_how_to_use_tooltip_1)) - } - ), - - HelpTip( - title = R.string.help_title_how_to_use_double_click_translation, - description = { _, _ -> - append(stringResource(id = R.string.help_desc_how_to_use_double_click_translation_1)) - } - ), - - HelpTip( - title = R.string.help_title_how_to_create_color_presets, - description = { _, _ -> - append(stringResource(id = R.string.help_desc_how_to_create_color_presets_1)) - } - ), - - HelpTip( - title = R.string.help_title_how_to_use_color_presets, - description = { _, _ -> - append(stringResource(id = R.string.help_desc_how_to_use_color_presets_1)) - } - ), - ) + val HELP_TIPS = provideHelpTips() // About Badges - val ABOUT_BADGES = listOf( - Badge( - id = "x", - drawable = R.drawable.x_logo, - imageVector = null, - contentDescription = R.string.x_content_desc, - url = "https://www.x.com/acclorite" - ), - Badge( - id = "reddit", - drawable = R.drawable.reddit, - imageVector = null, - contentDescription = R.string.reddit_content_desc, - url = "https://www.reddit.com/user/Acclorite/" - ), - Badge( - id = "tryzub", - drawable = R.drawable.tryzub, - imageVector = null, - contentDescription = R.string.tryzub_content_desc, - url = null - ), - Badge( - id = "github_project", - drawable = R.drawable.github, - imageVector = null, - contentDescription = R.string.github_project_content_desc, - url = "https://www.github.com/Acclorite/book-story" - ), - Badge( - id = "github_profile", - drawable = null, - imageVector = Icons.Default.Person, - contentDescription = R.string.github_profile_content_desc, - url = "https://www.github.com/Acclorite" - ), - ) + val ABOUT_BADGES = provideAboutBadges() // Fonts for Reader - val FONTS = listOf( - FontWithName( - "default", - UIText.StringResource(R.string.default_font), - FontFamily.Default + val FONTS = provideFonts() + + // Empty Book (Used for Default Screen values) + val EMPTY_BOOK = provideEmptyBook() + + // Default Color Preset (Used when creating new color presets) + val DEFAULT_COLOR_PRESET = provideDefaultColorPreset() +} + +private fun provideSupportedExtensions() = listOf( + ".epub", ".pdf", ".fb2", ".txt", ".zip", ".html", ".htm" +) + +private fun provideLanguages() = listOf( + Pair("en", "English"), + Pair("uk", "Українська"), +) + +private fun provideNavigationItems() = listOf( + NavigationItem( + screen = Screen.Library, + title = R.string.library_screen, + tooltip = R.string.library_content_desc, + selectedIcon = R.drawable.library_screen_filled, + unselectedIcon = R.drawable.library_screen_outlined + ), + NavigationItem( + screen = Screen.History, + title = R.string.history_screen, + tooltip = R.string.history_content_desc, + selectedIcon = R.drawable.history_screen_filled, + unselectedIcon = R.drawable.history_screen_outlined + ), + NavigationItem( + screen = Screen.Browse, + title = R.string.browse_screen, + tooltip = R.string.browse_content_desc, + selectedIcon = R.drawable.browse_screen_filled, + unselectedIcon = R.drawable.browse_screen_outlined + ) +) + +private fun provideThemes() = listOf( + Pair(Theme.DYNAMIC, UIText.StringResource(R.string.dynamic_theme)), + Pair(Theme.BLUE, UIText.StringResource(R.string.blue_theme)), + Pair(Theme.GREEN, UIText.StringResource(R.string.green_theme)), + Pair(Theme.RED, UIText.StringResource(R.string.red_theme)), + Pair(Theme.PURPLE, UIText.StringResource(R.string.purple_theme)), + Pair(Theme.PINK, UIText.StringResource(R.string.pink_theme)), + Pair(Theme.YELLOW, UIText.StringResource(R.string.yellow_theme)), + Pair(Theme.AQUA, UIText.StringResource(R.string.aqua_theme)), +) + +private fun provideCredits() = listOf( + Credit( + name = "Tachiyomi (Mihon)", + source = "GitHub", + credits = listOf( + UIText.StringResource(R.string.credits_design), + UIText.StringValue("Readme") ), - FontWithName( - "raleway", - UIText.StringValue("Raleway"), - FontFamily( - Font(R.font.raleway_regular), - Font(R.font.raleway_regular_italic, style = FontStyle.Italic) - ) + website = "https://www.github.com/mihonapp/mihon" + ), + Credit( + name = "Kitsune", + source = "GitHub", + credits = listOf( + UIText.StringResource(R.string.credits_updates) ), - FontWithName( - "open_sans", - UIText.StringValue("Open Sans"), - FontFamily( - Font(R.font.opensans_regular), - Font(R.font.opensans_regular_italic, style = FontStyle.Italic) - ) + website = "https://www.github.com/Drumber/Kitsune" + ), + Credit( + name = "Material Design Icons", + source = "Google Fonts", + credits = listOf( + UIText.StringResource(R.string.credits_icon) ), - FontWithName( - "mulish", - UIText.StringValue("Mulish"), - FontFamily( - Font(R.font.mulish_regular), - Font(R.font.mulish_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "arimo", - UIText.StringValue("Arimo"), - FontFamily( - Font(R.font.arimo_regular), - Font(R.font.arimo_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "garamond", - UIText.StringValue("Garamond"), - FontFamily( - Font(R.font.garamond_regular), - Font(R.font.garamond_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "roboto_serif", - UIText.StringValue("Roboto Serif"), - FontFamily( - Font(R.font.robotoserif_regular), - Font(R.font.robotoserif_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "noto_serif", - UIText.StringValue("Noto Serif"), - FontFamily( - Font(R.font.notoserif_regular), - Font(R.font.notoserif_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "noto_sans", - UIText.StringValue("Noto Sans"), - FontFamily( - Font(R.font.notosans_regular), - Font(R.font.notosans_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "roboto", - UIText.StringValue("Roboto"), - FontFamily( - Font(R.font.roboto_regular), - Font(R.font.roboto_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "jost", - UIText.StringValue("Jost"), - FontFamily( - Font(R.font.jost_regular), - Font(R.font.jost_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "merriweather", - UIText.StringValue("Merriweather"), - FontFamily( - Font(R.font.merriweather_regular), - Font(R.font.merriweather_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "montserrat", - UIText.StringValue("Montserrat"), - FontFamily( - Font(R.font.montserrat_regular), - Font(R.font.montserrat_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "nunito", - UIText.StringValue("Nunito"), - FontFamily( - Font(R.font.nunito_regular), - Font(R.font.nunito_regular_italic, style = FontStyle.Italic) - ) - ), - FontWithName( - "roboto_slab", - UIText.StringValue("Roboto Slab"), - FontFamily( - Font(R.font.robotoslab_regular) - ) - ), - FontWithName( - "lora", - UIText.StringValue("Lora"), - FontFamily( - Font(R.font.lora_regular), - Font(R.font.lora_regular_italic, style = FontStyle.Italic) - ) + website = "https://fonts.google.com/icons" + ), +) + +private fun provideHelpTips() = listOf( + HelpTip( + title = R.string.help_title_how_to_find_books, + description = { _, _ -> + append(stringResource(id = R.string.help_desc_how_to_find_books)) + }, + customContent = { onHelpEvent -> + HelpFindBooksContent(onEvent = onHelpEvent) + } + ), + + HelpTip( + title = R.string.help_title_how_to_add_books, + description = { onNavigate, fromStart -> + append(stringResource(id = R.string.help_desc_how_to_add_books_1) + " ") + + HelpAnnotation( + onClick = { + if (!fromStart) { + onNavigate { + navigate(Screen.Browse, useBackAnimation = true) + } + } + } + ) { + append(stringResource(id = R.string.help_desc_how_to_add_books_2)) + } + append(". ") + + append(stringResource(id = R.string.help_desc_how_to_add_books_3) + " ") + + HelpAnnotation( + onClick = { + if (!fromStart) { + onNavigate { + navigate(Screen.Library, useBackAnimation = true) + } + } + } + ) { + append(stringResource(id = R.string.help_desc_how_to_add_books_4)) + } + append(".") + } + ), + + HelpTip( + title = R.string.help_title_how_to_customize_app, + description = { onNavigate, fromStart -> + append(stringResource(id = R.string.help_desc_how_to_customize_app_1) + " ") + + HelpAnnotation( + onClick = { + if (!fromStart) { + onNavigate { + navigate(Screen.Settings, useBackAnimation = true) + } + } + } + ) { + append(stringResource(id = R.string.help_desc_how_to_customize_app_2)) + } + append(". ") + + append(stringResource(id = R.string.help_desc_how_to_customize_app_3)) + } + ), + + HelpTip( + title = R.string.help_title_how_to_move_or_delete_books, + description = { onNavigate, fromStart -> + append(stringResource(id = R.string.help_desc_how_to_move_or_delete_books_1) + " ") + + HelpAnnotation( + onClick = { + if (!fromStart) { + onNavigate { + navigate(Screen.Library, useBackAnimation = true) + } + } + } + ) { + append(stringResource(id = R.string.help_desc_how_to_move_or_delete_books_2)) + } + append(". ") + + append(stringResource(id = R.string.help_desc_how_to_move_or_delete_books_3)) + } + ), + + HelpTip( + title = R.string.help_title_how_to_edit_book, + description = { onNavigate, fromStart -> + append(stringResource(id = R.string.help_desc_how_to_edit_book_1) + " ") + + HelpAnnotation( + onClick = { + if (!fromStart) { + onNavigate { + navigate(Screen.Library, useBackAnimation = true) + } + } + } + ) { + append(stringResource(id = R.string.help_desc_how_to_edit_book_2)) + } + append(" ") + + append(stringResource(id = R.string.help_desc_how_to_edit_book_3)) + } + ), + + HelpTip( + title = R.string.help_title_how_to_read_book, + description = { onNavigate, fromStart -> + append(stringResource(id = R.string.help_desc_how_to_read_book_1) + " ") + + HelpAnnotation( + onClick = { + if (!fromStart) { + onNavigate { + navigate(Screen.Library, useBackAnimation = true) + } + } + } + ) { + append(stringResource(id = R.string.help_desc_how_to_read_book_2)) + } + append(". ") + + append(stringResource(id = R.string.help_desc_how_to_read_book_3)) + } + ), + + HelpTip( + title = R.string.help_title_how_to_customize_reader, + description = { onNavigate, fromStart -> + append(stringResource(id = R.string.help_desc_how_to_customize_reader_1) + " ") + + HelpAnnotation( + onClick = { + if (!fromStart) { + onNavigate { + navigate(Screen.Settings, useBackAnimation = true) + } + } + } + ) { + append(stringResource(id = R.string.help_desc_how_to_customize_reader_2)) + } + append(" ") + + append(stringResource(id = R.string.help_desc_how_to_customize_reader_3)) + } + ), + + HelpTip( + title = R.string.help_title_how_to_update_book, + description = { onNavigate, fromStart -> + append(stringResource(id = R.string.help_desc_how_to_update_book_1) + " ") + + HelpAnnotation( + onClick = { + if (!fromStart) { + onNavigate { + navigate(Screen.Library, useBackAnimation = true) + } + } + } + ) { + append(stringResource(id = R.string.help_desc_how_to_update_book_2)) + } + append(" ") + + append(stringResource(id = R.string.help_desc_how_to_update_book_3)) + } + ), + + HelpTip( + title = R.string.help_title_how_to_manage_history, + description = { onNavigate, fromStart -> + append(stringResource(id = R.string.help_desc_how_to_manage_history_1) + " ") + + HelpAnnotation( + onClick = { + if (!fromStart) { + onNavigate { + navigate(Screen.History, useBackAnimation = true) + } + } + } + ) { + append(stringResource(id = R.string.help_desc_how_to_manage_history_2)) + } + append(". ") + + append(stringResource(id = R.string.help_desc_how_to_manage_history_3)) + } + ), + + HelpTip( + title = R.string.help_title_how_to_use_tooltip, + description = { _, _ -> + append(stringResource(id = R.string.help_desc_how_to_use_tooltip_1)) + } + ), + + HelpTip( + title = R.string.help_title_how_to_use_double_click_translation, + description = { _, _ -> + append(stringResource(id = R.string.help_desc_how_to_use_double_click_translation_1)) + } + ), + + HelpTip( + title = R.string.help_title_how_to_create_color_presets, + description = { _, _ -> + append(stringResource(id = R.string.help_desc_how_to_create_color_presets_1)) + } + ), + + HelpTip( + title = R.string.help_title_how_to_use_color_presets, + description = { _, _ -> + append(stringResource(id = R.string.help_desc_how_to_use_color_presets_1)) + } + ), +) + +private fun provideAboutBadges() = listOf( + Badge( + id = "x", + drawable = R.drawable.x_logo, + imageVector = null, + contentDescription = R.string.x_content_desc, + url = "https://www.x.com/acclorite" + ), + Badge( + id = "reddit", + drawable = R.drawable.reddit, + imageVector = null, + contentDescription = R.string.reddit_content_desc, + url = "https://www.reddit.com/user/Acclorite/" + ), + Badge( + id = "tryzub", + drawable = R.drawable.tryzub, + imageVector = null, + contentDescription = R.string.tryzub_content_desc, + url = null + ), + Badge( + id = "github_project", + drawable = R.drawable.github, + imageVector = null, + contentDescription = R.string.github_project_content_desc, + url = "https://www.github.com/Acclorite/book-story" + ), + Badge( + id = "github_profile", + drawable = null, + imageVector = Icons.Default.Person, + contentDescription = R.string.github_profile_content_desc, + url = "https://www.github.com/Acclorite" + ), +) + +private fun provideFonts() = listOf( + FontWithName( + "default", + UIText.StringResource(R.string.default_font), + FontFamily.Default + ), + FontWithName( + "raleway", + UIText.StringValue("Raleway"), + FontFamily( + Font(R.font.raleway_regular), + Font(R.font.raleway_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "open_sans", + UIText.StringValue("Open Sans"), + FontFamily( + Font(R.font.opensans_regular), + Font(R.font.opensans_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "mulish", + UIText.StringValue("Mulish"), + FontFamily( + Font(R.font.mulish_regular), + Font(R.font.mulish_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "arimo", + UIText.StringValue("Arimo"), + FontFamily( + Font(R.font.arimo_regular), + Font(R.font.arimo_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "garamond", + UIText.StringValue("Garamond"), + FontFamily( + Font(R.font.garamond_regular), + Font(R.font.garamond_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "roboto_serif", + UIText.StringValue("Roboto Serif"), + FontFamily( + Font(R.font.robotoserif_regular), + Font(R.font.robotoserif_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "noto_serif", + UIText.StringValue("Noto Serif"), + FontFamily( + Font(R.font.notoserif_regular), + Font(R.font.notoserif_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "noto_sans", + UIText.StringValue("Noto Sans"), + FontFamily( + Font(R.font.notosans_regular), + Font(R.font.notosans_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "roboto", + UIText.StringValue("Roboto"), + FontFamily( + Font(R.font.roboto_regular), + Font(R.font.roboto_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "jost", + UIText.StringValue("Jost"), + FontFamily( + Font(R.font.jost_regular), + Font(R.font.jost_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "merriweather", + UIText.StringValue("Merriweather"), + FontFamily( + Font(R.font.merriweather_regular), + Font(R.font.merriweather_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "montserrat", + UIText.StringValue("Montserrat"), + FontFamily( + Font(R.font.montserrat_regular), + Font(R.font.montserrat_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "nunito", + UIText.StringValue("Nunito"), + FontFamily( + Font(R.font.nunito_regular), + Font(R.font.nunito_regular_italic, style = FontStyle.Italic) + ) + ), + FontWithName( + "roboto_slab", + UIText.StringValue("Roboto Slab"), + FontFamily( + Font(R.font.robotoslab_regular) + ) + ), + FontWithName( + "lora", + UIText.StringValue("Lora"), + FontFamily( + Font(R.font.lora_regular), + Font(R.font.lora_regular_italic, style = FontStyle.Italic) ) ) +) - val EMPTY_BOOK = Book( - id = -1, - title = "", - author = UIText.StringValue(""), - description = null, - textPath = "", - filePath = "", - coverImage = null, - scrollIndex = 0, - scrollOffset = 0, - progress = 0f, - lastOpened = null, - category = Category.READING - ) +private fun provideEmptyBook() = Book( + id = -1, + title = "", + author = UIText.StringValue(""), + description = null, + textPath = "", + filePath = "", + coverImage = null, + scrollIndex = 0, + scrollOffset = 0, + progress = 0f, + lastOpened = null, + category = Category.READING +) - val DEFAULT_COLOR_PRESET = ColorPreset( - id = -1, - name = null, - backgroundColor = Color.DarkGray, - fontColor = Color.LightGray, - isSelected = false - ) -} \ No newline at end of file +private fun provideDefaultColorPreset() = ColorPreset( + id = -1, + name = null, + backgroundColor = Color.DarkGray, + fontColor = Color.LightGray, + isSelected = false +) \ No newline at end of file