mirror of
https://github.com/mihonapp/mihon.git
synced 2024-11-21 20:47:03 -05:00
Added configuration options to e-ink page flashes (#625)
* Recommit for e-ink pref changes * Fixed state holder for flash interval * Detekt * Refactor suggested by Antsy * inverted currentDisplayRefresh check for early exit
This commit is contained in:
parent
239c38982c
commit
2f86f25d5b
6 changed files with 189 additions and 13 deletions
|
@ -14,6 +14,7 @@ import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
import kotlinx.collections.immutable.toImmutableMap
|
import kotlinx.collections.immutable.toImmutableMap
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.collectAsState
|
import tachiyomi.presentation.core.util.collectAsState
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
|
@ -61,12 +62,8 @@ object SettingsReaderScreen : SearchableSettings {
|
||||||
pref = readerPref.pageTransitions(),
|
pref = readerPref.pageTransitions(),
|
||||||
title = stringResource(MR.strings.pref_page_transitions),
|
title = stringResource(MR.strings.pref_page_transitions),
|
||||||
),
|
),
|
||||||
Preference.PreferenceItem.SwitchPreference(
|
|
||||||
pref = readerPref.flashOnPageChange(),
|
|
||||||
title = stringResource(MR.strings.pref_flash_page),
|
|
||||||
subtitle = stringResource(MR.strings.pref_flash_page_summ),
|
|
||||||
),
|
|
||||||
getDisplayGroup(readerPreferences = readerPref),
|
getDisplayGroup(readerPreferences = readerPref),
|
||||||
|
getEInkGroup(readerPreferences = readerPref),
|
||||||
getReadingGroup(readerPreferences = readerPref),
|
getReadingGroup(readerPreferences = readerPref),
|
||||||
getPagedGroup(readerPreferences = readerPref),
|
getPagedGroup(readerPreferences = readerPref),
|
||||||
getWebtoonGroup(readerPreferences = readerPref),
|
getWebtoonGroup(readerPreferences = readerPref),
|
||||||
|
@ -122,6 +119,65 @@ object SettingsReaderScreen : SearchableSettings {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun getEInkGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
|
||||||
|
val flashPageState by readerPreferences.flashOnPageChange().collectAsState()
|
||||||
|
|
||||||
|
val flashMillisPref = readerPreferences.flashDurationMillis()
|
||||||
|
val flashMillis by flashMillisPref.collectAsState()
|
||||||
|
|
||||||
|
val flashIntervalPref = readerPreferences.flashPageInterval()
|
||||||
|
val flashInterval by flashIntervalPref.collectAsState()
|
||||||
|
|
||||||
|
val flashColorPref = readerPreferences.flashColor()
|
||||||
|
|
||||||
|
return Preference.PreferenceGroup(
|
||||||
|
title = "E-Ink",
|
||||||
|
preferenceItems = persistentListOf(
|
||||||
|
Preference.PreferenceItem.SwitchPreference(
|
||||||
|
pref = readerPreferences.flashOnPageChange(),
|
||||||
|
title = stringResource(MR.strings.pref_flash_page),
|
||||||
|
subtitle = stringResource(MR.strings.pref_flash_page_summ),
|
||||||
|
),
|
||||||
|
Preference.PreferenceItem.SliderPreference(
|
||||||
|
value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
|
||||||
|
min = 1,
|
||||||
|
max = 15,
|
||||||
|
title = stringResource(MR.strings.pref_flash_duration),
|
||||||
|
subtitle = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
|
||||||
|
onValueChanged = {
|
||||||
|
flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION)
|
||||||
|
true
|
||||||
|
},
|
||||||
|
enabled = flashPageState,
|
||||||
|
),
|
||||||
|
Preference.PreferenceItem.SliderPreference(
|
||||||
|
value = flashInterval,
|
||||||
|
min = 1,
|
||||||
|
max = 10,
|
||||||
|
title = stringResource(MR.strings.pref_flash_page_interval),
|
||||||
|
subtitle = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
|
||||||
|
onValueChanged = {
|
||||||
|
flashIntervalPref.set(it)
|
||||||
|
true
|
||||||
|
},
|
||||||
|
enabled = flashPageState,
|
||||||
|
),
|
||||||
|
Preference.PreferenceItem.ListPreference(
|
||||||
|
pref = flashColorPref,
|
||||||
|
title = stringResource(MR.strings.pref_flash_with),
|
||||||
|
entries = persistentMapOf(
|
||||||
|
ReaderPreferences.FlashColor.BLACK to stringResource(MR.strings.pref_flash_style_black),
|
||||||
|
ReaderPreferences.FlashColor.WHITE to stringResource(MR.strings.pref_flash_style_white),
|
||||||
|
ReaderPreferences.FlashColor.WHITE_BLACK
|
||||||
|
to stringResource(MR.strings.pref_flash_style_white_black),
|
||||||
|
),
|
||||||
|
enabled = flashPageState,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
|
private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
|
||||||
return Preference.PreferenceGroup(
|
return Preference.PreferenceGroup(
|
||||||
|
|
|
@ -7,19 +7,42 @@ import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import tachiyomi.presentation.core.util.collectAsState
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
class DisplayRefreshHost {
|
class DisplayRefreshHost {
|
||||||
|
|
||||||
internal var currentDisplayRefresh by mutableStateOf(false)
|
internal var currentDisplayRefresh by mutableStateOf(false)
|
||||||
|
private val readerPreferences = Injekt.get<ReaderPreferences>()
|
||||||
|
|
||||||
|
internal val flashMillis = readerPreferences.flashDurationMillis()
|
||||||
|
internal val flashMode = readerPreferences.flashColor()
|
||||||
|
|
||||||
|
internal val flashIntervalPref = readerPreferences.flashPageInterval()
|
||||||
|
|
||||||
|
// Internal State for Flash
|
||||||
|
private var flashInterval = flashIntervalPref.get()
|
||||||
|
private var timesCalled = 0
|
||||||
|
|
||||||
fun flash() {
|
fun flash() {
|
||||||
currentDisplayRefresh = true
|
if (timesCalled % flashInterval == 0) {
|
||||||
|
currentDisplayRefresh = true
|
||||||
|
}
|
||||||
|
timesCalled += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setInterval(interval: Int) {
|
||||||
|
flashInterval = interval
|
||||||
|
timesCalled = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,18 +52,39 @@ fun DisplayRefreshHost(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val currentDisplayRefresh = hostState.currentDisplayRefresh
|
val currentDisplayRefresh = hostState.currentDisplayRefresh
|
||||||
|
val refreshDuration by hostState.flashMillis.collectAsState()
|
||||||
|
val flashMode by hostState.flashMode.collectAsState()
|
||||||
|
val flashInterval by hostState.flashIntervalPref.collectAsState()
|
||||||
|
|
||||||
|
var currentColor by remember { mutableStateOf<Color?>(null) }
|
||||||
|
|
||||||
LaunchedEffect(currentDisplayRefresh) {
|
LaunchedEffect(currentDisplayRefresh) {
|
||||||
if (currentDisplayRefresh) {
|
if (!currentDisplayRefresh) {
|
||||||
delay(1.5.seconds)
|
currentColor = null
|
||||||
hostState.currentDisplayRefresh = false
|
return@LaunchedEffect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val refreshDurationHalf = refreshDuration.milliseconds / 2
|
||||||
|
currentColor = if (flashMode == ReaderPreferences.FlashColor.BLACK) {
|
||||||
|
Color.Black
|
||||||
|
} else {
|
||||||
|
Color.White
|
||||||
|
}
|
||||||
|
delay(refreshDurationHalf)
|
||||||
|
if (flashMode == ReaderPreferences.FlashColor.WHITE_BLACK) {
|
||||||
|
currentColor = Color.Black
|
||||||
|
}
|
||||||
|
delay(refreshDurationHalf)
|
||||||
|
hostState.currentDisplayRefresh = false
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(flashInterval) {
|
||||||
|
hostState.setInterval(flashInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
Canvas(
|
Canvas(
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
if (currentDisplayRefresh) {
|
currentColor?.let { drawRect(it) }
|
||||||
drawRect(Color.Black)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,13 @@ import androidx.compose.material3.FilterChip
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.CheckboxItem
|
import tachiyomi.presentation.core.components.CheckboxItem
|
||||||
import tachiyomi.presentation.core.components.SettingsChipRow
|
import tachiyomi.presentation.core.components.SettingsChipRow
|
||||||
|
import tachiyomi.presentation.core.components.SliderItem
|
||||||
|
import tachiyomi.presentation.core.i18n.pluralStringResource
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
import tachiyomi.presentation.core.util.collectAsState
|
import tachiyomi.presentation.core.util.collectAsState
|
||||||
|
|
||||||
|
@ -19,9 +22,27 @@ private val themes = listOf(
|
||||||
MR.strings.automatic_background to 3,
|
MR.strings.automatic_background to 3,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val flashColors = listOf(
|
||||||
|
MR.strings.pref_flash_style_black to ReaderPreferences.FlashColor.BLACK,
|
||||||
|
MR.strings.pref_flash_style_white to ReaderPreferences.FlashColor.WHITE,
|
||||||
|
MR.strings.pref_flash_style_white_black to ReaderPreferences.FlashColor.WHITE_BLACK,
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
|
internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
|
||||||
val readerTheme by screenModel.preferences.readerTheme().collectAsState()
|
val readerTheme by screenModel.preferences.readerTheme().collectAsState()
|
||||||
|
|
||||||
|
val flashPageState by screenModel.preferences.flashOnPageChange().collectAsState()
|
||||||
|
|
||||||
|
val flashMillisPref = screenModel.preferences.flashDurationMillis()
|
||||||
|
val flashMillis by flashMillisPref.collectAsState()
|
||||||
|
|
||||||
|
val flashIntervalPref = screenModel.preferences.flashPageInterval()
|
||||||
|
val flashInterval by flashIntervalPref.collectAsState()
|
||||||
|
|
||||||
|
val flashColorPref = screenModel.preferences.flashColor()
|
||||||
|
val flashColor by flashColorPref.collectAsState()
|
||||||
|
|
||||||
SettingsChipRow(MR.strings.pref_reader_theme) {
|
SettingsChipRow(MR.strings.pref_reader_theme) {
|
||||||
themes.map { (labelRes, value) ->
|
themes.map { (labelRes, value) ->
|
||||||
FilterChip(
|
FilterChip(
|
||||||
|
@ -73,4 +94,33 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
|
||||||
label = stringResource(MR.strings.pref_flash_page),
|
label = stringResource(MR.strings.pref_flash_page),
|
||||||
pref = screenModel.preferences.flashOnPageChange(),
|
pref = screenModel.preferences.flashOnPageChange(),
|
||||||
)
|
)
|
||||||
|
if (flashPageState) {
|
||||||
|
SliderItem(
|
||||||
|
value = flashMillis / ReaderPreferences.MILLI_CONVERSION,
|
||||||
|
label = stringResource(MR.strings.pref_flash_duration),
|
||||||
|
valueText = stringResource(MR.strings.pref_flash_duration_summary, flashMillis),
|
||||||
|
onChange = { flashMillisPref.set(it * ReaderPreferences.MILLI_CONVERSION) },
|
||||||
|
min = 1,
|
||||||
|
max = 15,
|
||||||
|
)
|
||||||
|
SliderItem(
|
||||||
|
value = flashInterval,
|
||||||
|
label = stringResource(MR.strings.pref_flash_page_interval),
|
||||||
|
valueText = pluralStringResource(MR.plurals.pref_pages, flashInterval, flashInterval),
|
||||||
|
onChange = {
|
||||||
|
flashIntervalPref.set(it)
|
||||||
|
},
|
||||||
|
min = 1,
|
||||||
|
max = 10,
|
||||||
|
)
|
||||||
|
SettingsChipRow(MR.strings.pref_flash_with) {
|
||||||
|
flashColors.map { (labelRes, value) ->
|
||||||
|
FilterChip(
|
||||||
|
selected = flashColor == value,
|
||||||
|
onClick = { flashColorPref.set(value) },
|
||||||
|
label = { Text(stringResource(labelRes)) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,12 @@ class ReaderPreferences(
|
||||||
|
|
||||||
fun flashOnPageChange() = preferenceStore.getBoolean("pref_reader_flash", false)
|
fun flashOnPageChange() = preferenceStore.getBoolean("pref_reader_flash", false)
|
||||||
|
|
||||||
|
fun flashDurationMillis() = preferenceStore.getInt("pref_reader_flash_duration", MILLI_CONVERSION)
|
||||||
|
|
||||||
|
fun flashPageInterval() = preferenceStore.getInt("pref_reader_flash_interval", 1)
|
||||||
|
|
||||||
|
fun flashColor() = preferenceStore.getEnum("pref_reader_flash_mode", FlashColor.BLACK)
|
||||||
|
|
||||||
fun doubleTapAnimSpeed() = preferenceStore.getInt("pref_double_tap_anim_speed", 500)
|
fun doubleTapAnimSpeed() = preferenceStore.getInt("pref_double_tap_anim_speed", 500)
|
||||||
|
|
||||||
fun showPageNumber() = preferenceStore.getBoolean("pref_show_page_number_key", true)
|
fun showPageNumber() = preferenceStore.getBoolean("pref_show_page_number_key", true)
|
||||||
|
@ -133,6 +139,12 @@ class ReaderPreferences(
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
enum class FlashColor {
|
||||||
|
BLACK,
|
||||||
|
WHITE,
|
||||||
|
WHITE_BLACK
|
||||||
|
}
|
||||||
|
|
||||||
enum class TappingInvertMode(
|
enum class TappingInvertMode(
|
||||||
val titleRes: StringResource,
|
val titleRes: StringResource,
|
||||||
val shouldInvertHorizontal: Boolean = false,
|
val shouldInvertHorizontal: Boolean = false,
|
||||||
|
@ -155,6 +167,8 @@ class ReaderPreferences(
|
||||||
const val WEBTOON_PADDING_MIN = 0
|
const val WEBTOON_PADDING_MIN = 0
|
||||||
const val WEBTOON_PADDING_MAX = 25
|
const val WEBTOON_PADDING_MAX = 25
|
||||||
|
|
||||||
|
const val MILLI_CONVERSION = 100
|
||||||
|
|
||||||
val TapZones = listOf(
|
val TapZones = listOf(
|
||||||
MR.strings.label_default,
|
MR.strings.label_default,
|
||||||
MR.strings.l_nav,
|
MR.strings.l_nav,
|
||||||
|
|
|
@ -40,6 +40,11 @@
|
||||||
<item quantity="other">%d days</item>
|
<item quantity="other">%d days</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
|
||||||
|
<plurals name="pref_pages">
|
||||||
|
<item quantity="one">1 page</item>
|
||||||
|
<item quantity="other">%1$s pages</item>
|
||||||
|
</plurals>
|
||||||
|
|
||||||
<!-- Manga info -->
|
<!-- Manga info -->
|
||||||
<plurals name="missing_chapters">
|
<plurals name="missing_chapters">
|
||||||
<item quantity="one">Missing %1$s chapter</item>
|
<item quantity="one">Missing %1$s chapter</item>
|
||||||
|
|
|
@ -366,6 +366,13 @@
|
||||||
<string name="pref_page_transitions">Animate page transitions</string>
|
<string name="pref_page_transitions">Animate page transitions</string>
|
||||||
<string name="pref_flash_page">Flash on page change</string>
|
<string name="pref_flash_page">Flash on page change</string>
|
||||||
<string name="pref_flash_page_summ">Reduces ghosting on e-ink displays</string>
|
<string name="pref_flash_page_summ">Reduces ghosting on e-ink displays</string>
|
||||||
|
<string name="pref_flash_duration">Flash duration</string>
|
||||||
|
<string name="pref_flash_duration_summary">%1$s ms</string>
|
||||||
|
<string name="pref_flash_page_interval">Flash every</string>
|
||||||
|
<string name="pref_flash_with">Flash with</string>
|
||||||
|
<string name="pref_flash_style_black">Black</string>
|
||||||
|
<string name="pref_flash_style_white">White</string>
|
||||||
|
<string name="pref_flash_style_white_black">White and Black</string>
|
||||||
<string name="pref_double_tap_anim_speed">Double tap animation speed</string>
|
<string name="pref_double_tap_anim_speed">Double tap animation speed</string>
|
||||||
<string name="pref_show_page_number">Show page number</string>
|
<string name="pref_show_page_number">Show page number</string>
|
||||||
<string name="pref_show_reading_mode">Show reading mode</string>
|
<string name="pref_show_reading_mode">Show reading mode</string>
|
||||||
|
|
Loading…
Reference in a new issue