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
|
||||
targetSdk = 34
|
||||
versionCode = 1
|
||||
versionName = "0.9.8"
|
||||
versionName = "0.9.9"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
|
|
@ -52,13 +52,24 @@ android {
|
|||
packaging {
|
||||
resources {
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
// Default
|
||||
|
|
@ -137,5 +148,5 @@ dependencies {
|
|||
|
||||
// About open source libraries
|
||||
implementation("com.mikepenz:aboutlibraries-core:latest.release")
|
||||
implementation("com.mikepenz:aboutlibraries-compose:latest.release")
|
||||
implementation("com.mikepenz:aboutlibraries-compose-m3:latest.release")
|
||||
}
|
||||
12
app/proguard-rules.pro
vendored
12
app/proguard-rules.pro
vendored
|
|
@ -1,14 +1,7 @@
|
|||
# Proguard-rules
|
||||
-dontwarn org.xmlpull.v1.**
|
||||
-dontwarn org.kxml2.io.**
|
||||
-dontwarn android.content.res.**
|
||||
-dontwarn org.slf4j.impl.StaticLoggerBinder
|
||||
-dontwarn com.gemalto.jp2.JP2Decoder
|
||||
-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.element.AnnotationMirror
|
||||
-dontwarn javax.lang.model.element.AnnotationValue
|
||||
|
|
@ -38,6 +31,11 @@
|
|||
-dontwarn javax.lang.model.util.SimpleTypeVisitor8
|
||||
-dontwarn javax.lang.model.util.Types
|
||||
-dontwarn javax.tools.Diagnostic$Kind
|
||||
-dontwarn org.kxml2.io.**
|
||||
|
||||
-keep class com.mikepenz.aboutlibraries.** { *; }
|
||||
-keep 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.screens.about.AboutScreenRoot
|
||||
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.data.BookInfoViewModel
|
||||
import ua.acclorite.book_story.presentation.screens.browse.BrowseScreenRoot
|
||||
|
|
@ -65,6 +69,7 @@ class Activity : AppCompatActivity() {
|
|||
private val readerViewModel: ReaderViewModel by viewModels()
|
||||
private val helpViewModel: HelpViewModel by viewModels()
|
||||
private val aboutViewModel: AboutViewModel by viewModels()
|
||||
private val licenseInfoViewModel: LicenseInfoViewModel by viewModels()
|
||||
private val settingsViewModel: SettingsViewModel by viewModels()
|
||||
private val startViewModel: StartViewModel by viewModels()
|
||||
|
||||
|
|
@ -192,6 +197,17 @@ class Activity : AppCompatActivity() {
|
|||
AboutScreenRoot()
|
||||
}
|
||||
|
||||
// Nested about categories
|
||||
composable(screen = Screen.LICENSES) {
|
||||
LicensesScreenRoot()
|
||||
}
|
||||
composable(screen = Screen.LICENSES_INFO) {
|
||||
LicenseInfoScreenRoot()
|
||||
}
|
||||
composable(screen = Screen.CREDITS) {
|
||||
CreditsScreenRoot()
|
||||
}
|
||||
|
||||
// Help screen
|
||||
composable(screen = Screen.HELP) {
|
||||
HelpScreenRoot()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
package ua.acclorite.book_story.data.remote.dto
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@Suppress("PropertyName")
|
||||
@Keep
|
||||
@JsonClass(generateAdapter = true)
|
||||
@Immutable
|
||||
data class ReleaseResponse(
|
||||
val name: String,
|
||||
val tag_name: String
|
||||
@Json(name = "name") val 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.Resource
|
||||
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.FileOutputStream
|
||||
import java.util.UUID
|
||||
|
|
@ -391,8 +391,8 @@ class BookRepositoryImpl @Inject constructor(
|
|||
dataStore.putData(key, value)
|
||||
}
|
||||
|
||||
override suspend fun getAllSettings(scope: CoroutineScope): MainSettingsState {
|
||||
val result = CompletableDeferred<MainSettingsState>()
|
||||
override suspend fun getAllSettings(scope: CoroutineScope): MainState {
|
||||
val result = CompletableDeferred<MainState>()
|
||||
|
||||
scope.launch {
|
||||
val keys = dataStore.getAllData()
|
||||
|
|
@ -411,7 +411,7 @@ class BookRepositoryImpl @Inject constructor(
|
|||
}
|
||||
jobs?.awaitAll()
|
||||
|
||||
result.complete(MainSettingsState.initialize(data))
|
||||
result.complete(MainState.initialize(data))
|
||||
}
|
||||
|
||||
return result.await()
|
||||
|
|
@ -613,7 +613,7 @@ class BookRepositoryImpl @Inject constructor(
|
|||
try {
|
||||
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)
|
||||
|
||||
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.util.CoverImage
|
||||
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
|
||||
|
||||
interface BookRepository {
|
||||
|
|
@ -58,7 +58,7 @@ interface BookRepository {
|
|||
value: T
|
||||
)
|
||||
|
||||
suspend fun getAllSettings(scope: CoroutineScope): MainSettingsState
|
||||
suspend fun getAllSettings(scope: CoroutineScope): MainState
|
||||
|
||||
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 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
|
||||
|
||||
class GetAllSettings @Inject constructor(
|
||||
private val repository: BookRepository
|
||||
) {
|
||||
|
||||
suspend fun execute(scope: CoroutineScope): MainSettingsState {
|
||||
suspend fun execute(scope: CoroutineScope): MainState {
|
||||
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.domain.model.Book
|
||||
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.presentation.ui.Theme
|
||||
|
||||
|
|
@ -36,6 +37,83 @@ object Constants {
|
|||
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
|
||||
val FONTS = listOf(
|
||||
FontWithName(
|
||||
|
|
|
|||
|
|
@ -21,4 +21,5 @@ object DataStoreConstants {
|
|||
val PARAGRAPH_HEIGHT = intPreferencesKey("paragraph_height")
|
||||
val PARAGRAPH_INDENTATION = booleanPreferencesKey("paragraph_indentation")
|
||||
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 OnChangeShowStartScreen(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.
|
||||
*/
|
||||
@Immutable
|
||||
data class MainSettingsState(
|
||||
data class MainState(
|
||||
val language: String? = null,
|
||||
val theme: Theme? = null,
|
||||
val darkTheme: DarkTheme? = null,
|
||||
|
|
@ -37,6 +37,7 @@ data class MainSettingsState(
|
|||
val fontColor: Long? = null,
|
||||
val showStartScreen: Boolean? = null,
|
||||
val checkForUpdates: Boolean? = null,
|
||||
val sidePadding: Int? = null,
|
||||
) : Parcelable {
|
||||
constructor(parcel: Parcel) : this(
|
||||
// String
|
||||
|
|
@ -72,6 +73,8 @@ data class MainSettingsState(
|
|||
// Boolean
|
||||
checkForUpdates = if (parcel.readByte().toInt() != 0) parcel.readByte()
|
||||
.toInt() == 1 else null,
|
||||
// Int
|
||||
sidePadding = if (parcel.readByte().toInt() != 0) parcel.readInt() else null,
|
||||
)
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
|
|
@ -114,25 +117,28 @@ data class MainSettingsState(
|
|||
// Check For Updates
|
||||
parcel.writeByte((if (checkForUpdates != null) 1 else 0).toByte())
|
||||
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 {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<MainSettingsState> {
|
||||
override fun createFromParcel(parcel: Parcel): MainSettingsState {
|
||||
return MainSettingsState(parcel)
|
||||
companion object CREATOR : Parcelable.Creator<MainState> {
|
||||
override fun createFromParcel(parcel: Parcel): MainState {
|
||||
return MainState(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<MainSettingsState?> {
|
||||
override fun newArray(size: Int): Array<MainState?> {
|
||||
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 {
|
||||
|
||||
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
|
||||
?: false
|
||||
|
||||
return MainSettingsState(
|
||||
val sidePadding: Int = data[SIDE_PADDING.name] as? Int
|
||||
?: 6
|
||||
|
||||
return MainState(
|
||||
language = language,
|
||||
theme = theme.toTheme(),
|
||||
darkTheme = darkTheme.toDarkTheme(),
|
||||
|
|
@ -201,7 +210,8 @@ data class MainSettingsState(
|
|||
lineHeight = lineHeight,
|
||||
paragraphHeight = paragraphHeight,
|
||||
paragraphIndentation = paragraphIndentation,
|
||||
checkForUpdates = checkForUpdates
|
||||
checkForUpdates = checkForUpdates,
|
||||
sidePadding = sidePadding
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -44,8 +44,8 @@ class MainViewModel @Inject constructor(
|
|||
private val isSettingsReady = MutableStateFlow(false)
|
||||
private val isViewModelReady = MutableStateFlow(false)
|
||||
|
||||
private val _state: MutableStateFlow<MainSettingsState> = MutableStateFlow(
|
||||
stateHandle[Constants.MAIN_STATE] ?: MainSettingsState()
|
||||
private val _state: MutableStateFlow<MainState> = MutableStateFlow(
|
||||
stateHandle[Constants.MAIN_STATE] ?: MainState()
|
||||
)
|
||||
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(
|
||||
function: (MainSettingsState) -> MainSettingsState
|
||||
function: (MainState) -> MainState
|
||||
) {
|
||||
_state.update {
|
||||
stateHandle[Constants.MAIN_STATE] = function(it)
|
||||
|
|
|
|||
|
|
@ -77,6 +77,10 @@ enum class Screen {
|
|||
READER_SETTINGS,
|
||||
|
||||
ABOUT,
|
||||
LICENSES,
|
||||
LICENSES_INFO,
|
||||
CREDITS,
|
||||
|
||||
HELP,
|
||||
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.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.components.AboutItem
|
||||
import ua.acclorite.book_story.presentation.screens.about.components.AboutUpdateDialog
|
||||
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 {
|
||||
AboutItem(
|
||||
title = stringResource(id = R.string.support_option),
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ fun AboutItem(
|
|||
) {
|
||||
Text(
|
||||
title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontSize = 18.sp
|
||||
)
|
||||
description?.let {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ fun AboutUpdateDialog(
|
|||
}
|
||||
|
||||
CustomDialogWithContent(
|
||||
title = stringResource(id = R.string.update_query, update.tag_name),
|
||||
title = stringResource(id = R.string.update_query, update.tagName),
|
||||
imageVectorIcon = Icons.Default.Update,
|
||||
description = stringResource(
|
||||
id = R.string.update_app_description
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ class AboutViewModel @Inject constructor(
|
|||
return@launch
|
||||
}
|
||||
|
||||
val version = result.tag_name.substringAfterLast("v")
|
||||
val version = result.tagName.substringAfterLast("v")
|
||||
val currentVersion = event.context.getString(R.string.app_version)
|
||||
|
||||
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.data.LocalNavigator
|
||||
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.Navigator
|
||||
import ua.acclorite.book_story.presentation.screens.history.data.HistoryEvent
|
||||
|
|
@ -127,7 +127,7 @@ fun ReaderScreenRoot() {
|
|||
@Composable
|
||||
private fun ReaderScreen(
|
||||
state: State<ReaderState>,
|
||||
mainState: State<MainSettingsState>,
|
||||
mainState: State<MainState>,
|
||||
listState: LazyListState,
|
||||
loading: State<Boolean>,
|
||||
navigator: Navigator,
|
||||
|
|
@ -167,6 +167,29 @@ private fun ReaderScreen(
|
|||
it.id == mainState.value.fontFamily
|
||||
} ?: 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) {
|
||||
if (!canScroll && !state.value.showMenu && !loading.value) {
|
||||
|
|
@ -329,16 +352,6 @@ private fun ReaderScreen(
|
|||
val text = remember(mainState.value.paragraphIndentation) {
|
||||
"${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(
|
||||
Modifier
|
||||
|
|
@ -346,23 +359,18 @@ private fun ReaderScreen(
|
|||
.fillMaxWidth()
|
||||
.padding(
|
||||
top = if (index == 0) 18.dp else 0.dp,
|
||||
start = 18.dp,
|
||||
end = 18.dp,
|
||||
start = sidePadding,
|
||||
end = sidePadding,
|
||||
bottom = if (index == state.value.book.text.lastIndex) 18.dp
|
||||
else (mainState.value.paragraphHeight!! * 3).dp
|
||||
else paragraphHeight
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
color = fontColor,
|
||||
style = TextStyle(
|
||||
lineBreak = LineBreak.Paragraph
|
||||
),
|
||||
style = TextStyle(lineBreak = LineBreak.Paragraph),
|
||||
fontFamily = fontFamily.font,
|
||||
fontStyle = when (mainState.value.isItalic!!) {
|
||||
true -> FontStyle.Italic
|
||||
false -> FontStyle.Normal
|
||||
},
|
||||
fontStyle = fontStyle,
|
||||
fontSize = mainState.value.fontSize!!.sp,
|
||||
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.presentation.components.CategoryTitle
|
||||
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.settings.components.CheckboxWithTitle
|
||||
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
|
||||
fun ReaderSettingsBottomSheet(
|
||||
mainState: State<MainSettingsState>,
|
||||
mainState: State<MainState>,
|
||||
onEvent: (ReaderEvent) -> Unit,
|
||||
onMainEvent: (MainEvent) -> Unit
|
||||
) {
|
||||
|
|
@ -212,6 +212,19 @@ fun ReaderSettingsBottomSheet(
|
|||
item {
|
||||
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 {
|
||||
SliderWithTitle(
|
||||
value = mainState.value.lineHeight!! to "pt",
|
||||
|
|
|
|||
|
|
@ -111,7 +111,13 @@ class ReaderViewModel @Inject constructor(
|
|||
updateBooks.execute(
|
||||
listOf(_state.value.book)
|
||||
)
|
||||
event.refreshList(_state.value.book)
|
||||
event.refreshList(
|
||||
_state.value.book.copy(
|
||||
text = emptyList(),
|
||||
letters = 0,
|
||||
words = 0
|
||||
)
|
||||
)
|
||||
|
||||
viewModelScope.launch {
|
||||
snapshotFlow {
|
||||
|
|
@ -231,7 +237,13 @@ class ReaderViewModel @Inject constructor(
|
|||
event.navigator.putArgument(
|
||||
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())
|
||||
event.navigate(event.navigator)
|
||||
|
|
@ -269,7 +281,13 @@ class ReaderViewModel @Inject constructor(
|
|||
_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))
|
||||
|
||||
event.onUpdateCategories(_state.value.book)
|
||||
event.onUpdateCategories(
|
||||
_state.value.book.copy(
|
||||
text = emptyList(),
|
||||
letters = 0,
|
||||
words = 0
|
||||
)
|
||||
)
|
||||
event.updatePage(
|
||||
Category.entries.dropLastWhile {
|
||||
it != Category.ALREADY_READ
|
||||
|
|
@ -396,6 +420,7 @@ class ReaderViewModel @Inject constructor(
|
|||
return@launch
|
||||
}
|
||||
|
||||
if (_state.value.book.id != bookId) {
|
||||
val book = getBooksById.execute(listOf(bookId))
|
||||
|
||||
if (book.isEmpty()) {
|
||||
|
|
@ -403,13 +428,18 @@ class ReaderViewModel @Inject constructor(
|
|||
return@launch
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
onEvent(ReaderEvent.OnShowHideMenu(false, context))
|
||||
}
|
||||
|
||||
_state.update {
|
||||
ReaderState(book = book.first())
|
||||
}
|
||||
} else {
|
||||
_state.update {
|
||||
ReaderState(book = it.book)
|
||||
}
|
||||
}
|
||||
|
||||
viewModelScope.launch {
|
||||
onEvent(ReaderEvent.OnShowHideMenu(false, context))
|
||||
}
|
||||
|
||||
onEvent(
|
||||
ReaderEvent.OnLoadText(
|
||||
|
|
|
|||
|
|
@ -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.data.LocalNavigator
|
||||
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.Navigator
|
||||
import ua.acclorite.book_story.presentation.screens.settings.components.ColorPickerWithTitle
|
||||
|
|
@ -66,7 +66,7 @@ fun AppearanceSettingsRoot() {
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun AppearanceSettings(
|
||||
state: State<MainSettingsState>,
|
||||
state: State<MainState>,
|
||||
navigator: Navigator,
|
||||
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.presentation.components.CategoryTitle
|
||||
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.isDark
|
||||
import ua.acclorite.book_story.presentation.ui.isPureDark
|
||||
|
|
@ -32,7 +32,7 @@ import ua.acclorite.book_story.presentation.ui.isPureDark
|
|||
@Composable
|
||||
fun AppearanceSettingsThemeSwitcher(
|
||||
modifier: Modifier = Modifier,
|
||||
state: State<MainSettingsState>,
|
||||
state: State<MainState>,
|
||||
onMainEvent: (MainEvent) -> Unit,
|
||||
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.data.LocalNavigator
|
||||
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.Navigator
|
||||
import ua.acclorite.book_story.presentation.screens.settings.components.CheckboxWithTitle
|
||||
|
|
@ -63,7 +63,7 @@ fun GeneralSettingsRoot() {
|
|||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
private fun GeneralSettings(
|
||||
state: State<MainSettingsState>,
|
||||
state: State<MainState>,
|
||||
navigator: Navigator,
|
||||
onSettingsEvent: (SettingsEvent) -> 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.data.LocalNavigator
|
||||
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.Navigator
|
||||
import ua.acclorite.book_story.presentation.screens.settings.components.CheckboxWithTitle
|
||||
|
|
@ -56,7 +56,7 @@ fun ReaderSettingsRoot() {
|
|||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun ReaderSettings(
|
||||
state: State<MainSettingsState>,
|
||||
state: State<MainState>,
|
||||
navigator: Navigator,
|
||||
onMainEvent: (MainEvent) -> Unit
|
||||
) {
|
||||
|
|
@ -187,6 +187,19 @@ private fun ReaderSettings(
|
|||
item {
|
||||
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 {
|
||||
SliderWithTitle(
|
||||
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.rememberPermissionState
|
||||
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.screens.browse.data.BrowseEvent
|
||||
import ua.acclorite.book_story.presentation.screens.browse.data.BrowseViewModel
|
||||
|
|
@ -69,7 +69,7 @@ fun StartScreenRoot() {
|
|||
@Composable
|
||||
private fun StartScreen(
|
||||
state: State<StartState>,
|
||||
mainState: State<MainSettingsState>,
|
||||
mainState: State<MainState>,
|
||||
storagePermissionState: PermissionState,
|
||||
notificationsPermissionState: PermissionState,
|
||||
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.presentation.components.CategoryTitle
|
||||
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.nested.appearance.components.theme_switcher.AppearanceSettingsThemeSwitcher
|
||||
import ua.acclorite.book_story.presentation.ui.BookStoryTheme
|
||||
|
|
@ -29,7 +29,7 @@ import ua.acclorite.book_story.presentation.ui.isPureDark
|
|||
* Appearance settings.
|
||||
*/
|
||||
fun LazyListScope.startAppearanceScreen(
|
||||
mainState: State<MainSettingsState>,
|
||||
mainState: State<MainState>,
|
||||
onMainEvent: (MainEvent) -> Unit,
|
||||
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.util.Constants
|
||||
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.data.StartEvent
|
||||
import ua.acclorite.book_story.presentation.screens.start.data.StartNavigationScreen
|
||||
|
|
@ -49,7 +49,7 @@ import ua.acclorite.book_story.presentation.ui.Theme
|
|||
@Composable
|
||||
fun StartSettings(
|
||||
state: State<StartState>,
|
||||
mainState: State<MainSettingsState>,
|
||||
mainState: State<MainState>,
|
||||
onEvent: (StartEvent) -> Unit,
|
||||
onMainEvent: (MainEvent) -> Unit,
|
||||
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="check_for_updates_option">Перевіряти на оновлення</string>
|
||||
<string name="check_for_updates_option_desc">Перевіряти на новішу версію застосунку при запуску</string>
|
||||
<string name="side_padding_option">Бокові відступи</string>
|
||||
|
||||
<!-- Colors -->
|
||||
<string name="red_color">Червоний</string>
|
||||
|
|
@ -289,9 +290,16 @@
|
|||
<string name="report_bug_option">Повідомити про помилку</string>
|
||||
<string name="project_page_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_desc">Героям Слава! 🐉</string>
|
||||
|
||||
<!-- Credits -->
|
||||
<string name="credits_design">Дизайн</string>
|
||||
<string name="credits_icon">Іконки</string>
|
||||
<string name="credits_updates">Керування новими оновленнями</string>
|
||||
|
||||
<!-- Help -->
|
||||
<string name="help_clickable_note_1">Будь ласка, зверніть увагу, що на</string>
|
||||
<string name="help_clickable_note_2">такий виділений текст</string>
|
||||
|
|
@ -464,6 +472,7 @@
|
|||
<string name="required_content_desc">Необхідно</string>
|
||||
<string name="reset_start_content_desc">Скинути початковий посібник</string>
|
||||
<string name="hide_edit_title_content_desc">Вийти з редагування назви</string>
|
||||
<string name="open_in_web_content_desc">Відкрити в браузері</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="InconsistentArrays">
|
||||
<!-- 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>
|
||||
|
||||
<!-- Notifications -->
|
||||
|
|
@ -225,6 +225,7 @@
|
|||
<string name="font_color_option">Font color</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="side_padding_option">Side padding</string>
|
||||
|
||||
<!-- Colors -->
|
||||
<string name="red_color">Red</string>
|
||||
|
|
@ -308,9 +309,16 @@
|
|||
<string name="report_bug_option">Report a bug</string>
|
||||
<string name="project_page_option">Project\'s page</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_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 -->
|
||||
<string name="help_clickable_note_1">Please note, that</string>
|
||||
<string name="help_clickable_note_2">such highlighted text</string>
|
||||
|
|
@ -484,5 +492,6 @@
|
|||
<string name="required_content_desc">Required</string>
|
||||
<string name="reset_start_content_desc">Reset start guide</string>
|
||||
<string name="hide_edit_title_content_desc">Exit title editing</string>
|
||||
<string name="open_in_web_content_desc">Open in web</string>
|
||||
|
||||
</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
|
||||
# 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
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
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
|
||||
org.gradle.configuration-cache=true
|
||||
android.enableR8.fullMode=false
|
||||
Loading…
Add table
Add a link
Reference in a new issue