diff --git a/README.md b/README.md
index b45fb50d..c2ba732a 100644
--- a/README.md
+++ b/README.md
@@ -39,12 +39,14 @@ Built using Jetpack Compose and Kotlin.
-

-

-

-

-

-

+

+

+

+

+

+

+

+
@@ -117,6 +119,10 @@ _Don't see your favorite app store? Create an [Issue](https://github.com/Acclori
## 🤝 How to contribute
- **The most improtant thing is I do not want someone's code.** This project is maintaining only by me and I want it to be this way. Creating Pull Requests will not have much sense as they will probably be closed.
+- **Place your suggestions on Issues
+ with [Suggestion Needed](https://github.com/Acclorite/book-story/labels/%F0%9F%93%9D%20Suggestion%20needed)
+ label.** When the issue has this label that means I seek new ideas on how to improve what I
+ already propose. Feel free to tell me what's on your mind!
- **Share your ideas and suggestions.** Your ideas and suggestions are always welcome, if you miss some feature or got an interesting idea, feel free to create a new [Issue](https://github.com/Acclorite/book-story/issues).
- **Report bugs**. Had a crash or something went wrong? Create a new [Issue](https://github.com/Acclorite/book-story/issues), the more information you can give the better it is.
- **Enjoy the app**. Your best contribution is using my app and enjoying it!
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 5f53aad4..c72bc2f5 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -137,9 +137,6 @@ dependencies {
implementation("com.tom-roush:pdfbox-android:2.0.27.0")
// Epub parser
- implementation("com.positiondev.epublib:epublib-core:3.1") {
- exclude("xmlpull")
- }
implementation("org.jsoup:jsoup:1.18.1")
// Fb2 parser
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 0314c8fc..07e3b415 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -37,11 +37,12 @@
-dontwarn org.xmlpull.v1.XmlPullParserFactory
# Fixes issue with displaying .xml files, but may produce reproducibility issues
--keep class org.xmlpull.** { *; }
--keepclassmembers class org.xmlpull.** { *; }
+#-keep class org.xmlpull.** { *; }
+#-keepclassmembers class org.xmlpull.** { *; }
# Keeping names of classes that produce undeterministic results
-keepnames class **
-keepnames class org.xmlpull.** { *; }
-keepclassmembernames class org.xmlpull.** { *; }
--keepnames class kotlin.reflect.jvm.internal.impl.builtins.PrimitiveType { values(); }
\ No newline at end of file
+-keepnames class kotlin.reflect.jvm.internal.impl.builtins.PrimitiveType { values(); }
+-keepnames class * implements android.os.Parcelable { ** CREATOR; }
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 05ff76ad..ae9f380e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,26 +17,6 @@
android:name="ua.acclorite.book_story.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
tools:node="remove" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
? = 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()
+ val author = UIText.StringValue(
+ document.select("metadata > dc|creator").text().trim()
+ )
+ val description = Jsoup.parse(
+ document.select("metadata > dc|description").text()
+ ).text()
+
+ var coverImagePath: String? = null
+
+ val coverId = document
+ .select("metadata > meta[name=cover]")
+ .attr("content")
+ if (coverId.isNotBlank()) {
+ coverImagePath = document
+ .select("manifest > item[id=$coverId]")
+ .attr("href")
+ }
+
+ if (coverImagePath.isNullOrBlank()) {
+ coverImagePath = 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, coverImagePath)
}
}
-
- val metadata = book.metadata
- val author = UIText.StringValue(metadata.authors.joinToString(", "))
- val title = metadata.titles.joinToString(", ")
- val coverImage = book.coverImage?.let { BitmapFactory.decodeStream(it.inputStream) }
-
- var description: StringBuilder? = StringBuilder()
- metadata.descriptions.forEach {
- description?.append(it)?.append(" ")
- }
-
- if (description != null) {
- if (description.isBlank()) {
- description = null
- }
- }
-
- return Book(
- title = title,
- author = author,
- description = description?.toString(),
- textPath = "",
- scrollIndex = 0,
- scrollOffset = 0,
- progress = 0f,
- filePath = file.path,
- lastOpened = null,
- category = Category.entries[0],
- coverImage = null
- ) to 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/epub/EpubTextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubTextParser.kt
index 3930ddd4..992f1efa 100644
--- a/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubTextParser.kt
+++ b/app/src/main/java/ua/acclorite/book_story/data/parser/epub/EpubTextParser.kt
@@ -2,19 +2,13 @@ package ua.acclorite.book_story.data.parser.epub
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
-import nl.siegmann.epublib.epub.EpubReader
import org.jsoup.Jsoup
-import org.jsoup.nodes.Document.OutputSettings
-import org.jsoup.safety.Safelist
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.BufferedReader
import java.io.File
-import java.io.FileInputStream
-import java.io.InputStreamReader
-import java.nio.charset.Charset
+import java.util.zip.ZipFile
import javax.inject.Inject
@@ -26,66 +20,42 @@ class EpubTextParser @Inject constructor() : TextParser {
}
try {
- val book = withContext(Dispatchers.IO) {
- EpubReader().readEpub(FileInputStream(file))
- }
- val unformattedText = StringBuilder()
+ val lines = mutableListOf()
- for (spineReference in book.spine.spineReferences) {
- val resource = spineReference.resource
- val inputStream = resource.inputStream
- val reader = BufferedReader(
- InputStreamReader(
- inputStream,
- Charset.forName("UTF-8")
- )
- )
- var line: String?
+ 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()
+ }
- withContext(Dispatchers.IO) {
- inputStream.close()
- }
-
- while (withContext(Dispatchers.IO) { reader.readLine() }
- .also { line = it } != null) {
- unformattedText.append(line).append("\n")
- }
-
- withContext(Dispatchers.IO) {
- reader.close()
- }
- }
-
- val strings = mutableListOf()
-
- val parsedText = Jsoup.parse(unformattedText.toString())
- parsedText.outputSettings(OutputSettings().prettyPrint(false))
- parsedText.select("br").append("\n")
- parsedText.select("p").prepend("\n")
- parsedText.select("em").append(" ").prepend("")
-
- val formattedText = Jsoup.clean(
- parsedText.html(),
- "",
- Safelist.none(),
- OutputSettings().prettyPrint(false)
- )
-
- formattedText
- .replace(" ", "")
- .replace("\u00a0", "")
- .split("\n")
- .forEach {
- if (it.isNotBlank()) {
- strings.add(it.trim())
+ val document = Jsoup.parse(content)
+ document.select("p").append("\n")
+ document
+ .wholeText()
+ .lines()
+ .forEach { line ->
+ if (line.isNotBlank()) {
+ lines.add(line.trim())
+ }
+ }
+ }
}
}
+ }
- if (strings.isEmpty()) {
+ if (lines.isEmpty()) {
return Resource.Error(UIText.StringResource(R.string.error_file_empty))
}
- return Resource.Success(strings)
+ return Resource.Success(lines)
} catch (e: Exception) {
e.printStackTrace()
return Resource.Error(
diff --git a/app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2TextParser.kt b/app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2TextParser.kt
index 4ddb1f30..0a769ace 100644
--- a/app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2TextParser.kt
+++ b/app/src/main/java/ua/acclorite/book_story/data/parser/fb2/Fb2TextParser.kt
@@ -58,20 +58,6 @@ class Fb2TextParser @Inject constructor() : TextParser {
return@forEachIndexed
}
- if (
- line.all {
- if (it == ' ') {
- true
- } else if (it.isUpperCase() || it.isDigit() || it == '-') {
- true
- } else {
- false
- }
- }
- ) {
- return@forEachIndexed
- }
-
if (line.all { it.isDigit() }) {
return@forEachIndexed
}
diff --git a/app/src/main/java/ua/acclorite/book_story/domain/use_case/GetText.kt b/app/src/main/java/ua/acclorite/book_story/domain/use_case/GetText.kt
index e7311860..a3a0c933 100644
--- a/app/src/main/java/ua/acclorite/book_story/domain/use_case/GetText.kt
+++ b/app/src/main/java/ua/acclorite/book_story/domain/use_case/GetText.kt
@@ -1,15 +1,11 @@
package ua.acclorite.book_story.domain.use_case
-import ua.acclorite.book_story.domain.model.ReaderLine
import ua.acclorite.book_story.domain.repository.BookRepository
-import ua.acclorite.book_story.domain.util.ID
import javax.inject.Inject
class GetText @Inject constructor(private val repository: BookRepository) {
- suspend fun execute(textPath: String): Map {
- return repository.getBookText(textPath = textPath).withIndex().associate {
- it.index to ReaderLine(line = it.value)
- }
+ suspend fun execute(textPath: String): List {
+ return repository.getBookText(textPath = textPath)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/components/CustomCheckbox.kt b/app/src/main/java/ua/acclorite/book_story/presentation/components/CustomCheckbox.kt
index 48bfa527..602167b9 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/components/CustomCheckbox.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/components/CustomCheckbox.kt
@@ -20,12 +20,14 @@ import ua.acclorite.book_story.R
/**
* Custom Checkbox. Has a Circle shape.
*
+ * @param modifier Modifier.
* @param selected Whether checked or not.
* @param containerColor Container color.
* @param size Size of the checkbox icon(not the whole checkbox).
*/
@Composable
fun CustomCheckbox(
+ modifier: Modifier = Modifier,
selected: Boolean,
containerColor: Color,
size: Dp = 22.dp
@@ -33,7 +35,7 @@ fun CustomCheckbox(
Icon(
imageVector = Icons.Default.Check,
tint = if (selected) containerColor else Color.Transparent,
- modifier = Modifier
+ modifier = modifier
.border(
width = if (!selected) 1.dp else 0.dp,
if (!selected) MaterialTheme.colorScheme.outline else Color.Transparent,
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/components/CustomSelectionContainer.kt b/app/src/main/java/ua/acclorite/book_story/presentation/components/CustomSelectionContainer.kt
index 0f4fba42..3944725f 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/components/CustomSelectionContainer.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/components/CustomSelectionContainer.kt
@@ -29,8 +29,10 @@ import ua.acclorite.book_story.R
private const val MENU_ITEM_COPY = 0
-private const val MENU_ITEM_TRANSLATE = 1
-private const val MENU_ITEM_DICTIONARY = 2
+private const val MENU_ITEM_SHARE = 1
+private const val MENU_ITEM_WEB = 2
+private const val MENU_ITEM_TRANSLATE = 3
+private const val MENU_ITEM_DICTIONARY = 4
/**
* Custom Text ActionMode callback. Used in pair with [CustomSelectionToolbar]. Follow [TextToolbar] for more info.
@@ -39,6 +41,8 @@ private class CustomTextActionModeCallback(
private val context: Context,
var rect: Rect = Rect.Zero,
var onCopyRequested: (() -> Unit)? = null,
+ var onShareRequested: (() -> Unit)? = null,
+ var onWebSearchRequested: (() -> Unit)? = null,
var onTranslateRequested: (() -> Unit)? = null,
var onDictionaryRequested: (() -> Unit)? = null
) : ActionMode.Callback {
@@ -51,13 +55,23 @@ private class CustomTextActionModeCallback(
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
}
+ onShareRequested?.let {
+ menu.add(0, MENU_ITEM_SHARE, 1, context.getString(R.string.share))
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
+ }
+
+ onWebSearchRequested?.let {
+ menu.add(0, MENU_ITEM_WEB, 2, context.getString(R.string.web_search))
+ .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
+ }
+
onTranslateRequested?.let {
- menu.add(0, MENU_ITEM_TRANSLATE, 1, context.getString(R.string.translate))
+ menu.add(0, MENU_ITEM_TRANSLATE, 3, context.getString(R.string.translate))
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
}
onDictionaryRequested?.let {
- menu.add(0, MENU_ITEM_DICTIONARY, 2, context.getString(R.string.dictionary))
+ menu.add(0, MENU_ITEM_DICTIONARY, 4, context.getString(R.string.dictionary))
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER)
}
@@ -71,6 +85,8 @@ private class CustomTextActionModeCallback(
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
when (item!!.itemId) {
MENU_ITEM_COPY -> onCopyRequested?.invoke()
+ MENU_ITEM_SHARE -> onShareRequested?.invoke()
+ MENU_ITEM_WEB -> onWebSearchRequested?.invoke()
MENU_ITEM_TRANSLATE -> onTranslateRequested?.invoke()
MENU_ITEM_DICTIONARY -> onDictionaryRequested?.invoke()
else -> return false
@@ -116,12 +132,15 @@ private class FloatingTextActionModeCallback(
}
/**
- * Custom Selection Toolbar. Used in pair with [CustomSelectionContainer] to display custom toolbar.
+ * Custom Selection Toolbar.
+ * Used in pair with [CustomSelectionContainer] to display custom toolbar.
*/
private class CustomSelectionToolbar(
private val view: View,
context: Context,
private val onCopyRequest: (() -> Unit)?,
+ private val onShareRequest: ((String) -> Unit)?,
+ private val onWebSearchRequest: ((String) -> Unit)?,
private val onTranslateRequest: ((String) -> Unit)?,
private val onDictionaryRequest: ((String) -> Unit)?
) : TextToolbar {
@@ -145,6 +164,36 @@ private class CustomSelectionToolbar(
onCopyRequested?.invoke()
onCopyRequest?.invoke()
}
+ callback.onShareRequested = {
+ val previousClipboard = clipboardManager.primaryClip
+ onCopyRequested?.invoke()
+ val currentClipboard = clipboardManager.text
+
+ onShareRequest?.invoke(currentClipboard.toString())
+
+ if (previousClipboard != null) {
+ clipboardManager.setPrimaryClip(
+ previousClipboard
+ )
+ } else {
+ clipboardManager.setPrimaryClip(ClipData.newPlainText(null, " "))
+ }
+ }
+ callback.onWebSearchRequested = {
+ val previousClipboard = clipboardManager.primaryClip
+ onCopyRequested?.invoke()
+ val currentClipboard = clipboardManager.text
+
+ onWebSearchRequest?.invoke(currentClipboard.toString())
+
+ if (previousClipboard != null) {
+ clipboardManager.setPrimaryClip(
+ previousClipboard
+ )
+ } else {
+ clipboardManager.setPrimaryClip(ClipData.newPlainText(null, " "))
+ }
+ }
callback.onTranslateRequested = {
val previousClipboard = clipboardManager.primaryClip
onCopyRequested?.invoke()
@@ -205,6 +254,8 @@ private class CustomSelectionToolbar(
@Composable
fun CustomSelectionContainer(
onCopyRequested: (() -> Unit),
+ onShareRequested: ((String) -> Unit),
+ onWebSearchRequested: ((String) -> Unit),
onTranslateRequested: ((String) -> Unit),
onDictionaryRequested: ((String) -> Unit),
content: @Composable (toolbarHidden: Boolean) -> Unit
@@ -220,6 +271,12 @@ fun CustomSelectionContainer(
onCopyRequest = {
onCopyRequested()
},
+ onShareRequest = {
+ onShareRequested(it)
+ },
+ onWebSearchRequest = {
+ onWebSearchRequested(it)
+ },
onTranslateRequest = {
onTranslateRequested(it)
},
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/components/custom_dialog/CustomDialogWithContent.kt b/app/src/main/java/ua/acclorite/book_story/presentation/components/custom_dialog/CustomDialogWithContent.kt
index 0c8aa962..2fa53529 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/components/custom_dialog/CustomDialogWithContent.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/components/custom_dialog/CustomDialogWithContent.kt
@@ -60,7 +60,7 @@ fun CustomDialogWithContent(
modifier: Modifier = Modifier,
drawableIcon: Painter? = null,
imageVectorIcon: ImageVector? = null,
- backgroundTransparency: Float = 0.3f,
+ backgroundTransparency: Float = 0.5f,
title: String,
description: String?,
actionText: String?,
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/components/custom_dialog/CustomDialogWithLazyColumn.kt b/app/src/main/java/ua/acclorite/book_story/presentation/components/custom_dialog/CustomDialogWithLazyColumn.kt
index 521d1229..00dde813 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/components/custom_dialog/CustomDialogWithLazyColumn.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/components/custom_dialog/CustomDialogWithLazyColumn.kt
@@ -62,7 +62,7 @@ fun CustomDialogWithLazyColumn(
modifier: Modifier = Modifier,
drawableIcon: Painter? = null,
imageVectorIcon: ImageVector? = null,
- backgroundTransparency: Float = 0.3f,
+ backgroundTransparency: Float = 0.5f,
title: String,
description: String?,
actionText: String?,
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/data/Functions.kt b/app/src/main/java/ua/acclorite/book_story/presentation/data/Functions.kt
index 3f84ab28..0abd5fad 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/data/Functions.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/data/Functions.kt
@@ -15,16 +15,18 @@ fun Double.removeDigits(digits: Int) = "%.${digits}f".format(this).replace(",",
fun Intent.launchActivity(
activity: ComponentActivity,
+ createChooser: Boolean = false,
openInNewWindow: Boolean = true,
success: (() -> Unit)? = null,
error: () -> Unit
) {
+ val intent = if (createChooser) Intent.createChooser(this, "") else this
if (openInNewWindow) {
- this.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
try {
- activity.baseContext.startActivity(this)
+ activity.baseContext.startActivity(intent)
} catch (e: Exception) {
error()
return
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/nested/licenses/components/LicenseItem.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/nested/licenses/components/LicenseItem.kt
index e6833260..8f45a0a4 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/nested/licenses/components/LicenseItem.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/about/nested/licenses/components/LicenseItem.kt
@@ -90,7 +90,9 @@ fun LicenseItem(
Text(
text = library.author,
style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.onSurfaceVariant
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ maxLines = 2,
+ overflow = TextOverflow.Ellipsis
)
}
if (library.licenses.isNotEmpty()) {
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/data/BookInfoViewModel.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/data/BookInfoViewModel.kt
index 36b8a6d0..582d5a51 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/data/BookInfoViewModel.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/book_info/data/BookInfoViewModel.kt
@@ -534,7 +534,7 @@ class BookInfoViewModel @Inject constructor(
val updatedText = updatedBook.text
val text = getText.execute(book.textPath)
- if (updatedText != text.map { it.value.line }) {
+ if (updatedText != text) {
textUpdated = true
}
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/browse/components/adding_dialog/BrowseAddingDialogItem.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/browse/components/adding_dialog/BrowseAddingDialogItem.kt
index 63e2e89b..64ff5e0f 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/browse/components/adding_dialog/BrowseAddingDialogItem.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/browse/components/adding_dialog/BrowseAddingDialogItem.kt
@@ -64,7 +64,7 @@ fun BrowseAddingDialogItem(result: Pair, onClick: (Boole
Spacer(modifier = Modifier.width(24.dp))
CustomCheckbox(
selected = result.second,
- MaterialTheme.colorScheme.surfaceContainerHigh
+ containerColor = MaterialTheme.colorScheme.surfaceContainerHigh
)
}
}
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/ReaderScreen.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/ReaderScreen.kt
index de97e874..9c3c0a53 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/ReaderScreen.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/ReaderScreen.kt
@@ -52,7 +52,7 @@ import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.domain.util.OnNavigate
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
import ua.acclorite.book_story.presentation.components.CustomSelectionContainer
-import ua.acclorite.book_story.presentation.components.customItems
+import ua.acclorite.book_story.presentation.components.customItemsIndexed
import ua.acclorite.book_story.presentation.components.is_messages.IsError
import ua.acclorite.book_story.presentation.data.LocalNavigator
import ua.acclorite.book_story.presentation.data.MainEvent
@@ -188,9 +188,6 @@ private fun ReaderScreen(
}
}
- val text = remember(state.value.text) {
- state.value.text.toList().sortedBy { it.first }
- }
val fontFamily = remember(mainState.value.fontFamily) {
Constants.FONTS.find {
it.id == mainState.value.fontFamily
@@ -277,10 +274,41 @@ private fun ReaderScreen(
).show()
}
},
+ onShareRequested = { textToShare ->
+ onEvent(
+ ReaderEvent.OnOpenShareApp(
+ textToShare = textToShare,
+ context,
+ noAppsFound = {
+ Toast.makeText(
+ context,
+ context.getString(R.string.error_no_share_app),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ )
+ )
+ },
+ onWebSearchRequested = { textToSearch ->
+ onEvent(
+ ReaderEvent.OnOpenWebBrowser(
+ textToSearch = textToSearch,
+ context,
+ noAppsFound = {
+ Toast.makeText(
+ context,
+ context.getString(R.string.error_no_browser),
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ )
+ )
+ },
onTranslateRequested = { textToTranslate ->
onEvent(
ReaderEvent.OnOpenTranslator(
- textToTranslate,
+ textToTranslate = textToTranslate,
+ translateWholeParagraph = false,
context,
noAppsFound = {
Toast.makeText(
@@ -300,7 +328,7 @@ private fun ReaderScreen(
noAppsFound = {
Toast.makeText(
context,
- context.getString(R.string.error_no_browser),
+ context.getString(R.string.error_no_dictionary),
Toast.LENGTH_SHORT
).show()
}
@@ -359,11 +387,11 @@ private fun ReaderScreen(
Spacer(modifier = Modifier.height(18.dp))
}
- customItems(
- text, key = { key -> key.first }
- ) { line ->
+ customItemsIndexed(
+ state.value.text, key = { _, index -> index }
+ ) { _, line ->
ReaderTextParagraph(
- line = line.second,
+ line = line,
context = context,
fontFamily = fontFamily,
fontColor = fontColor.value,
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/components/ReaderTextParagraph.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/components/ReaderTextParagraph.kt
index e4284eb9..d05fb875 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/components/ReaderTextParagraph.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/components/ReaderTextParagraph.kt
@@ -22,7 +22,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.FontWithName
-import ua.acclorite.book_story.domain.model.ReaderLine
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
/**
@@ -44,7 +43,7 @@ import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun LazyItemScope.ReaderTextParagraph(
- line: ReaderLine,
+ line: String,
context: Context,
fontFamily: FontWithName,
fontColor: Color,
@@ -72,7 +71,7 @@ fun LazyItemScope.ReaderTextParagraph(
append(" ")
}
- append(line.line)
+ append(line)
},
modifier = Modifier.then(
if (
@@ -85,7 +84,8 @@ fun LazyItemScope.ReaderTextParagraph(
onDoubleClick = {
onEvent(
ReaderEvent.OnOpenTranslator(
- textToTranslate = line.line,
+ textToTranslate = line,
+ translateWholeParagraph = true,
context = context as ComponentActivity,
noAppsFound = {
Toast.makeText(
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderEvent.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderEvent.kt
index 9134a288..36a149af 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderEvent.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderEvent.kt
@@ -46,6 +46,19 @@ sealed class ReaderEvent {
data class OnOpenTranslator(
val textToTranslate: String,
+ val translateWholeParagraph: Boolean,
+ val context: ComponentActivity,
+ val noAppsFound: () -> Unit
+ ) : ReaderEvent()
+
+ data class OnOpenShareApp(
+ val textToShare: String,
+ val context: ComponentActivity,
+ val noAppsFound: () -> Unit
+ ) : ReaderEvent()
+
+ data class OnOpenWebBrowser(
+ val textToSearch: String,
val context: ComponentActivity,
val noAppsFound: () -> Unit
) : ReaderEvent()
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderState.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderState.kt
index d290483f..5ddf0249 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderState.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderState.kt
@@ -3,15 +3,13 @@ package ua.acclorite.book_story.presentation.screens.reader.data
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Immutable
import ua.acclorite.book_story.domain.model.Book
-import ua.acclorite.book_story.domain.model.ReaderLine
import ua.acclorite.book_story.domain.util.Constants
-import ua.acclorite.book_story.domain.util.ID
import ua.acclorite.book_story.domain.util.UIText
@Immutable
data class ReaderState(
val book: Book = Constants.EMPTY_BOOK,
- val text: Map = emptyMap(),
+ val text: List = emptyList(),
val words: Int = 0,
val letters: Int = 0,
val listState: LazyListState = LazyListState(),
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderViewModel.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderViewModel.kt
index 72afd8f2..2f74e5c0 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderViewModel.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/reader/data/ReaderViewModel.kt
@@ -2,6 +2,7 @@ package ua.acclorite.book_story.presentation.screens.reader.data
import android.app.SearchManager
import android.content.Intent
+import android.net.Uri
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.compose.runtime.snapshotFlow
@@ -73,7 +74,6 @@ class ReaderViewModel @Inject constructor(
}
val textAsLine = text
- .map { it.value.line }
.joinToString(
separator = "\n"
)
@@ -338,7 +338,10 @@ class ReaderViewModel @Inject constructor(
)
var translatorFailure = false
- translatorIntent.launchActivity(event.context) {
+ translatorIntent.launchActivity(
+ event.context,
+ createChooser = !event.translateWholeParagraph
+ ) {
translatorFailure = true
}
if (!translatorFailure) {
@@ -357,22 +360,85 @@ class ReaderViewModel @Inject constructor(
}
}
- is ReaderEvent.OnOpenDictionary -> {
+ is ReaderEvent.OnOpenShareApp -> {
+ launch {
+ val shareIntent = Intent()
+
+ shareIntent.action = Intent.ACTION_SEND
+ shareIntent.type = "text/plain"
+ shareIntent.putExtra(
+ Intent.EXTRA_SUBJECT,
+ event.context.getString(R.string.app_name)
+ )
+ shareIntent.putExtra(
+ Intent.EXTRA_TEXT,
+ event.textToShare.trim()
+ )
+
+ var shareFailure = false
+ shareIntent.launchActivity(event.context, createChooser = true) {
+ shareFailure = true
+ }
+ if (!shareFailure) {
+ return@launch
+ }
+
+ event.noAppsFound()
+ }
+ }
+
+ is ReaderEvent.OnOpenWebBrowser -> {
launch {
val browserIntent = Intent()
browserIntent.action = Intent.ACTION_WEB_SEARCH
browserIntent.putExtra(
SearchManager.QUERY,
- "dictionary" +
- ": ${event.textToDefine.trim()}"
+ event.textToSearch
)
- var failure = false
+ var browserFailure = false
browserIntent.launchActivity(event.context) {
- failure = true
+ browserFailure = true
}
- if (!failure) {
+ if (!browserFailure) {
+ return@launch
+ }
+
+ event.noAppsFound()
+ }
+ }
+
+ is ReaderEvent.OnOpenDictionary -> {
+ launch {
+ val dictionaryIntent = Intent()
+ val browserIntent = Intent()
+
+ dictionaryIntent.type = "text/plain"
+ dictionaryIntent.action = Intent.ACTION_PROCESS_TEXT
+ dictionaryIntent.putExtra(
+ Intent.EXTRA_PROCESS_TEXT,
+ event.textToDefine.trim()
+ )
+ dictionaryIntent.putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, true)
+
+ browserIntent.action = Intent.ACTION_VIEW
+ val text = event.textToDefine.trim().replace(" ", "+")
+ browserIntent.data = Uri.parse("https://www.onelook.com/?w=$text")
+
+ var dictionaryFailure = false
+ dictionaryIntent.launchActivity(event.context, createChooser = true) {
+ dictionaryFailure = true
+ }
+ if (!dictionaryFailure) {
+ return@launch
+ }
+
+ var browserFailure = false
+ browserIntent.launchActivity(event.context) {
+ browserFailure = true
+ }
+ if (!browserFailure) {
return@launch
}
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/components/SegmentedButtonWithTitle.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/components/SegmentedButtonWithTitle.kt
index 7fa647e2..6fddf8cc 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/components/SegmentedButtonWithTitle.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/components/SegmentedButtonWithTitle.kt
@@ -1,38 +1,70 @@
package ua.acclorite.book_story.presentation.screens.settings.components
+import androidx.compose.animation.expandHorizontally
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.scaleIn
+import androidx.compose.animation.scaleOut
+import androidx.compose.animation.shrinkHorizontally
+import androidx.compose.animation.slideInVertically
+import androidx.compose.animation.slideOutVertically
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.SegmentedButton
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Done
+import androidx.compose.material3.Icon
+import androidx.compose.material3.SegmentedButtonColors
import androidx.compose.material3.SegmentedButtonDefaults
-import androidx.compose.material3.SingleChoiceSegmentedButtonRow
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import ua.acclorite.book_story.R
import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.presentation.components.CategoryTitle
+import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
/**
- * Segmented Button with Title. Contains [buttons].
+ * Segmented Button with Title.
+ * Uses custom implementation(material3 one has many flaws).
+ * Contains [buttons].
+ *
+ * @param modifier Modifier.
+ * @param title Title of the buttons.
+ * @param buttons [ButtonItem]s.
+ * @param enabled Whether button is enabled.
+ * @param horizontalPadding Horizontal item padding.
+ * @param verticalPadding Vertical item padding.
+ * @param onClick OnClick callback.
*/
@Composable
fun SegmentedButtonWithTitle(
modifier: Modifier = Modifier,
title: String,
buttons: List,
- locked: Boolean,
+ enabled: Boolean = true,
horizontalPadding: Dp = 18.dp,
verticalPadding: Dp = 8.dp,
onClick: (ButtonItem) -> Unit
) {
- val colors = SegmentedButtonDefaults.colors()
-
Column(
modifier
.fillMaxWidth()
@@ -42,50 +74,118 @@ fun SegmentedButtonWithTitle(
Spacer(modifier = Modifier.height(8.dp))
- SingleChoiceSegmentedButtonRow {
- buttons.forEachIndexed { index, buttonItem ->
- SegmentedButton(
- enabled = locked,
- selected = buttonItem.selected,
- onClick = { onClick(buttonItem) },
- shape = when (index) {
- buttons.lastIndex -> RoundedCornerShape(
- topEndPercent = 100,
- bottomEndPercent = 100
+ LazyRow(Modifier.fillMaxWidth()) {
+ item {
+ Row(
+ Modifier
+ .clip(CircleShape)
+ .border(
+ width = 0.5.dp,
+ color = SegmentedButtonDefaults.colors().activeBorderColor,
+ shape = CircleShape
)
+ .padding(0.5.dp)
+ ) {
+ buttons.forEachIndexed { index, buttonItem ->
+ SegmentedButton(
+ button = buttonItem,
+ enabled = enabled,
+ shape = when (index) {
+ buttons.lastIndex -> RoundedCornerShape(
+ topEndPercent = 100,
+ bottomEndPercent = 100
+ )
- 0 -> RoundedCornerShape(
- topStartPercent = 100,
- bottomStartPercent = 100
- )
+ 0 -> RoundedCornerShape(
+ topStartPercent = 100,
+ bottomStartPercent = 100
+ )
- else -> RoundedCornerShape(0)
- },
- colors = SegmentedButtonDefaults.colors(
- disabledInactiveBorderColor = colors.activeBorderColor,
- disabledInactiveContentColor = colors.inactiveContentColor,
- disabledActiveContainerColor = colors.activeContainerColor,
- disabledActiveContentColor = colors.activeContentColor,
- disabledInactiveContainerColor = colors.inactiveContainerColor,
- disabledActiveBorderColor = colors.activeBorderColor,
- ),
- label = {
- Text(
- text = buttonItem.title,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis
+ else -> RoundedCornerShape(0)
+ },
+ onClick = { onClick(buttonItem) }
)
}
- )
+ }
}
}
}
}
+/**
+ * Custom Segmented button.
+ * Adjusts width based on component name.
+ *
+ * @param button [ButtonItem].
+ * @param enabled Whether can be clicked.
+ * @param shape Shape of the button. For proper implementation should have 100% corners on first and last buttons(start and end).
+ * @param colors [SegmentedButtonColors].
+ * @param onClick OnClick callback.
+ */
+@Composable
+private fun SegmentedButton(
+ button: ButtonItem,
+ enabled: Boolean,
+ shape: RoundedCornerShape,
+ colors: SegmentedButtonColors = SegmentedButtonDefaults.colors(),
+ onClick: () -> Unit
+) {
+ Row(
+ modifier = Modifier
+ .height(40.dp)
+ .clip(shape)
+ .clickable(enabled = enabled && !button.selected) {
+ onClick()
+ }
+ .border(
+ width = 0.5.dp,
+ color = colors.activeBorderColor,
+ shape = shape
+ )
+ .padding(0.5.dp)
+ .background(
+ if (button.selected) colors.activeContainerColor
+ else Color.Transparent,
+ shape = shape
+ )
+ .padding(horizontal = 18.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ CustomAnimatedVisibility(
+ visible = button.selected,
+ enter = expandHorizontally()
+ + slideInVertically(initialOffsetY = { it / 2 })
+ + scaleIn()
+ + fadeIn(),
+ exit = shrinkHorizontally()
+ + slideOutVertically(targetOffsetY = { it / 2 })
+ + scaleOut()
+ + fadeOut()
+ ) {
+ Row {
+ Icon(
+ imageVector = Icons.Default.Done,
+ contentDescription = stringResource(id = R.string.selected_content_desc),
+ modifier = Modifier
+ .size(18.dp),
+ tint = colors.activeContentColor
+ )
+ Spacer(modifier = Modifier.width(8.dp))
+ }
+ }
+
+ Text(
+ text = button.title,
+ style = button.textStyle,
+ fontWeight = FontWeight.Medium,
+ color = if (button.selected) colors.activeContentColor
+ else colors.inactiveContentColor
+ )
+ }
+}
+
-
-
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/DarkThemeSetting.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/DarkThemeSetting.kt
index b661da8c..4a534ec6 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/DarkThemeSetting.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/DarkThemeSetting.kt
@@ -25,17 +25,16 @@ fun LazyItemScope.DarkThemeSetting(
SegmentedButtonWithTitle(
title = stringResource(id = R.string.dark_theme_option),
modifier = Modifier.animateItem(),
- locked = true,
buttons = DarkTheme.entries.map {
ButtonItem(
it.toString(),
- when (it) {
+ title = when (it) {
DarkTheme.OFF -> stringResource(id = R.string.dark_theme_off)
DarkTheme.ON -> stringResource(id = R.string.dark_theme_on)
DarkTheme.FOLLOW_SYSTEM -> stringResource(id = R.string.dark_theme_follow_system)
},
- MaterialTheme.typography.labelLarge,
- it == state.value.darkTheme
+ textStyle = MaterialTheme.typography.labelLarge,
+ selected = it == state.value.darkTheme
)
}
) {
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/PureDarkSetting.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/PureDarkSetting.kt
index 0b1815ee..37a47365 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/PureDarkSetting.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/PureDarkSetting.kt
@@ -33,17 +33,16 @@ fun LazyItemScope.PureDarkSetting(
) {
SegmentedButtonWithTitle(
title = stringResource(id = R.string.pure_dark_option),
- locked = true,
buttons = PureDark.entries.map {
ButtonItem(
- it.toString(),
- when (it) {
+ id = it.toString(),
+ title = when (it) {
PureDark.OFF -> stringResource(id = R.string.pure_dark_off)
PureDark.ON -> stringResource(id = R.string.pure_dark_on)
PureDark.SAVER -> stringResource(id = R.string.pure_dark_power_saver)
},
- MaterialTheme.typography.labelLarge,
- it == state.value.pureDark
+ textStyle = MaterialTheme.typography.labelLarge,
+ selected = it == state.value.pureDark
)
}
) {
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/ThemeContrastSetting.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/ThemeContrastSetting.kt
index 7b21d698..ae1c910c 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/ThemeContrastSetting.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/appearance/components/settings/ThemeContrastSetting.kt
@@ -54,17 +54,17 @@ fun LazyItemScope.ThemeContrastSetting(
) {
SegmentedButtonWithTitle(
title = stringResource(id = R.string.theme_contrast_option),
- locked = state.value.theme != Theme.DYNAMIC,
+ enabled = state.value.theme != Theme.DYNAMIC,
buttons = ThemeContrast.entries.map {
ButtonItem(
- it.toString(),
- when (it) {
+ id = it.toString(),
+ title = when (it) {
ThemeContrast.STANDARD -> stringResource(id = R.string.theme_contrast_standard)
ThemeContrast.MEDIUM -> stringResource(id = R.string.theme_contrast_medium)
ThemeContrast.HIGH -> stringResource(id = R.string.theme_contrast_high)
},
- MaterialTheme.typography.labelLarge,
- it == state.value.themeContrast
+ textStyle = MaterialTheme.typography.labelLarge,
+ selected = it == state.value.themeContrast
)
}
) {
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/reader/components/settings/FontFamilySetting.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/reader/components/settings/FontFamilySetting.kt
index 9f89d94a..ee3ddde3 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/reader/components/settings/FontFamilySetting.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/reader/components/settings/FontFamilySetting.kt
@@ -35,12 +35,12 @@ fun LazyItemScope.FontFamilySetting(
chips = Constants.FONTS
.map {
ButtonItem(
- it.id,
- it.fontName.asString(),
- MaterialTheme.typography.labelLarge.copy(
+ id = it.id,
+ title = it.fontName.asString(),
+ textStyle = MaterialTheme.typography.labelLarge.copy(
fontFamily = it.font
),
- it.id == fontFamily.id
+ selected = it.id == fontFamily.id
)
},
onClick = {
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/reader/components/settings/FontStyleSetting.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/reader/components/settings/FontStyleSetting.kt
index 57044070..05e2c796 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/reader/components/settings/FontStyleSetting.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/settings/nested/reader/components/settings/FontStyleSetting.kt
@@ -13,7 +13,7 @@ import ua.acclorite.book_story.domain.model.ButtonItem
import ua.acclorite.book_story.domain.util.Constants
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.data.MainState
-import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle
+import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
/**
* Font Style setting.
@@ -30,27 +30,27 @@ fun LazyItemScope.FontStyleSetting(
} ?: Constants.FONTS[0]
}
- ChipsWithTitle(
+ SegmentedButtonWithTitle(
title = stringResource(id = R.string.font_style_option),
modifier = Modifier.animateItem(),
- chips = listOf(
+ buttons = listOf(
ButtonItem(
- "normal",
- stringResource(id = R.string.font_style_normal),
- MaterialTheme.typography.labelLarge.copy(
+ id = "normal",
+ title = stringResource(id = R.string.font_style_normal),
+ textStyle = MaterialTheme.typography.labelLarge.copy(
fontFamily = fontFamily.font,
fontStyle = FontStyle.Normal
),
- !state.value.isItalic!!
+ selected = !state.value.isItalic!!
),
ButtonItem(
- "italic",
- stringResource(id = R.string.font_style_italic),
- MaterialTheme.typography.labelLarge.copy(
+ id = "italic",
+ title = stringResource(id = R.string.font_style_italic),
+ textStyle = MaterialTheme.typography.labelLarge.copy(
fontFamily = fontFamily.font,
fontStyle = FontStyle.Italic
),
- state.value.isItalic!!
+ selected = state.value.isItalic!!
),
),
onClick = {
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/screens/start/components/StartSettings.kt b/app/src/main/java/ua/acclorite/book_story/presentation/screens/start/components/StartSettings.kt
index 40b8f799..3d3aa75f 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/screens/start/components/StartSettings.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/screens/start/components/StartSettings.kt
@@ -64,10 +64,10 @@ fun StartSettings(
val languages = remember(mainState.value.language) {
Constants.LANGUAGES.sortedBy { it.second }.map {
ButtonItem(
- it.first,
- it.second,
- TextStyle(),
- it.first == mainState.value.language
+ id = it.first,
+ title = it.second,
+ textStyle = TextStyle(),
+ selected = it.first == mainState.value.language
)
}.sortedBy { it.title }
}
diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/ui/Transitions.kt b/app/src/main/java/ua/acclorite/book_story/presentation/ui/Transitions.kt
index b6c943b6..6f043cf3 100644
--- a/app/src/main/java/ua/acclorite/book_story/presentation/ui/Transitions.kt
+++ b/app/src/main/java/ua/acclorite/book_story/presentation/ui/Transitions.kt
@@ -58,22 +58,6 @@ fun DefaultTransition(
}
}
-@Composable
-fun T.DefaultTransition(
- visible: Boolean,
- modifier: Modifier = Modifier,
- content: @Composable T.() -> Unit
-) {
- CustomAnimatedVisibility(
- visible = visible,
- modifier = modifier,
- enter = Transitions.DefaultTransitionIn,
- exit = Transitions.DefaultTransitionOut
- ) {
- content()
- }
-}
-
@Composable
fun SlidingTransition(
visible: Boolean,
diff --git a/app/src/main/res/drawable/browse_screen_filled.png b/app/src/main/res/drawable/browse_screen_filled.png
new file mode 100644
index 00000000..e52334d9
Binary files /dev/null and b/app/src/main/res/drawable/browse_screen_filled.png differ
diff --git a/app/src/main/res/drawable/browse_screen_filled.xml b/app/src/main/res/drawable/browse_screen_filled.xml
deleted file mode 100644
index a03c6520..00000000
--- a/app/src/main/res/drawable/browse_screen_filled.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/browse_screen_outlined.png b/app/src/main/res/drawable/browse_screen_outlined.png
new file mode 100644
index 00000000..8f9c1bb3
Binary files /dev/null and b/app/src/main/res/drawable/browse_screen_outlined.png differ
diff --git a/app/src/main/res/drawable/browse_screen_outlined.xml b/app/src/main/res/drawable/browse_screen_outlined.xml
deleted file mode 100644
index d0bf00e9..00000000
--- a/app/src/main/res/drawable/browse_screen_outlined.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/empty_browse.png b/app/src/main/res/drawable/empty_browse.png
new file mode 100644
index 00000000..6a4df38f
Binary files /dev/null and b/app/src/main/res/drawable/empty_browse.png differ
diff --git a/app/src/main/res/drawable/empty_browse.xml b/app/src/main/res/drawable/empty_browse.xml
deleted file mode 100644
index d3230a31..00000000
--- a/app/src/main/res/drawable/empty_browse.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/empty_history.png b/app/src/main/res/drawable/empty_history.png
new file mode 100644
index 00000000..a714c2a6
Binary files /dev/null and b/app/src/main/res/drawable/empty_history.png differ
diff --git a/app/src/main/res/drawable/empty_history.xml b/app/src/main/res/drawable/empty_history.xml
deleted file mode 100644
index ad6d8ff3..00000000
--- a/app/src/main/res/drawable/empty_history.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/empty_library.png b/app/src/main/res/drawable/empty_library.png
new file mode 100644
index 00000000..2099c29c
Binary files /dev/null and b/app/src/main/res/drawable/empty_library.png differ
diff --git a/app/src/main/res/drawable/empty_library.xml b/app/src/main/res/drawable/empty_library.xml
deleted file mode 100644
index 8612cf0e..00000000
--- a/app/src/main/res/drawable/empty_library.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/error.png b/app/src/main/res/drawable/error.png
new file mode 100644
index 00000000..b86b89de
Binary files /dev/null and b/app/src/main/res/drawable/error.png differ
diff --git a/app/src/main/res/drawable/error.xml b/app/src/main/res/drawable/error.xml
deleted file mode 100644
index 3f8953db..00000000
--- a/app/src/main/res/drawable/error.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/github.png b/app/src/main/res/drawable/github.png
new file mode 100644
index 00000000..e03c369a
Binary files /dev/null and b/app/src/main/res/drawable/github.png differ
diff --git a/app/src/main/res/drawable/github.xml b/app/src/main/res/drawable/github.xml
deleted file mode 100644
index 85acf69d..00000000
--- a/app/src/main/res/drawable/github.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/history_screen_filled.png b/app/src/main/res/drawable/history_screen_filled.png
new file mode 100644
index 00000000..0ce352d9
Binary files /dev/null and b/app/src/main/res/drawable/history_screen_filled.png differ
diff --git a/app/src/main/res/drawable/history_screen_filled.xml b/app/src/main/res/drawable/history_screen_filled.xml
deleted file mode 100644
index 3c9844ba..00000000
--- a/app/src/main/res/drawable/history_screen_filled.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/history_screen_outlined.png b/app/src/main/res/drawable/history_screen_outlined.png
new file mode 100644
index 00000000..a52acc36
Binary files /dev/null and b/app/src/main/res/drawable/history_screen_outlined.png differ
diff --git a/app/src/main/res/drawable/history_screen_outlined.xml b/app/src/main/res/drawable/history_screen_outlined.xml
deleted file mode 100644
index ed47780f..00000000
--- a/app/src/main/res/drawable/history_screen_outlined.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/library_screen_filled.png b/app/src/main/res/drawable/library_screen_filled.png
new file mode 100644
index 00000000..640f15af
Binary files /dev/null and b/app/src/main/res/drawable/library_screen_filled.png differ
diff --git a/app/src/main/res/drawable/library_screen_filled.xml b/app/src/main/res/drawable/library_screen_filled.xml
deleted file mode 100644
index 6638db7b..00000000
--- a/app/src/main/res/drawable/library_screen_filled.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/library_screen_outlined.png b/app/src/main/res/drawable/library_screen_outlined.png
new file mode 100644
index 00000000..2a5ff603
Binary files /dev/null and b/app/src/main/res/drawable/library_screen_outlined.png differ
diff --git a/app/src/main/res/drawable/library_screen_outlined.xml b/app/src/main/res/drawable/library_screen_outlined.xml
deleted file mode 100644
index ea5bab63..00000000
--- a/app/src/main/res/drawable/library_screen_outlined.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/reddit.png b/app/src/main/res/drawable/reddit.png
new file mode 100644
index 00000000..fbb7242a
Binary files /dev/null and b/app/src/main/res/drawable/reddit.png differ
diff --git a/app/src/main/res/drawable/reddit.xml b/app/src/main/res/drawable/reddit.xml
deleted file mode 100644
index 665e0eaa..00000000
--- a/app/src/main/res/drawable/reddit.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/start_done.png b/app/src/main/res/drawable/start_done.png
new file mode 100644
index 00000000..6493de0c
Binary files /dev/null and b/app/src/main/res/drawable/start_done.png differ
diff --git a/app/src/main/res/drawable/start_done.xml b/app/src/main/res/drawable/start_done.xml
deleted file mode 100644
index 3b9056b3..00000000
--- a/app/src/main/res/drawable/start_done.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/tryzub.png b/app/src/main/res/drawable/tryzub.png
new file mode 100644
index 00000000..b51f4d42
Binary files /dev/null and b/app/src/main/res/drawable/tryzub.png differ
diff --git a/app/src/main/res/drawable/tryzub.xml b/app/src/main/res/drawable/tryzub.xml
deleted file mode 100644
index 176f1594..00000000
--- a/app/src/main/res/drawable/tryzub.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/drawable/x_logo.png b/app/src/main/res/drawable/x_logo.png
new file mode 100644
index 00000000..38d6b28c
Binary files /dev/null and b/app/src/main/res/drawable/x_logo.png differ
diff --git a/app/src/main/res/drawable/x_logo.xml b/app/src/main/res/drawable/x_logo.xml
deleted file mode 100644
index 85cf4d58..00000000
--- a/app/src/main/res/drawable/x_logo.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 497e2f34..6d393843 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -101,7 +101,9 @@
Помилка: %1$s
Цей файл пустий або пошкоджений.
Не знайдено жодного перекладача.
+ Не знайдено жодного словника.
Не знайдено жодного браузера.
+ Не знайдено застосунка щоб поділитися.
Вміст книги пустий. Будь ласка, перевірте вміст книги та оновіть її.
@@ -160,6 +162,8 @@
Завантажити
Готово
Увімкнути
+ Веб-пошук
+ Поділитися
файли
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 86cbf832..e2480a92 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -118,7 +118,9 @@
Error: %1$s
This file is empty or corrupted.
No translator app found.
+ No dictionary app found.
No browser app found.
+ No app to share found.
The book content is empty. Please, make sure that the book content is not empty and update it.
@@ -176,6 +178,8 @@
Download
Done
Enable
+ Web search
+ Share
files
diff --git a/assets/Book-Info Dark + Frame.png b/assets/Book-Info Dark + Frame.png
deleted file mode 100644
index 9e9b40b4..00000000
Binary files a/assets/Book-Info Dark + Frame.png and /dev/null differ
diff --git a/assets/Book-Info Light + Frame.png b/assets/Book-Info Light + Frame.png
deleted file mode 100644
index 43f2bebf..00000000
Binary files a/assets/Book-Info Light + Frame.png and /dev/null differ
diff --git a/assets/Library Dark + Frame.png b/assets/Library Dark + Frame.png
deleted file mode 100644
index d3c2fedc..00000000
Binary files a/assets/Library Dark + Frame.png and /dev/null differ
diff --git a/assets/Library Light + Frame.png b/assets/Library Light + Frame.png
deleted file mode 100644
index 431efa89..00000000
Binary files a/assets/Library Light + Frame.png and /dev/null differ
diff --git a/assets/Reader Dark + Frame.png b/assets/Reader Dark + Frame.png
deleted file mode 100644
index 5320ce3f..00000000
Binary files a/assets/Reader Dark + Frame.png and /dev/null differ
diff --git a/assets/Reader Light + Frame.png b/assets/Reader Light + Frame.png
deleted file mode 100644
index 0b43646e..00000000
Binary files a/assets/Reader Light + Frame.png and /dev/null differ
diff --git a/assets/Reader Translator.png b/assets/Reader Translator.png
deleted file mode 100644
index 852647cb..00000000
Binary files a/assets/Reader Translator.png and /dev/null differ
diff --git a/assets/Translator Settings.png b/assets/Translator Settings.png
deleted file mode 100644
index eb4deda3..00000000
Binary files a/assets/Translator Settings.png and /dev/null differ
diff --git a/fastlane/metadata/android/en-US/changelogs/6.txt b/fastlane/metadata/android/en-US/changelogs/6.txt
new file mode 100644
index 00000000..78ccca08
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/6.txt
@@ -0,0 +1,8 @@
+* Added loading indicator in AboutScreen's check for updates
+* Reduced APK size (30Mb → 12Mb)
+* Improved performance
+* Improved text parsers
+* Fixed various crashes
+* Various small changes
+* Removed no longer valid Credits
+* More small changes
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
index d3c2fedc..c6fbd981 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
index 431efa89..5848b693 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
index 5320ce3f..cafdf773 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
index 0b43646e..1c2f52b5 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png
index 9e9b40b4..0bdf294f 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png
index 43f2bebf..8656d749 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png
new file mode 100644
index 00000000..bdf7679d
Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png
new file mode 100644
index 00000000..804fe9b4
Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/9.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/9.png
new file mode 100644
index 00000000..bb50290d
Binary files /dev/null and b/fastlane/metadata/android/en-US/images/phoneScreenshots/9.png differ