0.9.9 - Open source licenses & Credits screens in About + Bug fixes(No check for updates in Release Build) + Side Padding setting in ReaderScreen.
This commit is contained in:
parent
df215fcfa3
commit
43da347ae0
42 changed files with 1116 additions and 112 deletions
|
|
@ -16,7 +16,7 @@ android {
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "0.9.8"
|
versionName = "0.9.9"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
|
@ -52,13 +52,24 @@ android {
|
||||||
packaging {
|
packaging {
|
||||||
resources {
|
resources {
|
||||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||||
excludes += "/META-INF/LICENSE.md"
|
|
||||||
excludes += "/META-INF/LICENSE-notice.md"
|
|
||||||
excludes += "/META-INF/gradle/incremental.annotation.processors"
|
excludes += "/META-INF/gradle/incremental.annotation.processors"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aboutLibraries {
|
||||||
|
registerAndroidTasks = true
|
||||||
|
outputFileName = "aboutlibraries.json"
|
||||||
|
offlineMode = false
|
||||||
|
fetchRemoteLicense = true
|
||||||
|
fetchRemoteFunding = true
|
||||||
|
includePlatform = true
|
||||||
|
duplicationMode = com.mikepenz.aboutlibraries.plugin.DuplicateMode.MERGE
|
||||||
|
duplicationRule = com.mikepenz.aboutlibraries.plugin.DuplicateRule.SIMPLE
|
||||||
|
prettyPrint = false
|
||||||
|
filterVariants = arrayOf("debug", "release")
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
// Default
|
// Default
|
||||||
|
|
@ -137,5 +148,5 @@ dependencies {
|
||||||
|
|
||||||
// About open source libraries
|
// About open source libraries
|
||||||
implementation("com.mikepenz:aboutlibraries-core:latest.release")
|
implementation("com.mikepenz:aboutlibraries-core:latest.release")
|
||||||
implementation("com.mikepenz:aboutlibraries-compose:latest.release")
|
implementation("com.mikepenz:aboutlibraries-compose-m3:latest.release")
|
||||||
}
|
}
|
||||||
14
app/proguard-rules.pro
vendored
14
app/proguard-rules.pro
vendored
|
|
@ -1,14 +1,7 @@
|
||||||
# Proguard-rules
|
# Proguard-rules
|
||||||
-dontwarn org.xmlpull.v1.**
|
-dontwarn org.xmlpull.v1.**
|
||||||
-dontwarn org.kxml2.io.**
|
|
||||||
-dontwarn android.content.res.**
|
|
||||||
-dontwarn org.slf4j.impl.StaticLoggerBinder
|
|
||||||
-dontwarn com.gemalto.jp2.JP2Decoder
|
-dontwarn com.gemalto.jp2.JP2Decoder
|
||||||
-dontwarn com.google.auto.service.AutoService
|
-dontwarn com.google.auto.service.AutoService
|
||||||
-dontwarn javax.annotation.processing.AbstractProcessor
|
|
||||||
-dontwarn javax.annotation.processing.Filer
|
|
||||||
-dontwarn javax.annotation.processing.Messager
|
|
||||||
-dontwarn javax.annotation.processing.ProcessingEnvironment
|
|
||||||
-dontwarn javax.lang.model.SourceVersion
|
-dontwarn javax.lang.model.SourceVersion
|
||||||
-dontwarn javax.lang.model.element.AnnotationMirror
|
-dontwarn javax.lang.model.element.AnnotationMirror
|
||||||
-dontwarn javax.lang.model.element.AnnotationValue
|
-dontwarn javax.lang.model.element.AnnotationValue
|
||||||
|
|
@ -38,6 +31,11 @@
|
||||||
-dontwarn javax.lang.model.util.SimpleTypeVisitor8
|
-dontwarn javax.lang.model.util.SimpleTypeVisitor8
|
||||||
-dontwarn javax.lang.model.util.Types
|
-dontwarn javax.lang.model.util.Types
|
||||||
-dontwarn javax.tools.Diagnostic$Kind
|
-dontwarn javax.tools.Diagnostic$Kind
|
||||||
|
-dontwarn org.kxml2.io.**
|
||||||
|
|
||||||
|
-keep class com.mikepenz.aboutlibraries.** { *; }
|
||||||
-keep class org.xmlpull.** { *; }
|
-keep class org.xmlpull.** { *; }
|
||||||
-keepclassmembers class org.xmlpull.** { *; }
|
-keepclassmembers class org.xmlpull.** { *; }
|
||||||
|
|
||||||
|
# Debug rule
|
||||||
|
#-keepnames class ** { *; }
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ import ua.acclorite.book_story.presentation.data.NavigationHost
|
||||||
import ua.acclorite.book_story.presentation.data.Screen
|
import ua.acclorite.book_story.presentation.data.Screen
|
||||||
import ua.acclorite.book_story.presentation.screens.about.AboutScreenRoot
|
import ua.acclorite.book_story.presentation.screens.about.AboutScreenRoot
|
||||||
import ua.acclorite.book_story.presentation.screens.about.data.AboutViewModel
|
import ua.acclorite.book_story.presentation.screens.about.data.AboutViewModel
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.credits.CreditsScreenRoot
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.license_info.LicenseInfoScreenRoot
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.license_info.data.LicenseInfoViewModel
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.LicensesScreenRoot
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.BookInfoScreenRoot
|
import ua.acclorite.book_story.presentation.screens.book_info.BookInfoScreenRoot
|
||||||
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
|
import ua.acclorite.book_story.presentation.screens.book_info.data.BookInfoViewModel
|
||||||
import ua.acclorite.book_story.presentation.screens.browse.BrowseScreenRoot
|
import ua.acclorite.book_story.presentation.screens.browse.BrowseScreenRoot
|
||||||
|
|
@ -65,6 +69,7 @@ class Activity : AppCompatActivity() {
|
||||||
private val readerViewModel: ReaderViewModel by viewModels()
|
private val readerViewModel: ReaderViewModel by viewModels()
|
||||||
private val helpViewModel: HelpViewModel by viewModels()
|
private val helpViewModel: HelpViewModel by viewModels()
|
||||||
private val aboutViewModel: AboutViewModel by viewModels()
|
private val aboutViewModel: AboutViewModel by viewModels()
|
||||||
|
private val licenseInfoViewModel: LicenseInfoViewModel by viewModels()
|
||||||
private val settingsViewModel: SettingsViewModel by viewModels()
|
private val settingsViewModel: SettingsViewModel by viewModels()
|
||||||
private val startViewModel: StartViewModel by viewModels()
|
private val startViewModel: StartViewModel by viewModels()
|
||||||
|
|
||||||
|
|
@ -192,6 +197,17 @@ class Activity : AppCompatActivity() {
|
||||||
AboutScreenRoot()
|
AboutScreenRoot()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nested about categories
|
||||||
|
composable(screen = Screen.LICENSES) {
|
||||||
|
LicensesScreenRoot()
|
||||||
|
}
|
||||||
|
composable(screen = Screen.LICENSES_INFO) {
|
||||||
|
LicenseInfoScreenRoot()
|
||||||
|
}
|
||||||
|
composable(screen = Screen.CREDITS) {
|
||||||
|
CreditsScreenRoot()
|
||||||
|
}
|
||||||
|
|
||||||
// Help screen
|
// Help screen
|
||||||
composable(screen = Screen.HELP) {
|
composable(screen = Screen.HELP) {
|
||||||
HelpScreenRoot()
|
HelpScreenRoot()
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
package ua.acclorite.book_story.data.remote.dto
|
package ua.acclorite.book_story.data.remote.dto
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
@Suppress("PropertyName")
|
@Keep
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
@Immutable
|
@Immutable
|
||||||
data class ReleaseResponse(
|
data class ReleaseResponse(
|
||||||
val name: String,
|
@Json(name = "name") val name: String,
|
||||||
val tag_name: String
|
@Json(name = "tag_name") val tagName: String
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import ua.acclorite.book_story.domain.util.Constants
|
||||||
import ua.acclorite.book_story.domain.util.CoverImage
|
import ua.acclorite.book_story.domain.util.CoverImage
|
||||||
import ua.acclorite.book_story.domain.util.Resource
|
import ua.acclorite.book_story.domain.util.Resource
|
||||||
import ua.acclorite.book_story.domain.util.UIText
|
import ua.acclorite.book_story.domain.util.UIText
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
@ -391,8 +391,8 @@ class BookRepositoryImpl @Inject constructor(
|
||||||
dataStore.putData(key, value)
|
dataStore.putData(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getAllSettings(scope: CoroutineScope): MainSettingsState {
|
override suspend fun getAllSettings(scope: CoroutineScope): MainState {
|
||||||
val result = CompletableDeferred<MainSettingsState>()
|
val result = CompletableDeferred<MainState>()
|
||||||
|
|
||||||
scope.launch {
|
scope.launch {
|
||||||
val keys = dataStore.getAllData()
|
val keys = dataStore.getAllData()
|
||||||
|
|
@ -411,7 +411,7 @@ class BookRepositoryImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
jobs?.awaitAll()
|
jobs?.awaitAll()
|
||||||
|
|
||||||
result.complete(MainSettingsState.initialize(data))
|
result.complete(MainState.initialize(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
return result.await()
|
return result.await()
|
||||||
|
|
@ -613,7 +613,7 @@ class BookRepositoryImpl @Inject constructor(
|
||||||
try {
|
try {
|
||||||
val result = githubAPI.getLatestRelease()
|
val result = githubAPI.getLatestRelease()
|
||||||
|
|
||||||
val version = result.tag_name.substringAfterLast("v")
|
val version = result.tagName.substringAfterLast("v")
|
||||||
val currentVersion = application.getString(R.string.app_version)
|
val currentVersion = application.getString(R.string.app_version)
|
||||||
|
|
||||||
if (version != currentVersion && postNotification) {
|
if (version != currentVersion && postNotification) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
package ua.acclorite.book_story.domain.model
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import ua.acclorite.book_story.domain.util.UIText
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class Credit(
|
||||||
|
val name: String,
|
||||||
|
val source: String,
|
||||||
|
val credits: List<UIText>,
|
||||||
|
val website: String
|
||||||
|
)
|
||||||
|
|
@ -10,7 +10,7 @@ import ua.acclorite.book_story.domain.model.NullableBook
|
||||||
import ua.acclorite.book_story.domain.model.StringWithId
|
import ua.acclorite.book_story.domain.model.StringWithId
|
||||||
import ua.acclorite.book_story.domain.util.CoverImage
|
import ua.acclorite.book_story.domain.util.CoverImage
|
||||||
import ua.acclorite.book_story.domain.util.Resource
|
import ua.acclorite.book_story.domain.util.Resource
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
interface BookRepository {
|
interface BookRepository {
|
||||||
|
|
@ -58,7 +58,7 @@ interface BookRepository {
|
||||||
value: T
|
value: T
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun getAllSettings(scope: CoroutineScope): MainSettingsState
|
suspend fun getAllSettings(scope: CoroutineScope): MainState
|
||||||
|
|
||||||
suspend fun getFilesFromDevice(query: String = ""): Flow<Resource<List<File>>>
|
suspend fun getFilesFromDevice(query: String = ""): Flow<Resource<List<File>>>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@ package ua.acclorite.book_story.domain.use_case
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import ua.acclorite.book_story.domain.repository.BookRepository
|
import ua.acclorite.book_story.domain.repository.BookRepository
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class GetAllSettings @Inject constructor(
|
class GetAllSettings @Inject constructor(
|
||||||
private val repository: BookRepository
|
private val repository: BookRepository
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun execute(scope: CoroutineScope): MainSettingsState {
|
suspend fun execute(scope: CoroutineScope): MainState {
|
||||||
return repository.getAllSettings(scope)
|
return repository.getAllSettings(scope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import androidx.compose.ui.text.font.FontWeight
|
||||||
import ua.acclorite.book_story.R
|
import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.domain.model.Book
|
import ua.acclorite.book_story.domain.model.Book
|
||||||
import ua.acclorite.book_story.domain.model.Category
|
import ua.acclorite.book_story.domain.model.Category
|
||||||
|
import ua.acclorite.book_story.domain.model.Credit
|
||||||
import ua.acclorite.book_story.domain.model.FontWithName
|
import ua.acclorite.book_story.domain.model.FontWithName
|
||||||
import ua.acclorite.book_story.presentation.ui.Theme
|
import ua.acclorite.book_story.presentation.ui.Theme
|
||||||
|
|
||||||
|
|
@ -36,6 +37,83 @@ object Constants {
|
||||||
Pair(Theme.AQUA, UIText.StringResource(R.string.aqua_theme)),
|
Pair(Theme.AQUA, UIText.StringResource(R.string.aqua_theme)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Credits
|
||||||
|
val CREDITS = listOf(
|
||||||
|
Credit(
|
||||||
|
name = "Tachiyomi (Mihon)",
|
||||||
|
source = "GitHub",
|
||||||
|
credits = listOf(
|
||||||
|
UIText.StringResource(R.string.credits_design),
|
||||||
|
UIText.StringValue("Readme")
|
||||||
|
),
|
||||||
|
website = "https://github.com/mihonapp/mihon"
|
||||||
|
),
|
||||||
|
Credit(
|
||||||
|
name = "Libre-Sudoku",
|
||||||
|
source = "GitHub",
|
||||||
|
credits = listOf(
|
||||||
|
UIText.StringValue("Readme")
|
||||||
|
),
|
||||||
|
website = "https://github.com/kaajjo/Libre-Sudoku"
|
||||||
|
),
|
||||||
|
Credit(
|
||||||
|
name = "Kitsune",
|
||||||
|
source = "GitHub",
|
||||||
|
credits = listOf(
|
||||||
|
UIText.StringResource(R.string.credits_updates)
|
||||||
|
),
|
||||||
|
website = "https://github.com/Drumber/Kitsune"
|
||||||
|
),
|
||||||
|
Credit(
|
||||||
|
name = "Freepik",
|
||||||
|
source = "Flaticon",
|
||||||
|
credits = listOf(
|
||||||
|
UIText.StringResource(R.string.credits_icon)
|
||||||
|
),
|
||||||
|
website = "https://www.flaticon.com/authors/freepik"
|
||||||
|
),
|
||||||
|
Credit(
|
||||||
|
name = "Anggara",
|
||||||
|
source = "Flaticon",
|
||||||
|
credits = listOf(
|
||||||
|
UIText.StringResource(R.string.credits_icon)
|
||||||
|
),
|
||||||
|
website = "https://www.flaticon.com/authors/anggara"
|
||||||
|
),
|
||||||
|
Credit(
|
||||||
|
name = "Google",
|
||||||
|
source = "Flaticon",
|
||||||
|
credits = listOf(
|
||||||
|
UIText.StringResource(R.string.credits_icon)
|
||||||
|
),
|
||||||
|
website = "https://www.flaticon.com/authors/google"
|
||||||
|
),
|
||||||
|
Credit(
|
||||||
|
name = "Creative Stall Premium",
|
||||||
|
source = "Flaticon",
|
||||||
|
credits = listOf(
|
||||||
|
UIText.StringResource(R.string.credits_icon)
|
||||||
|
),
|
||||||
|
website = "https://www.flaticon.com/authors/creative-stall-premium"
|
||||||
|
),
|
||||||
|
Credit(
|
||||||
|
name = "Gajah Mada",
|
||||||
|
source = "Flaticon",
|
||||||
|
credits = listOf(
|
||||||
|
UIText.StringResource(R.string.credits_icon)
|
||||||
|
),
|
||||||
|
website = "https://www.flaticon.com/authors/gajah-mada"
|
||||||
|
),
|
||||||
|
Credit(
|
||||||
|
name = "mavadee",
|
||||||
|
source = "Flaticon",
|
||||||
|
credits = listOf(
|
||||||
|
UIText.StringResource(R.string.credits_icon)
|
||||||
|
),
|
||||||
|
website = "https://www.flaticon.com/authors/mavadee"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// Fonts for Reader
|
// Fonts for Reader
|
||||||
val FONTS = listOf(
|
val FONTS = listOf(
|
||||||
FontWithName(
|
FontWithName(
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,5 @@ object DataStoreConstants {
|
||||||
val PARAGRAPH_HEIGHT = intPreferencesKey("paragraph_height")
|
val PARAGRAPH_HEIGHT = intPreferencesKey("paragraph_height")
|
||||||
val PARAGRAPH_INDENTATION = booleanPreferencesKey("paragraph_indentation")
|
val PARAGRAPH_INDENTATION = booleanPreferencesKey("paragraph_indentation")
|
||||||
val CHECK_FOR_UPDATES = booleanPreferencesKey("check_for_updates")
|
val CHECK_FOR_UPDATES = booleanPreferencesKey("check_for_updates")
|
||||||
|
val SIDE_PADDING = intPreferencesKey("side_padding")
|
||||||
}
|
}
|
||||||
|
|
@ -16,4 +16,5 @@ sealed class MainEvent {
|
||||||
data class OnChangeFontColor(val color: Long) : MainEvent()
|
data class OnChangeFontColor(val color: Long) : MainEvent()
|
||||||
data class OnChangeShowStartScreen(val bool: Boolean) : MainEvent()
|
data class OnChangeShowStartScreen(val bool: Boolean) : MainEvent()
|
||||||
data class OnChangeCheckForUpdates(val bool: Boolean) : MainEvent()
|
data class OnChangeCheckForUpdates(val bool: Boolean) : MainEvent()
|
||||||
|
data class OnChangeSidePadding(val sidePadding: Int) : MainEvent()
|
||||||
}
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ import java.util.Locale
|
||||||
* Main State. All app's settings are here. Wrapped in SavedStateHandle, so it won't reset.
|
* Main State. All app's settings are here. Wrapped in SavedStateHandle, so it won't reset.
|
||||||
*/
|
*/
|
||||||
@Immutable
|
@Immutable
|
||||||
data class MainSettingsState(
|
data class MainState(
|
||||||
val language: String? = null,
|
val language: String? = null,
|
||||||
val theme: Theme? = null,
|
val theme: Theme? = null,
|
||||||
val darkTheme: DarkTheme? = null,
|
val darkTheme: DarkTheme? = null,
|
||||||
|
|
@ -37,6 +37,7 @@ data class MainSettingsState(
|
||||||
val fontColor: Long? = null,
|
val fontColor: Long? = null,
|
||||||
val showStartScreen: Boolean? = null,
|
val showStartScreen: Boolean? = null,
|
||||||
val checkForUpdates: Boolean? = null,
|
val checkForUpdates: Boolean? = null,
|
||||||
|
val sidePadding: Int? = null,
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
constructor(parcel: Parcel) : this(
|
constructor(parcel: Parcel) : this(
|
||||||
// String
|
// String
|
||||||
|
|
@ -72,6 +73,8 @@ data class MainSettingsState(
|
||||||
// Boolean
|
// Boolean
|
||||||
checkForUpdates = if (parcel.readByte().toInt() != 0) parcel.readByte()
|
checkForUpdates = if (parcel.readByte().toInt() != 0) parcel.readByte()
|
||||||
.toInt() == 1 else null,
|
.toInt() == 1 else null,
|
||||||
|
// Int
|
||||||
|
sidePadding = if (parcel.readByte().toInt() != 0) parcel.readInt() else null,
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||||
|
|
@ -114,25 +117,28 @@ data class MainSettingsState(
|
||||||
// Check For Updates
|
// Check For Updates
|
||||||
parcel.writeByte((if (checkForUpdates != null) 1 else 0).toByte())
|
parcel.writeByte((if (checkForUpdates != null) 1 else 0).toByte())
|
||||||
checkForUpdates?.let { parcel.writeByte(if (it) 1 else 0) }
|
checkForUpdates?.let { parcel.writeByte(if (it) 1 else 0) }
|
||||||
|
// Side padding
|
||||||
|
parcel.writeByte((if (sidePadding != null) 1 else 0).toByte())
|
||||||
|
sidePadding?.let { parcel.writeInt(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun describeContents(): Int {
|
override fun describeContents(): Int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object CREATOR : Parcelable.Creator<MainSettingsState> {
|
companion object CREATOR : Parcelable.Creator<MainState> {
|
||||||
override fun createFromParcel(parcel: Parcel): MainSettingsState {
|
override fun createFromParcel(parcel: Parcel): MainState {
|
||||||
return MainSettingsState(parcel)
|
return MainState(parcel)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun newArray(size: Int): Array<MainSettingsState?> {
|
override fun newArray(size: Int): Array<MainState?> {
|
||||||
return arrayOfNulls(size)
|
return arrayOfNulls(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes [MainSettingsState] by given [Map].
|
* Initializes [MainState] by given [Map].
|
||||||
*/
|
*/
|
||||||
fun initialize(data: Map<String, Any>): MainSettingsState {
|
fun initialize(data: Map<String, Any>): MainState {
|
||||||
DataStoreConstants.apply {
|
DataStoreConstants.apply {
|
||||||
|
|
||||||
val language: String = data[LANGUAGE.name] as? String ?: if (
|
val language: String = data[LANGUAGE.name] as? String ?: if (
|
||||||
|
|
@ -186,7 +192,10 @@ data class MainSettingsState(
|
||||||
val checkForUpdates: Boolean = data[CHECK_FOR_UPDATES.name] as? Boolean
|
val checkForUpdates: Boolean = data[CHECK_FOR_UPDATES.name] as? Boolean
|
||||||
?: false
|
?: false
|
||||||
|
|
||||||
return MainSettingsState(
|
val sidePadding: Int = data[SIDE_PADDING.name] as? Int
|
||||||
|
?: 6
|
||||||
|
|
||||||
|
return MainState(
|
||||||
language = language,
|
language = language,
|
||||||
theme = theme.toTheme(),
|
theme = theme.toTheme(),
|
||||||
darkTheme = darkTheme.toDarkTheme(),
|
darkTheme = darkTheme.toDarkTheme(),
|
||||||
|
|
@ -201,7 +210,8 @@ data class MainSettingsState(
|
||||||
lineHeight = lineHeight,
|
lineHeight = lineHeight,
|
||||||
paragraphHeight = paragraphHeight,
|
paragraphHeight = paragraphHeight,
|
||||||
paragraphIndentation = paragraphIndentation,
|
paragraphIndentation = paragraphIndentation,
|
||||||
checkForUpdates = checkForUpdates
|
checkForUpdates = checkForUpdates,
|
||||||
|
sidePadding = sidePadding
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -44,8 +44,8 @@ class MainViewModel @Inject constructor(
|
||||||
private val isSettingsReady = MutableStateFlow(false)
|
private val isSettingsReady = MutableStateFlow(false)
|
||||||
private val isViewModelReady = MutableStateFlow(false)
|
private val isViewModelReady = MutableStateFlow(false)
|
||||||
|
|
||||||
private val _state: MutableStateFlow<MainSettingsState> = MutableStateFlow(
|
private val _state: MutableStateFlow<MainState> = MutableStateFlow(
|
||||||
stateHandle[Constants.MAIN_STATE] ?: MainSettingsState()
|
stateHandle[Constants.MAIN_STATE] ?: MainState()
|
||||||
)
|
)
|
||||||
val state = _state.asStateFlow()
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
|
|
@ -216,6 +216,17 @@ class MainViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is MainEvent.OnChangeSidePadding -> {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
setDatastore.execute(DataStoreConstants.SIDE_PADDING, event.sidePadding)
|
||||||
|
updateStateWithSavedHandle {
|
||||||
|
it.copy(
|
||||||
|
sidePadding = event.sidePadding
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,10 +281,10 @@ class MainViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates [MainSettingsState] along with [SavedStateHandle].
|
* Updates [MainState] along with [SavedStateHandle].
|
||||||
*/
|
*/
|
||||||
private fun updateStateWithSavedHandle(
|
private fun updateStateWithSavedHandle(
|
||||||
function: (MainSettingsState) -> MainSettingsState
|
function: (MainState) -> MainState
|
||||||
) {
|
) {
|
||||||
_state.update {
|
_state.update {
|
||||||
stateHandle[Constants.MAIN_STATE] = function(it)
|
stateHandle[Constants.MAIN_STATE] = function(it)
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,10 @@ enum class Screen {
|
||||||
READER_SETTINGS,
|
READER_SETTINGS,
|
||||||
|
|
||||||
ABOUT,
|
ABOUT,
|
||||||
|
LICENSES,
|
||||||
|
LICENSES_INFO,
|
||||||
|
CREDITS,
|
||||||
|
|
||||||
HELP,
|
HELP,
|
||||||
START
|
START
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import ua.acclorite.book_story.presentation.components.GoBackButton
|
||||||
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
||||||
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
||||||
import ua.acclorite.book_story.presentation.data.Navigator
|
import ua.acclorite.book_story.presentation.data.Navigator
|
||||||
|
import ua.acclorite.book_story.presentation.data.Screen
|
||||||
import ua.acclorite.book_story.presentation.screens.about.components.AboutItem
|
import ua.acclorite.book_story.presentation.screens.about.components.AboutItem
|
||||||
import ua.acclorite.book_story.presentation.screens.about.components.AboutUpdateDialog
|
import ua.acclorite.book_story.presentation.screens.about.components.AboutUpdateDialog
|
||||||
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
|
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
|
||||||
|
|
@ -207,6 +208,24 @@ private fun AboutScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item {
|
||||||
|
AboutItem(
|
||||||
|
title = stringResource(id = R.string.licenses_option),
|
||||||
|
description = null
|
||||||
|
) {
|
||||||
|
navigator.navigate(Screen.LICENSES, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item {
|
||||||
|
AboutItem(
|
||||||
|
title = stringResource(id = R.string.credits_option),
|
||||||
|
description = null
|
||||||
|
) {
|
||||||
|
navigator.navigate(Screen.CREDITS, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
item {
|
item {
|
||||||
AboutItem(
|
AboutItem(
|
||||||
title = stringResource(id = R.string.support_option),
|
title = stringResource(id = R.string.support_option),
|
||||||
|
|
|
||||||
|
|
@ -38,8 +38,8 @@ fun AboutItem(
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
fontSize = 18.sp
|
fontSize = 18.sp
|
||||||
)
|
)
|
||||||
description?.let {
|
description?.let {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ fun AboutUpdateDialog(
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomDialogWithContent(
|
CustomDialogWithContent(
|
||||||
title = stringResource(id = R.string.update_query, update.tag_name),
|
title = stringResource(id = R.string.update_query, update.tagName),
|
||||||
imageVectorIcon = Icons.Default.Update,
|
imageVectorIcon = Icons.Default.Update,
|
||||||
description = stringResource(
|
description = stringResource(
|
||||||
id = R.string.update_app_description
|
id = R.string.update_app_description
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ class AboutViewModel @Inject constructor(
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
val version = result.tag_name.substringAfterLast("v")
|
val version = result.tagName.substringAfterLast("v")
|
||||||
val currentVersion = event.context.getString(R.string.app_version)
|
val currentVersion = event.context.getString(R.string.app_version)
|
||||||
|
|
||||||
if (version == currentVersion) {
|
if (version == currentVersion) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.about.nested.credits
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.LargeTopAppBar
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import ua.acclorite.book_story.R
|
||||||
|
import ua.acclorite.book_story.domain.util.Constants
|
||||||
|
import ua.acclorite.book_story.presentation.components.GoBackButton
|
||||||
|
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
||||||
|
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
||||||
|
import ua.acclorite.book_story.presentation.data.Navigator
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.data.AboutEvent
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.data.AboutViewModel
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.credits.components.CreditItem
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CreditsScreenRoot() {
|
||||||
|
val navigator = LocalNavigator.current
|
||||||
|
val aboutViewModel: AboutViewModel = hiltViewModel()
|
||||||
|
|
||||||
|
CreditsScreen(
|
||||||
|
navigator = navigator,
|
||||||
|
onAboutNavigateEvent = aboutViewModel::onEvent
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun CreditsScreen(
|
||||||
|
navigator: Navigator,
|
||||||
|
onAboutNavigateEvent: (AboutEvent.OnNavigateToBrowserPage) -> Unit
|
||||||
|
) {
|
||||||
|
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.nestedScroll(scrollState.first.nestedScrollConnection)
|
||||||
|
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
topBar = {
|
||||||
|
LargeTopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(stringResource(id = R.string.credits_option))
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
GoBackButton(navigator = navigator)
|
||||||
|
},
|
||||||
|
scrollBehavior = scrollState.first,
|
||||||
|
colors = TopAppBarDefaults.largeTopAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
LazyColumn(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(top = paddingValues.calculateTopPadding()),
|
||||||
|
state = scrollState.second
|
||||||
|
) {
|
||||||
|
items(Constants.CREDITS, key = { it.name }) {
|
||||||
|
CreditItem(credit = it) {
|
||||||
|
onAboutNavigateEvent(
|
||||||
|
AboutEvent.OnNavigateToBrowserPage(
|
||||||
|
page = it.website,
|
||||||
|
context = context,
|
||||||
|
noAppsFound = {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.error_no_browser),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.about.nested.credits.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
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.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import ua.acclorite.book_story.domain.model.Credit
|
||||||
|
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
|
@Composable
|
||||||
|
fun CreditItem(
|
||||||
|
credit: Credit,
|
||||||
|
horizontalPadding: Dp = 18.dp,
|
||||||
|
verticalPadding: Dp = 12.dp,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { onClick.invoke() }
|
||||||
|
.padding(vertical = verticalPadding, horizontal = horizontalPadding)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = credit.name,
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = credit.source,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
|
||||||
|
if (credit.credits.isNotEmpty()) {
|
||||||
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
FlowRow(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
credit.credits.forEach {
|
||||||
|
Text(
|
||||||
|
text = it.asString(),
|
||||||
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(100))
|
||||||
|
.background(MaterialTheme.colorScheme.primary)
|
||||||
|
.padding(vertical = 4.dp, horizontal = 8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,232 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.about.nested.license_info
|
||||||
|
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Language
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.LargeTopAppBar
|
||||||
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import ua.acclorite.book_story.R
|
||||||
|
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
|
||||||
|
import ua.acclorite.book_story.presentation.components.CustomIconButton
|
||||||
|
import ua.acclorite.book_story.presentation.components.GoBackButton
|
||||||
|
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
||||||
|
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
||||||
|
import ua.acclorite.book_story.presentation.data.Navigator
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.license_info.data.LicenseInfoEvent
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.license_info.data.LicenseInfoState
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.license_info.data.LicenseInfoViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LicenseInfoScreenRoot() {
|
||||||
|
val navigator = LocalNavigator.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
val licenseInfoViewModel: LicenseInfoViewModel = hiltViewModel()
|
||||||
|
|
||||||
|
val state = licenseInfoViewModel.state.collectAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
licenseInfoViewModel.init(
|
||||||
|
navigator,
|
||||||
|
context
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
LicenseInfoScreen(
|
||||||
|
state = state,
|
||||||
|
onEvent = licenseInfoViewModel::onEvent,
|
||||||
|
navigator = navigator
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun LicenseInfoScreen(
|
||||||
|
state: State<LicenseInfoState>,
|
||||||
|
onEvent: (LicenseInfoEvent) -> Unit,
|
||||||
|
navigator: Navigator
|
||||||
|
) {
|
||||||
|
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
if (state.value.license != null) {
|
||||||
|
val licenses = remember(state.value) {
|
||||||
|
state.value.license!!.licenses.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.nestedScroll(scrollState.first.nestedScrollConnection)
|
||||||
|
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
topBar = {
|
||||||
|
LargeTopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
state.value.license!!.name,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
GoBackButton(navigator = navigator)
|
||||||
|
},
|
||||||
|
actions = {
|
||||||
|
if (state.value.license?.website?.isNotBlank() == true) {
|
||||||
|
CustomIconButton(
|
||||||
|
icon = Icons.Outlined.Language,
|
||||||
|
contentDescription = R.string.open_in_web_content_desc,
|
||||||
|
disableOnClick = false
|
||||||
|
) {
|
||||||
|
var url = state.value.license?.website!!
|
||||||
|
if (!url.startsWith("http://") && !url.startsWith("https://")) {
|
||||||
|
url = "https://$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
onEvent(
|
||||||
|
LicenseInfoEvent.OnOpenLicensePage(
|
||||||
|
page = url,
|
||||||
|
context = context,
|
||||||
|
noAppsFound = {
|
||||||
|
Toast.makeText(
|
||||||
|
context,
|
||||||
|
context.getString(R.string.error_no_browser),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scrollBehavior = scrollState.first,
|
||||||
|
colors = TopAppBarDefaults.largeTopAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
LazyColumn(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(top = paddingValues.calculateTopPadding()),
|
||||||
|
state = scrollState.second,
|
||||||
|
contentPadding = PaddingValues(vertical = 12.dp, horizontal = 16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
items(licenses, key = { it.name }) { license ->
|
||||||
|
val showed = remember {
|
||||||
|
mutableStateOf(licenses.size == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.animateItem(
|
||||||
|
fadeInSpec = null,
|
||||||
|
fadeOutSpec = null
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = license.name,
|
||||||
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
style = MaterialTheme.typography.titleSmall,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(100))
|
||||||
|
.clickable(enabled = license.licenseContent?.isNotBlank() == true) {
|
||||||
|
showed.value = !showed.value
|
||||||
|
}
|
||||||
|
.background(MaterialTheme.colorScheme.primary)
|
||||||
|
.padding(vertical = 6.dp, horizontal = 12.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
CustomAnimatedVisibility(
|
||||||
|
visible = showed.value && license.licenseContent?.isNotBlank() == true,
|
||||||
|
enter = slideInVertically(tween(300)) { -50 } +
|
||||||
|
fadeIn(tween(300)),
|
||||||
|
exit = slideOutVertically(tween(200)) { -30 } +
|
||||||
|
fadeOut(tween(150)),
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp)
|
||||||
|
) {
|
||||||
|
Column {
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
Text(
|
||||||
|
text = license.licenseContent!!
|
||||||
|
.lines()
|
||||||
|
.joinToString(separator = "\n") {
|
||||||
|
it.trim()
|
||||||
|
},
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Column(
|
||||||
|
Modifier.fillMaxSize(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(id = R.string.loading),
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
LinearProgressIndicator(
|
||||||
|
modifier = Modifier.fillMaxWidth(0.55f),
|
||||||
|
strokeCap = StrokeCap.Round,
|
||||||
|
trackColor = MaterialTheme.colorScheme.secondaryContainer.copy(0.7f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.about.nested.license_info.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
sealed class LicenseInfoEvent {
|
||||||
|
data class OnOpenLicensePage(
|
||||||
|
val page: String,
|
||||||
|
val context: Context,
|
||||||
|
val noAppsFound: () -> Unit
|
||||||
|
) : LicenseInfoEvent()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.about.nested.license_info.data
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.mikepenz.aboutlibraries.entity.Library
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class LicenseInfoState(
|
||||||
|
val license: Library? = null,
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.about.nested.license_info.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.mikepenz.aboutlibraries.Libs
|
||||||
|
import com.mikepenz.aboutlibraries.util.withContext
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import ua.acclorite.book_story.presentation.data.Navigator
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class LicenseInfoViewModel @Inject constructor() : ViewModel() {
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow(LicenseInfoState())
|
||||||
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
|
fun onEvent(event: LicenseInfoEvent) {
|
||||||
|
when (event) {
|
||||||
|
is LicenseInfoEvent.OnOpenLicensePage -> {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val intent = Intent(
|
||||||
|
Intent.ACTION_VIEW,
|
||||||
|
Uri.parse(event.page)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (intent.resolveActivity(event.context.packageManager) != null) {
|
||||||
|
event.context.startActivity(intent)
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
event.noAppsFound()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init(navigator: Navigator, context: Context) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
_state.update {
|
||||||
|
it.copy(license = null)
|
||||||
|
}
|
||||||
|
|
||||||
|
val licenseId = navigator.retrieveArgument("license") as? String
|
||||||
|
|
||||||
|
if (licenseId == null) {
|
||||||
|
navigator.navigateBack()
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val license = Libs.Builder().withContext(context).build().libraries.find {
|
||||||
|
it.uniqueId == licenseId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (license == null) {
|
||||||
|
navigator.navigateBack()
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
_state.update {
|
||||||
|
it.copy(
|
||||||
|
license = license
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.about.nested.licenses
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.LargeTopAppBar
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
|
import ua.acclorite.book_story.R
|
||||||
|
import ua.acclorite.book_story.presentation.components.CustomAnimatedVisibility
|
||||||
|
import ua.acclorite.book_story.presentation.components.GoBackButton
|
||||||
|
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
||||||
|
import ua.acclorite.book_story.presentation.data.Argument
|
||||||
|
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
||||||
|
import ua.acclorite.book_story.presentation.data.Navigator
|
||||||
|
import ua.acclorite.book_story.presentation.data.Screen
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.components.LicenseItem
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.data.LicensesState
|
||||||
|
import ua.acclorite.book_story.presentation.screens.about.nested.licenses.data.LicensesViewModel
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun LicensesScreenRoot() {
|
||||||
|
val navigator = LocalNavigator.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
val licensesViewModel: LicensesViewModel = hiltViewModel()
|
||||||
|
|
||||||
|
val state = licensesViewModel.state.collectAsState()
|
||||||
|
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
licensesViewModel.init(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
LicensesScreen(
|
||||||
|
state = state,
|
||||||
|
navigator = navigator
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun LicensesScreen(
|
||||||
|
state: State<LicensesState>,
|
||||||
|
navigator: Navigator
|
||||||
|
) {
|
||||||
|
val scrollState = TopAppBarDefaults.collapsibleUntilExitScrollBehaviorWithLazyListState()
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.nestedScroll(scrollState.first.nestedScrollConnection)
|
||||||
|
.windowInsetsPadding(WindowInsets.navigationBars),
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
topBar = {
|
||||||
|
LargeTopAppBar(
|
||||||
|
title = {
|
||||||
|
Text(stringResource(id = R.string.licenses_option))
|
||||||
|
},
|
||||||
|
navigationIcon = {
|
||||||
|
GoBackButton(navigator = navigator)
|
||||||
|
},
|
||||||
|
scrollBehavior = scrollState.first,
|
||||||
|
colors = TopAppBarDefaults.largeTopAppBarColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface,
|
||||||
|
scrolledContainerColor = MaterialTheme.colorScheme.surfaceContainer
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { paddingValues ->
|
||||||
|
CustomAnimatedVisibility(
|
||||||
|
visible = state.value.licenses.isNotEmpty(),
|
||||||
|
enter = fadeIn(tween(300)),
|
||||||
|
exit = fadeOut(tween(300))
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(top = paddingValues.calculateTopPadding()),
|
||||||
|
state = scrollState.second,
|
||||||
|
contentPadding = PaddingValues(vertical = 12.dp)
|
||||||
|
) {
|
||||||
|
items(state.value.licenses, key = { it.uniqueId }) {
|
||||||
|
LicenseItem(library = it) {
|
||||||
|
navigator.navigate(
|
||||||
|
Screen.LICENSES_INFO,
|
||||||
|
false,
|
||||||
|
Argument("license", it.uniqueId)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.about.nested.licenses.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
|
import androidx.compose.foundation.layout.FlowRow
|
||||||
|
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.width
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.mikepenz.aboutlibraries.entity.Library
|
||||||
|
import com.mikepenz.aboutlibraries.ui.compose.m3.util.author
|
||||||
|
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
|
@Composable
|
||||||
|
fun LicenseItem(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
library: Library,
|
||||||
|
horizontalPadding: Dp = 18.dp,
|
||||||
|
verticalPadding: Dp = 12.dp,
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { onClick.invoke() }
|
||||||
|
.padding(vertical = verticalPadding, horizontal = horizontalPadding)
|
||||||
|
) {
|
||||||
|
Row {
|
||||||
|
Text(
|
||||||
|
text = library.name,
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
maxLines = 2,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
library.artifactVersion?.let {
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
it,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (library.author.isNotBlank()) {
|
||||||
|
Text(
|
||||||
|
text = library.author,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (library.licenses.isNotEmpty()) {
|
||||||
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
FlowRow(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
library.licenses.forEach {
|
||||||
|
Text(
|
||||||
|
text = it.name,
|
||||||
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(100))
|
||||||
|
.background(MaterialTheme.colorScheme.primary)
|
||||||
|
.padding(vertical = 4.dp, horizontal = 8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.about.nested.licenses.data
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Immutable
|
||||||
|
import com.mikepenz.aboutlibraries.entity.Library
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
data class LicensesState(
|
||||||
|
val licenses: List<Library> = emptyList()
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package ua.acclorite.book_story.presentation.screens.about.nested.licenses.data
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.mikepenz.aboutlibraries.Libs
|
||||||
|
import com.mikepenz.aboutlibraries.util.withContext
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class LicensesViewModel @Inject constructor(
|
||||||
|
|
||||||
|
) : ViewModel() {
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow(LicensesState())
|
||||||
|
val state = _state.asStateFlow()
|
||||||
|
|
||||||
|
fun init(context: Context) {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
if (_state.value.licenses.isNotEmpty()) {
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
val licenses = Libs
|
||||||
|
.Builder()
|
||||||
|
.withContext(context)
|
||||||
|
.build()
|
||||||
|
.libraries
|
||||||
|
.toList()
|
||||||
|
|
||||||
|
_state.update {
|
||||||
|
it.copy(
|
||||||
|
licenses = licenses
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -62,7 +62,7 @@ import ua.acclorite.book_story.presentation.components.CustomSelectionContainer
|
||||||
import ua.acclorite.book_story.presentation.components.is_messages.IsError
|
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.LocalNavigator
|
||||||
import ua.acclorite.book_story.presentation.data.MainEvent
|
import ua.acclorite.book_story.presentation.data.MainEvent
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import ua.acclorite.book_story.presentation.data.MainViewModel
|
import ua.acclorite.book_story.presentation.data.MainViewModel
|
||||||
import ua.acclorite.book_story.presentation.data.Navigator
|
import ua.acclorite.book_story.presentation.data.Navigator
|
||||||
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
|
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
|
||||||
|
|
@ -127,7 +127,7 @@ fun ReaderScreenRoot() {
|
||||||
@Composable
|
@Composable
|
||||||
private fun ReaderScreen(
|
private fun ReaderScreen(
|
||||||
state: State<ReaderState>,
|
state: State<ReaderState>,
|
||||||
mainState: State<MainSettingsState>,
|
mainState: State<MainState>,
|
||||||
listState: LazyListState,
|
listState: LazyListState,
|
||||||
loading: State<Boolean>,
|
loading: State<Boolean>,
|
||||||
navigator: Navigator,
|
navigator: Navigator,
|
||||||
|
|
@ -167,6 +167,29 @@ private fun ReaderScreen(
|
||||||
it.id == mainState.value.fontFamily
|
it.id == mainState.value.fontFamily
|
||||||
} ?: Constants.FONTS[0]
|
} ?: Constants.FONTS[0]
|
||||||
}
|
}
|
||||||
|
val fontColor = remember(mainState.value.fontColor) {
|
||||||
|
Color(mainState.value.fontColor!!.toULong())
|
||||||
|
}
|
||||||
|
val backgroundColor = remember(mainState.value.backgroundColor) {
|
||||||
|
Color(mainState.value.backgroundColor!!.toULong())
|
||||||
|
}
|
||||||
|
val lineHeight =
|
||||||
|
remember(mainState.value.fontSize, mainState.value.lineHeight) {
|
||||||
|
(mainState.value.fontSize!! + mainState.value.lineHeight!!).sp
|
||||||
|
}
|
||||||
|
val sidePadding = remember(mainState.value.sidePadding) {
|
||||||
|
(mainState.value.sidePadding!! * 3).dp
|
||||||
|
}
|
||||||
|
val paragraphHeight = remember(mainState.value.paragraphHeight) {
|
||||||
|
(mainState.value.paragraphHeight!! * 3).dp
|
||||||
|
}
|
||||||
|
val fontStyle = remember(mainState.value.isItalic) {
|
||||||
|
when (mainState.value.isItalic!!) {
|
||||||
|
true -> FontStyle.Italic
|
||||||
|
false -> FontStyle.Normal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
LaunchedEffect(canScroll) {
|
LaunchedEffect(canScroll) {
|
||||||
if (!canScroll && !state.value.showMenu && !loading.value) {
|
if (!canScroll && !state.value.showMenu && !loading.value) {
|
||||||
|
|
@ -329,16 +352,6 @@ private fun ReaderScreen(
|
||||||
val text = remember(mainState.value.paragraphIndentation) {
|
val text = remember(mainState.value.paragraphIndentation) {
|
||||||
"${if (mainState.value.paragraphIndentation!!) " " else ""}${line.line}"
|
"${if (mainState.value.paragraphIndentation!!) " " else ""}${line.line}"
|
||||||
}
|
}
|
||||||
val fontColor = remember(mainState.value.fontColor) {
|
|
||||||
Color(mainState.value.fontColor!!.toULong())
|
|
||||||
}
|
|
||||||
val backgroundColor = remember(mainState.value.backgroundColor) {
|
|
||||||
Color(mainState.value.backgroundColor!!.toULong())
|
|
||||||
}
|
|
||||||
val lineHeight =
|
|
||||||
remember(mainState.value.fontSize, mainState.value.lineHeight) {
|
|
||||||
(mainState.value.fontSize!! + mainState.value.lineHeight!!).sp
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
|
|
@ -346,23 +359,18 @@ private fun ReaderScreen(
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(
|
.padding(
|
||||||
top = if (index == 0) 18.dp else 0.dp,
|
top = if (index == 0) 18.dp else 0.dp,
|
||||||
start = 18.dp,
|
start = sidePadding,
|
||||||
end = 18.dp,
|
end = sidePadding,
|
||||||
bottom = if (index == state.value.book.text.lastIndex) 18.dp
|
bottom = if (index == state.value.book.text.lastIndex) 18.dp
|
||||||
else (mainState.value.paragraphHeight!! * 3).dp
|
else paragraphHeight
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = text,
|
text = text,
|
||||||
color = fontColor,
|
color = fontColor,
|
||||||
style = TextStyle(
|
style = TextStyle(lineBreak = LineBreak.Paragraph),
|
||||||
lineBreak = LineBreak.Paragraph
|
|
||||||
),
|
|
||||||
fontFamily = fontFamily.font,
|
fontFamily = fontFamily.font,
|
||||||
fontStyle = when (mainState.value.isItalic!!) {
|
fontStyle = fontStyle,
|
||||||
true -> FontStyle.Italic
|
|
||||||
false -> FontStyle.Normal
|
|
||||||
},
|
|
||||||
fontSize = mainState.value.fontSize!!.sp,
|
fontSize = mainState.value.fontSize!!.sp,
|
||||||
lineHeight = lineHeight
|
lineHeight = lineHeight
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ import ua.acclorite.book_story.domain.model.ButtonItem
|
||||||
import ua.acclorite.book_story.domain.util.Constants
|
import ua.acclorite.book_story.domain.util.Constants
|
||||||
import ua.acclorite.book_story.presentation.components.CategoryTitle
|
import ua.acclorite.book_story.presentation.components.CategoryTitle
|
||||||
import ua.acclorite.book_story.presentation.data.MainEvent
|
import ua.acclorite.book_story.presentation.data.MainEvent
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
|
import ua.acclorite.book_story.presentation.screens.reader.data.ReaderEvent
|
||||||
import ua.acclorite.book_story.presentation.screens.settings.components.CheckboxWithTitle
|
import ua.acclorite.book_story.presentation.screens.settings.components.CheckboxWithTitle
|
||||||
import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle
|
import ua.acclorite.book_story.presentation.screens.settings.components.ChipsWithTitle
|
||||||
|
|
@ -54,7 +54,7 @@ import ua.acclorite.book_story.presentation.screens.settings.components.SliderWi
|
||||||
)
|
)
|
||||||
@Composable
|
@Composable
|
||||||
fun ReaderSettingsBottomSheet(
|
fun ReaderSettingsBottomSheet(
|
||||||
mainState: State<MainSettingsState>,
|
mainState: State<MainState>,
|
||||||
onEvent: (ReaderEvent) -> Unit,
|
onEvent: (ReaderEvent) -> Unit,
|
||||||
onMainEvent: (MainEvent) -> Unit
|
onMainEvent: (MainEvent) -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
@ -212,6 +212,19 @@ fun ReaderSettingsBottomSheet(
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
|
item {
|
||||||
|
SliderWithTitle(
|
||||||
|
value = mainState.value.sidePadding!! to "pt",
|
||||||
|
fromValue = 1,
|
||||||
|
toValue = 20,
|
||||||
|
title = stringResource(id = R.string.side_padding_option),
|
||||||
|
onValueChange = {
|
||||||
|
onMainEvent(
|
||||||
|
MainEvent.OnChangeSidePadding(it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
item {
|
item {
|
||||||
SliderWithTitle(
|
SliderWithTitle(
|
||||||
value = mainState.value.lineHeight!! to "pt",
|
value = mainState.value.lineHeight!! to "pt",
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,13 @@ class ReaderViewModel @Inject constructor(
|
||||||
updateBooks.execute(
|
updateBooks.execute(
|
||||||
listOf(_state.value.book)
|
listOf(_state.value.book)
|
||||||
)
|
)
|
||||||
event.refreshList(_state.value.book)
|
event.refreshList(
|
||||||
|
_state.value.book.copy(
|
||||||
|
text = emptyList(),
|
||||||
|
letters = 0,
|
||||||
|
words = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
snapshotFlow {
|
snapshotFlow {
|
||||||
|
|
@ -231,7 +237,13 @@ class ReaderViewModel @Inject constructor(
|
||||||
event.navigator.putArgument(
|
event.navigator.putArgument(
|
||||||
Argument("book", _state.value.book.id)
|
Argument("book", _state.value.book.id)
|
||||||
)
|
)
|
||||||
event.refreshList(_state.value.book)
|
event.refreshList(
|
||||||
|
_state.value.book.copy(
|
||||||
|
text = emptyList(),
|
||||||
|
letters = 0,
|
||||||
|
words = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
insetsController.show(WindowInsetsCompat.Type.systemBars())
|
insetsController.show(WindowInsetsCompat.Type.systemBars())
|
||||||
event.navigate(event.navigator)
|
event.navigate(event.navigator)
|
||||||
|
|
@ -269,7 +281,13 @@ class ReaderViewModel @Inject constructor(
|
||||||
_state.value.book.id
|
_state.value.book.id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
event.refreshList(_state.value.book)
|
event.refreshList(
|
||||||
|
_state.value.book.copy(
|
||||||
|
text = emptyList(),
|
||||||
|
letters = 0,
|
||||||
|
words = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,7 +335,13 @@ class ReaderViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
updateBooks.execute(listOf(_state.value.book))
|
updateBooks.execute(listOf(_state.value.book))
|
||||||
|
|
||||||
event.onUpdateCategories(_state.value.book)
|
event.onUpdateCategories(
|
||||||
|
_state.value.book.copy(
|
||||||
|
text = emptyList(),
|
||||||
|
letters = 0,
|
||||||
|
words = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
event.updatePage(
|
event.updatePage(
|
||||||
Category.entries.dropLastWhile {
|
Category.entries.dropLastWhile {
|
||||||
it != Category.ALREADY_READ
|
it != Category.ALREADY_READ
|
||||||
|
|
@ -396,21 +420,27 @@ class ReaderViewModel @Inject constructor(
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
val book = getBooksById.execute(listOf(bookId))
|
if (_state.value.book.id != bookId) {
|
||||||
|
val book = getBooksById.execute(listOf(bookId))
|
||||||
|
|
||||||
if (book.isEmpty()) {
|
if (book.isEmpty()) {
|
||||||
navigator.navigateBack()
|
navigator.navigateBack()
|
||||||
return@launch
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
_state.update {
|
||||||
|
ReaderState(book = book.first())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_state.update {
|
||||||
|
ReaderState(book = it.book)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
onEvent(ReaderEvent.OnShowHideMenu(false, context))
|
onEvent(ReaderEvent.OnShowHideMenu(false, context))
|
||||||
}
|
}
|
||||||
|
|
||||||
_state.update {
|
|
||||||
ReaderState(book = book.first())
|
|
||||||
}
|
|
||||||
|
|
||||||
onEvent(
|
onEvent(
|
||||||
ReaderEvent.OnLoadText(
|
ReaderEvent.OnLoadText(
|
||||||
refreshList = { refreshList(it) },
|
refreshList = { refreshList(it) },
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import ua.acclorite.book_story.presentation.components.GoBackButton
|
||||||
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
||||||
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
||||||
import ua.acclorite.book_story.presentation.data.MainEvent
|
import ua.acclorite.book_story.presentation.data.MainEvent
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import ua.acclorite.book_story.presentation.data.MainViewModel
|
import ua.acclorite.book_story.presentation.data.MainViewModel
|
||||||
import ua.acclorite.book_story.presentation.data.Navigator
|
import ua.acclorite.book_story.presentation.data.Navigator
|
||||||
import ua.acclorite.book_story.presentation.screens.settings.components.ColorPickerWithTitle
|
import ua.acclorite.book_story.presentation.screens.settings.components.ColorPickerWithTitle
|
||||||
|
|
@ -66,7 +66,7 @@ fun AppearanceSettingsRoot() {
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun AppearanceSettings(
|
private fun AppearanceSettings(
|
||||||
state: State<MainSettingsState>,
|
state: State<MainState>,
|
||||||
navigator: Navigator,
|
navigator: Navigator,
|
||||||
onMainEvent: (MainEvent) -> Unit
|
onMainEvent: (MainEvent) -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.domain.util.Constants
|
import ua.acclorite.book_story.domain.util.Constants
|
||||||
import ua.acclorite.book_story.presentation.components.CategoryTitle
|
import ua.acclorite.book_story.presentation.components.CategoryTitle
|
||||||
import ua.acclorite.book_story.presentation.data.MainEvent
|
import ua.acclorite.book_story.presentation.data.MainEvent
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import ua.acclorite.book_story.presentation.ui.Theme
|
import ua.acclorite.book_story.presentation.ui.Theme
|
||||||
import ua.acclorite.book_story.presentation.ui.isDark
|
import ua.acclorite.book_story.presentation.ui.isDark
|
||||||
import ua.acclorite.book_story.presentation.ui.isPureDark
|
import ua.acclorite.book_story.presentation.ui.isPureDark
|
||||||
|
|
@ -32,7 +32,7 @@ import ua.acclorite.book_story.presentation.ui.isPureDark
|
||||||
@Composable
|
@Composable
|
||||||
fun AppearanceSettingsThemeSwitcher(
|
fun AppearanceSettingsThemeSwitcher(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
state: State<MainSettingsState>,
|
state: State<MainState>,
|
||||||
onMainEvent: (MainEvent) -> Unit,
|
onMainEvent: (MainEvent) -> Unit,
|
||||||
verticalPadding: Dp = 8.dp
|
verticalPadding: Dp = 8.dp
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import ua.acclorite.book_story.presentation.components.GoBackButton
|
||||||
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
||||||
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
||||||
import ua.acclorite.book_story.presentation.data.MainEvent
|
import ua.acclorite.book_story.presentation.data.MainEvent
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import ua.acclorite.book_story.presentation.data.MainViewModel
|
import ua.acclorite.book_story.presentation.data.MainViewModel
|
||||||
import ua.acclorite.book_story.presentation.data.Navigator
|
import ua.acclorite.book_story.presentation.data.Navigator
|
||||||
import ua.acclorite.book_story.presentation.screens.settings.components.CheckboxWithTitle
|
import ua.acclorite.book_story.presentation.screens.settings.components.CheckboxWithTitle
|
||||||
|
|
@ -63,7 +63,7 @@ fun GeneralSettingsRoot() {
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun GeneralSettings(
|
private fun GeneralSettings(
|
||||||
state: State<MainSettingsState>,
|
state: State<MainState>,
|
||||||
navigator: Navigator,
|
navigator: Navigator,
|
||||||
onSettingsEvent: (SettingsEvent) -> Unit,
|
onSettingsEvent: (SettingsEvent) -> Unit,
|
||||||
onMainEvent: (MainEvent) -> Unit
|
onMainEvent: (MainEvent) -> Unit
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ import ua.acclorite.book_story.presentation.components.GoBackButton
|
||||||
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
import ua.acclorite.book_story.presentation.components.collapsibleUntilExitScrollBehaviorWithLazyListState
|
||||||
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
import ua.acclorite.book_story.presentation.data.LocalNavigator
|
||||||
import ua.acclorite.book_story.presentation.data.MainEvent
|
import ua.acclorite.book_story.presentation.data.MainEvent
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import ua.acclorite.book_story.presentation.data.MainViewModel
|
import ua.acclorite.book_story.presentation.data.MainViewModel
|
||||||
import ua.acclorite.book_story.presentation.data.Navigator
|
import ua.acclorite.book_story.presentation.data.Navigator
|
||||||
import ua.acclorite.book_story.presentation.screens.settings.components.CheckboxWithTitle
|
import ua.acclorite.book_story.presentation.screens.settings.components.CheckboxWithTitle
|
||||||
|
|
@ -56,7 +56,7 @@ fun ReaderSettingsRoot() {
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ReaderSettings(
|
private fun ReaderSettings(
|
||||||
state: State<MainSettingsState>,
|
state: State<MainState>,
|
||||||
navigator: Navigator,
|
navigator: Navigator,
|
||||||
onMainEvent: (MainEvent) -> Unit
|
onMainEvent: (MainEvent) -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
@ -187,6 +187,19 @@ private fun ReaderSettings(
|
||||||
item {
|
item {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
}
|
}
|
||||||
|
item {
|
||||||
|
SliderWithTitle(
|
||||||
|
value = state.value.sidePadding!! to "pt",
|
||||||
|
fromValue = 1,
|
||||||
|
toValue = 20,
|
||||||
|
title = stringResource(id = R.string.side_padding_option),
|
||||||
|
onValueChange = {
|
||||||
|
onMainEvent(
|
||||||
|
MainEvent.OnChangeSidePadding(it)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
item {
|
item {
|
||||||
SliderWithTitle(
|
SliderWithTitle(
|
||||||
value = state.value.lineHeight!! to "pt",
|
value = state.value.lineHeight!! to "pt",
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||||
import com.google.accompanist.permissions.PermissionState
|
import com.google.accompanist.permissions.PermissionState
|
||||||
import com.google.accompanist.permissions.rememberPermissionState
|
import com.google.accompanist.permissions.rememberPermissionState
|
||||||
import ua.acclorite.book_story.presentation.data.MainEvent
|
import ua.acclorite.book_story.presentation.data.MainEvent
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import ua.acclorite.book_story.presentation.data.MainViewModel
|
import ua.acclorite.book_story.presentation.data.MainViewModel
|
||||||
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
|
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseEvent
|
||||||
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
|
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
|
||||||
|
|
@ -69,7 +69,7 @@ fun StartScreenRoot() {
|
||||||
@Composable
|
@Composable
|
||||||
private fun StartScreen(
|
private fun StartScreen(
|
||||||
state: State<StartState>,
|
state: State<StartState>,
|
||||||
mainState: State<MainSettingsState>,
|
mainState: State<MainState>,
|
||||||
storagePermissionState: PermissionState,
|
storagePermissionState: PermissionState,
|
||||||
notificationsPermissionState: PermissionState,
|
notificationsPermissionState: PermissionState,
|
||||||
onEvent: (StartEvent) -> Unit,
|
onEvent: (StartEvent) -> Unit,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.domain.model.ButtonItem
|
import ua.acclorite.book_story.domain.model.ButtonItem
|
||||||
import ua.acclorite.book_story.presentation.components.CategoryTitle
|
import ua.acclorite.book_story.presentation.components.CategoryTitle
|
||||||
import ua.acclorite.book_story.presentation.data.MainEvent
|
import ua.acclorite.book_story.presentation.data.MainEvent
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
|
import ua.acclorite.book_story.presentation.screens.settings.components.SegmentedButtonWithTitle
|
||||||
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.theme_switcher.AppearanceSettingsThemeSwitcher
|
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.theme_switcher.AppearanceSettingsThemeSwitcher
|
||||||
import ua.acclorite.book_story.presentation.ui.BookStoryTheme
|
import ua.acclorite.book_story.presentation.ui.BookStoryTheme
|
||||||
|
|
@ -29,7 +29,7 @@ import ua.acclorite.book_story.presentation.ui.isPureDark
|
||||||
* Appearance settings.
|
* Appearance settings.
|
||||||
*/
|
*/
|
||||||
fun LazyListScope.startAppearanceScreen(
|
fun LazyListScope.startAppearanceScreen(
|
||||||
mainState: State<MainSettingsState>,
|
mainState: State<MainState>,
|
||||||
onMainEvent: (MainEvent) -> Unit,
|
onMainEvent: (MainEvent) -> Unit,
|
||||||
themeContrastTheme: State<Theme>
|
themeContrastTheme: State<Theme>
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import ua.acclorite.book_story.R
|
||||||
import ua.acclorite.book_story.domain.model.ButtonItem
|
import ua.acclorite.book_story.domain.model.ButtonItem
|
||||||
import ua.acclorite.book_story.domain.util.Constants
|
import ua.acclorite.book_story.domain.util.Constants
|
||||||
import ua.acclorite.book_story.presentation.data.MainEvent
|
import ua.acclorite.book_story.presentation.data.MainEvent
|
||||||
import ua.acclorite.book_story.presentation.data.MainSettingsState
|
import ua.acclorite.book_story.presentation.data.MainState
|
||||||
import ua.acclorite.book_story.presentation.screens.start.components.permissions.startPermissionsScreen
|
import ua.acclorite.book_story.presentation.screens.start.components.permissions.startPermissionsScreen
|
||||||
import ua.acclorite.book_story.presentation.screens.start.data.StartEvent
|
import ua.acclorite.book_story.presentation.screens.start.data.StartEvent
|
||||||
import ua.acclorite.book_story.presentation.screens.start.data.StartNavigationScreen
|
import ua.acclorite.book_story.presentation.screens.start.data.StartNavigationScreen
|
||||||
|
|
@ -49,7 +49,7 @@ import ua.acclorite.book_story.presentation.ui.Theme
|
||||||
@Composable
|
@Composable
|
||||||
fun StartSettings(
|
fun StartSettings(
|
||||||
state: State<StartState>,
|
state: State<StartState>,
|
||||||
mainState: State<MainSettingsState>,
|
mainState: State<MainState>,
|
||||||
onEvent: (StartEvent) -> Unit,
|
onEvent: (StartEvent) -> Unit,
|
||||||
onMainEvent: (MainEvent) -> Unit,
|
onMainEvent: (MainEvent) -> Unit,
|
||||||
storagePermissionState: PermissionState,
|
storagePermissionState: PermissionState,
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 20 KiB |
|
|
@ -206,6 +206,7 @@
|
||||||
<string name="font_color_option">Колір шрифта</string>
|
<string name="font_color_option">Колір шрифта</string>
|
||||||
<string name="check_for_updates_option">Перевіряти на оновлення</string>
|
<string name="check_for_updates_option">Перевіряти на оновлення</string>
|
||||||
<string name="check_for_updates_option_desc">Перевіряти на новішу версію застосунку при запуску</string>
|
<string name="check_for_updates_option_desc">Перевіряти на новішу версію застосунку при запуску</string>
|
||||||
|
<string name="side_padding_option">Бокові відступи</string>
|
||||||
|
|
||||||
<!-- Colors -->
|
<!-- Colors -->
|
||||||
<string name="red_color">Червоний</string>
|
<string name="red_color">Червоний</string>
|
||||||
|
|
@ -289,9 +290,16 @@
|
||||||
<string name="report_bug_option">Повідомити про помилку</string>
|
<string name="report_bug_option">Повідомити про помилку</string>
|
||||||
<string name="project_page_option">Сторінка проєкту</string>
|
<string name="project_page_option">Сторінка проєкту</string>
|
||||||
<string name="support_option">Підтримати мою роботу</string>
|
<string name="support_option">Підтримати мою роботу</string>
|
||||||
|
<string name="licenses_option">Ліцензії з відкритим кодом</string>
|
||||||
|
<string name="credits_option">Заслуги</string>
|
||||||
<string name="slava_ukraini_option">Слава Україні! 🇺🇦</string>
|
<string name="slava_ukraini_option">Слава Україні! 🇺🇦</string>
|
||||||
<string name="slava_ukraini_option_desc">Героям Слава! 🐉</string>
|
<string name="slava_ukraini_option_desc">Героям Слава! 🐉</string>
|
||||||
|
|
||||||
|
<!-- Credits -->
|
||||||
|
<string name="credits_design">Дизайн</string>
|
||||||
|
<string name="credits_icon">Іконки</string>
|
||||||
|
<string name="credits_updates">Керування новими оновленнями</string>
|
||||||
|
|
||||||
<!-- Help -->
|
<!-- Help -->
|
||||||
<string name="help_clickable_note_1">Будь ласка, зверніть увагу, що на</string>
|
<string name="help_clickable_note_1">Будь ласка, зверніть увагу, що на</string>
|
||||||
<string name="help_clickable_note_2">такий виділений текст</string>
|
<string name="help_clickable_note_2">такий виділений текст</string>
|
||||||
|
|
@ -464,6 +472,7 @@
|
||||||
<string name="required_content_desc">Необхідно</string>
|
<string name="required_content_desc">Необхідно</string>
|
||||||
<string name="reset_start_content_desc">Скинути початковий посібник</string>
|
<string name="reset_start_content_desc">Скинути початковий посібник</string>
|
||||||
<string name="hide_edit_title_content_desc">Вийти з редагування назви</string>
|
<string name="hide_edit_title_content_desc">Вийти з редагування назви</string>
|
||||||
|
<string name="open_in_web_content_desc">Відкрити в браузері</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="InconsistentArrays">
|
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="InconsistentArrays">
|
||||||
<!-- Basic Strings -->
|
<!-- Basic Strings -->
|
||||||
<string name="app_version" translatable="false">0.9.8</string>
|
<string name="app_version" translatable="false">0.9.9</string>
|
||||||
<string name="app_name">Book\'s Story</string>
|
<string name="app_name">Book\'s Story</string>
|
||||||
|
|
||||||
<!-- Notifications -->
|
<!-- Notifications -->
|
||||||
|
|
@ -225,6 +225,7 @@
|
||||||
<string name="font_color_option">Font color</string>
|
<string name="font_color_option">Font color</string>
|
||||||
<string name="check_for_updates_option">Check for updates</string>
|
<string name="check_for_updates_option">Check for updates</string>
|
||||||
<string name="check_for_updates_option_desc">Check for newer app version on startup</string>
|
<string name="check_for_updates_option_desc">Check for newer app version on startup</string>
|
||||||
|
<string name="side_padding_option">Side padding</string>
|
||||||
|
|
||||||
<!-- Colors -->
|
<!-- Colors -->
|
||||||
<string name="red_color">Red</string>
|
<string name="red_color">Red</string>
|
||||||
|
|
@ -308,9 +309,16 @@
|
||||||
<string name="report_bug_option">Report a bug</string>
|
<string name="report_bug_option">Report a bug</string>
|
||||||
<string name="project_page_option">Project\'s page</string>
|
<string name="project_page_option">Project\'s page</string>
|
||||||
<string name="support_option">Support my work</string>
|
<string name="support_option">Support my work</string>
|
||||||
|
<string name="licenses_option">Open source licenses</string>
|
||||||
|
<string name="credits_option">Credits</string>
|
||||||
<string name="slava_ukraini_option">Slava Ukraini! 🇺🇦</string>
|
<string name="slava_ukraini_option">Slava Ukraini! 🇺🇦</string>
|
||||||
<string name="slava_ukraini_option_desc">Heroyam Slava! 🐉</string>
|
<string name="slava_ukraini_option_desc">Heroyam Slava! 🐉</string>
|
||||||
|
|
||||||
|
<!-- Credits -->
|
||||||
|
<string name="credits_design">Design</string>
|
||||||
|
<string name="credits_icon">Icons</string>
|
||||||
|
<string name="credits_updates">New updates managing</string>
|
||||||
|
|
||||||
<!-- Help -->
|
<!-- Help -->
|
||||||
<string name="help_clickable_note_1">Please note, that</string>
|
<string name="help_clickable_note_1">Please note, that</string>
|
||||||
<string name="help_clickable_note_2">such highlighted text</string>
|
<string name="help_clickable_note_2">such highlighted text</string>
|
||||||
|
|
@ -484,5 +492,6 @@
|
||||||
<string name="required_content_desc">Required</string>
|
<string name="required_content_desc">Required</string>
|
||||||
<string name="reset_start_content_desc">Reset start guide</string>
|
<string name="reset_start_content_desc">Reset start guide</string>
|
||||||
<string name="hide_edit_title_content_desc">Exit title editing</string>
|
<string name="hide_edit_title_content_desc">Exit title editing</string>
|
||||||
|
<string name="open_in_web_content_desc">Open in web</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
@ -1,24 +1,6 @@
|
||||||
# Project-wide Gradle settings.
|
|
||||||
# IDE (e.g. Android Studio) users:
|
|
||||||
# Gradle settings configured through the IDE *will override*
|
|
||||||
# any settings specified in this file.
|
|
||||||
# For more details on how to configure your build environment visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
|
||||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
|
||||||
# org.gradle.parallel=true
|
|
||||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
|
||||||
# Android operating system, and which are packaged with your app's APK
|
|
||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
# Kotlin code style for this project: "official" or "obsolete":
|
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
# Enables namespacing of each library's R class so that its R class includes only the
|
|
||||||
# resources declared in the library itself and none from the library's dependencies,
|
|
||||||
# thereby reducing the size of the R class for that library
|
|
||||||
android.nonTransitiveRClass=true
|
android.nonTransitiveRClass=true
|
||||||
org.gradle.configuration-cache=true
|
org.gradle.configuration-cache=true
|
||||||
|
android.enableR8.fullMode=false
|
||||||
Loading…
Add table
Add a link
Reference in a new issue