Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added configuration options to e-ink page flashes #625

Merged
merged 5 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableMap
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.i18n.pluralStringResource
import tachiyomi.presentation.core.i18n.stringResource
import tachiyomi.presentation.core.util.collectAsState
import uy.kohesive.injekt.Injekt
Expand Down Expand Up @@ -61,12 +62,8 @@ object SettingsReaderScreen : SearchableSettings {
pref = readerPref.pageTransitions(),
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),
getEInkGroup(readerPreferences = readerPref),
getReadingGroup(readerPreferences = readerPref),
getPagedGroup(readerPreferences = readerPref),
getWebtoonGroup(readerPreferences = readerPref),
Expand Down Expand Up @@ -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
private fun getReadingGroup(readerPreferences: ReaderPreferences): Preference.PreferenceGroup {
return Preference.PreferenceGroup(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,42 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
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
class DisplayRefreshHost {

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() {
currentDisplayRefresh = true
if (timesCalled % flashInterval == 0) {
currentDisplayRefresh = true
}
timesCalled += 1
}

fun setInterval(interval: Int) {
flashInterval = interval
timesCalled = 0
}
}

Expand All @@ -29,18 +52,39 @@ fun DisplayRefreshHost(
modifier: Modifier = Modifier,
) {
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) {
if (currentDisplayRefresh) {
delay(1.5.seconds)
hostState.currentDisplayRefresh = false
if (!currentDisplayRefresh) {
currentColor = null
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(
modifier = modifier.fillMaxSize(),
) {
if (currentDisplayRefresh) {
drawRect(Color.Black)
}
currentColor?.let { drawRect(it) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import androidx.compose.material3.FilterChip
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.CheckboxItem
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.util.collectAsState

Expand All @@ -19,9 +22,27 @@ private val themes = listOf(
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
internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
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) {
themes.map { (labelRes, value) ->
FilterChip(
Expand Down Expand Up @@ -73,4 +94,33 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
label = stringResource(MR.strings.pref_flash_page),
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)) },
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ class ReaderPreferences(

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 showPageNumber() = preferenceStore.getBoolean("pref_show_page_number_key", true)
Expand Down Expand Up @@ -133,6 +139,12 @@ class ReaderPreferences(

// endregion

enum class FlashColor {
BLACK,
WHITE,
WHITE_BLACK
}

enum class TappingInvertMode(
val titleRes: StringResource,
val shouldInvertHorizontal: Boolean = false,
Expand All @@ -155,6 +167,8 @@ class ReaderPreferences(
const val WEBTOON_PADDING_MIN = 0
const val WEBTOON_PADDING_MAX = 25

const val MILLI_CONVERSION = 100

val TapZones = listOf(
MR.strings.label_default,
MR.strings.l_nav,
Expand Down
5 changes: 5 additions & 0 deletions i18n/src/commonMain/moko-resources/base/plurals.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@
<item quantity="other">%d days</item>
</plurals>

<plurals name="pref_pages">
<item quantity="one">1 page</item>
<item quantity="other">%1$s pages</item>
</plurals>

<!-- Manga info -->
<plurals name="missing_chapters">
<item quantity="one">Missing %1$s chapter</item>
Expand Down
7 changes: 7 additions & 0 deletions i18n/src/commonMain/moko-resources/base/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,13 @@
<string name="pref_page_transitions">Animate page transitions</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_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_show_page_number">Show page number</string>
<string name="pref_show_reading_mode">Show reading mode</string>
Expand Down