🚀 Absolute Dark setting

* Added new "Absolute Dark" setting, which darkens "Pure Dark" theme even more, giving it completely black color as background
* May be more suitable for OLED screen users, while default "Pure Dark" may be better for non-OLED display users
This commit is contained in:
Acclorite 2024-09-04 21:45:07 +03:00
parent be9b28834c
commit 63bb317865
9 changed files with 100 additions and 46 deletions

View file

@ -11,6 +11,7 @@ object DataStoreConstants {
val THEME = stringPreferencesKey("theme")
val DARK_THEME = stringPreferencesKey("dark_theme")
val PURE_DARK = stringPreferencesKey("pure_dark")
val ABSOLUTE_DARK = booleanPreferencesKey("absolute_dark")
val THEME_CONTRAST = stringPreferencesKey("theme_contrast")
val SHOW_START_SCREEN = booleanPreferencesKey("guide")
val CHECK_FOR_UPDATES = booleanPreferencesKey("check_for_updates")

View file

@ -31,4 +31,5 @@ sealed class MainEvent {
data class OnChangeTextAlignment(val alignment: String) : MainEvent()
data class OnChangeDoublePressExit(val bool: Boolean) : MainEvent()
data class OnChangeLetterSpacing(val spacing: Int) : MainEvent()
data class OnChangeAbsoluteDark(val bool: Boolean) : MainEvent()
}

View file

@ -51,6 +51,7 @@ data class MainState(
},
val darkTheme: DarkTheme = provideDefaultValue { DarkTheme.FOLLOW_SYSTEM },
val pureDark: PureDark = provideDefaultValue { PureDark.OFF },
val absoluteDark: Boolean = provideDefaultValue { false },
val themeContrast: ThemeContrast = provideDefaultValue { ThemeContrast.STANDARD },
val showStartScreen: Boolean = provideDefaultValue { true },
val checkForUpdates: Boolean = provideDefaultValue { false },
@ -118,6 +119,10 @@ data class MainState(
PURE_DARK, convert = { toPureDark() }
) { pureDark },
absoluteDark = provideValue(
ABSOLUTE_DARK
) { absoluteDark },
themeContrast = provideValue(
THEME_CONTRAST, convert = { toThemeContrast() }
) { themeContrast },

View file

@ -373,6 +373,20 @@ class MainViewModel @Inject constructor(
}
}
}
is MainEvent.OnChangeAbsoluteDark -> {
viewModelScope.launch(Dispatchers.IO) {
setDatastore.execute(
DataStoreConstants.ABSOLUTE_DARK,
event.bool
)
updateStateWithSavedHandle {
it.copy(
absoluteDark = event.bool
)
}
}
}
}
}

View file

@ -0,0 +1,37 @@
package ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
import ua.acclorite.book_story.presentation.data.MainEvent
import ua.acclorite.book_story.presentation.screens.settings.components.SwitchWithTitle
import ua.acclorite.book_story.presentation.ui.ExpandingTransition
import ua.acclorite.book_story.presentation.ui.isPureDark
/**
* Absolute Dark setting.
* If true, changes Pure Dark (OLED) theme to have black color as background.
*/
@Composable
fun AbsoluteDarkSetting() {
val state = LocalMainViewModel.current.state
val onMainEvent = LocalMainViewModel.current.onEvent
val context = LocalContext.current
ExpandingTransition(visible = state.value.pureDark.isPureDark(context)) {
SwitchWithTitle(
selected = state.value.absoluteDark,
title = stringResource(id = R.string.absolute_dark_option),
description = stringResource(id = R.string.absolute_dark_option_desc),
onClick = {
onMainEvent(
MainEvent.OnChangeAbsoluteDark(
!state.value.absoluteDark
)
)
}
)
}
}

View file

@ -16,6 +16,7 @@ import androidx.compose.ui.unit.coerceAtLeast
import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.CategoryTitle
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.AbsoluteDarkSetting
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.DarkThemeSetting
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.PureDarkSetting
import ua.acclorite.book_story.presentation.screens.settings.nested.appearance.components.settings.ThemeContrastSetting
@ -66,6 +67,10 @@ fun LazyListScope.ThemePreferencesSubcategory(
PureDarkSetting()
}
item {
AbsoluteDarkSetting()
}
item {
Spacer(
modifier = Modifier.height(bottomPadding)

View file

@ -3,61 +3,48 @@ package ua.acclorite.book_story.presentation.ui.theme
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import ua.acclorite.book_story.presentation.core.components.LocalMainViewModel
@Composable
fun blackTheme(initialTheme: ColorScheme): ColorScheme {
val absoluteDark = LocalMainViewModel.current.state.value.absoluteDark
val surfaceDarker = 3f
val surfaceContainerDarker = if (absoluteDark) 3f else 1.95f
return initialTheme.copy(
surface = initialTheme.surface.copy {
val darker = 3f
it.copy(
red = it.red / darker,
green = it.green / darker,
blue = it.blue / darker
)
},
surfaceContainer = initialTheme.surfaceContainer.copy {
val darker = 1.95f
it.copy(
red = it.red / darker,
green = it.green / darker,
blue = it.blue / darker
)
},
surfaceContainerLowest = initialTheme.surfaceContainerLowest.copy {
val darker = 1.95f
it.copy(
red = it.red / darker,
green = it.green / darker,
blue = it.blue / darker
)
},
surfaceContainerLow = initialTheme.surfaceContainerLow.copy {
val darker = 1.95f
it.copy(
red = it.red / darker,
green = it.green / darker,
blue = it.blue / darker
)
},
surfaceContainerHigh = initialTheme.surfaceContainerHigh.copy {
val darker = 1.95f
it.copy(
red = it.red / darker,
green = it.green / darker,
blue = it.blue / darker
)
},
surfaceContainerHighest = initialTheme.surfaceContainerHighest.copy {
val darker = 1.95f
it.copy(
red = it.red / darker,
green = it.green / darker,
blue = it.blue / darker
)
if (absoluteDark) {
return@copy Color.Black
}
it.darkenBy(surfaceDarker)
},
surfaceContainer = initialTheme.surfaceContainer.darkenBy(
surfaceContainerDarker
),
surfaceContainerLowest = initialTheme.surfaceContainerLowest.darkenBy(
surfaceContainerDarker
),
surfaceContainerLow = initialTheme.surfaceContainerLow.darkenBy(
surfaceContainerDarker
),
surfaceContainerHigh = initialTheme.surfaceContainerHigh.darkenBy(
surfaceContainerDarker
),
surfaceContainerHighest = initialTheme.surfaceContainerHighest.darkenBy(
surfaceContainerDarker
)
)
}
private fun Color.copy(calculation: (Color) -> Color): Color {
return calculation(this)
}
private fun Color.darkenBy(value: Float): Color {
return copy(
red = red / value,
green = green / value,
blue = blue / value
)
}

View file

@ -209,6 +209,8 @@
<string name="language_option">Мова застосунка</string>
<string name="dark_theme_option">Темна тема</string>
<string name="pure_dark_option">Суцільна темрява (ОЛЕД)</string>
<string name="absolute_dark_option">Абсолютна темрява</string>
<string name="absolute_dark_option_desc">Змініть колір фона на повністю чорний</string>
<string name="theme_contrast_option">Контрастність теми</string>
<string name="app_theme_option">Тема застосунка</string>
<string name="check_for_updates_option">Перевіряти на оновлення</string>

View file

@ -226,6 +226,8 @@
<string name="language_option">App language</string>
<string name="dark_theme_option">Dark theme</string>
<string name="pure_dark_option">Pure dark (OLED)</string>
<string name="absolute_dark_option">Absolute dark</string>
<string name="absolute_dark_option_desc">Change background color to be completely black</string>
<string name="theme_contrast_option">Theme contrast</string>
<string name="app_theme_option">App theme</string>
<string name="check_for_updates_option">Check for updates</string>