package com.aryan.reader import android.content.Context import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.luminance import androidx.core.content.edit import kotlin.math.max import kotlin.math.min private const val READER_SLIDER_CHROME_PREFS = "reader_slider_chrome_prefs" private const val READER_SLIDER_TOGGLE_PREFIX = "reader_slider_toggle_" private const val MIN_SLIDER_ACCENT_CONTRAST = 3f private const val MIN_SLIDER_CONTENT_CONTRAST = 4.5f internal data class ReaderSliderBookmarkPosition( val startPage: Int, val currentPage: Float ) internal data class ReaderSliderToggleState( val isToggledOn: Boolean, val bookmarkPosition: ReaderSliderBookmarkPosition ) internal data class ReaderSliderChromeColors( val activeTrackColor: Color, val inactiveTrackColor: Color, val thumbColor: Color, val bookmarkColor: Color, val contentColor: Color, val thumbnailSurfaceColor: Color, val thumbnailContentColor: Color ) internal fun readerSliderBookmarkPosition(currentPage: Int): ReaderSliderBookmarkPosition { val sanitizedPage = currentPage.coerceAtLeast(0) return ReaderSliderBookmarkPosition( startPage = sanitizedPage, currentPage = sanitizedPage.toFloat() ) } internal fun readerSliderToggleState( isCurrentlyToggledOn: Boolean, currentPage: Int ): ReaderSliderToggleState { return ReaderSliderToggleState( isToggledOn = !isCurrentlyToggledOn, bookmarkPosition = readerSliderBookmarkPosition(currentPage) ) } internal fun shouldRenderReaderSlider( isToggledOn: Boolean, isBottomChromeVisible: Boolean, isSearchActive: Boolean ): Boolean = isToggledOn && isBottomChromeVisible && !isSearchActive internal fun readerSliderTogglePreferenceKey(bookId: String): String = READER_SLIDER_TOGGLE_PREFIX + bookId internal fun loadReaderSliderToggled(context: Context, bookId: String): Boolean { return context .getSharedPreferences(READER_SLIDER_CHROME_PREFS, Context.MODE_PRIVATE) .getBoolean(readerSliderTogglePreferenceKey(bookId), false) } internal fun saveReaderSliderToggled( context: Context, bookId: String, isToggledOn: Boolean ) { context .getSharedPreferences(READER_SLIDER_CHROME_PREFS, Context.MODE_PRIVATE) .edit { putBoolean(readerSliderTogglePreferenceKey(bookId), isToggledOn) } } internal fun readerSliderChromeColors( pageBackground: Color, pageText: Color, themePrimary: Color ): ReaderSliderChromeColors { val background = specifiedColorOr(pageBackground, Color.White) val fallbackContent = highContrastColorFor(background) val content = specifiedColorOr(pageText, fallbackContent) .takeIf { contrastRatio(it, background) >= MIN_SLIDER_CONTENT_CONTRAST } ?: fallbackContent val active = specifiedColorOr(themePrimary, content) .takeIf { contrastRatio(it, background) >= MIN_SLIDER_ACCENT_CONTRAST } ?: content val inactiveAlpha = if (background.luminance() > 0.5f) 0.44f else 0.52f val thumbnailSurface = blendColors( foreground = content, background = background, alpha = if (background.luminance() > 0.5f) 0.08f else 0.12f ).copy(alpha = 0.96f) return ReaderSliderChromeColors( activeTrackColor = active, inactiveTrackColor = content.copy(alpha = inactiveAlpha), thumbColor = active, bookmarkColor = active, contentColor = content, thumbnailSurfaceColor = thumbnailSurface, thumbnailContentColor = content ) } private fun specifiedColorOr(color: Color, fallback: Color): Color { return if (color == Color.Unspecified) fallback else color } private fun highContrastColorFor(background: Color): Color { return if (contrastRatio(Color.Black, background) >= contrastRatio(Color.White, background)) { Color.Black } else { Color.White } } private fun contrastRatio(first: Color, second: Color): Float { val firstLuminance = first.luminance() val secondLuminance = second.luminance() val lighter = max(firstLuminance, secondLuminance) val darker = min(firstLuminance, secondLuminance) return (lighter + 0.05f) / (darker + 0.05f) } private fun blendColors(foreground: Color, background: Color, alpha: Float): Color { val clampedAlpha = alpha.coerceIn(0f, 1f) return Color( red = foreground.red * clampedAlpha + background.red * (1f - clampedAlpha), green = foreground.green * clampedAlpha + background.green * (1f - clampedAlpha), blue = foreground.blue * clampedAlpha + background.blue * (1f - clampedAlpha), alpha = 1f ) }