diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/ReaderBackground.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/ReaderBackground.kt index 0f4d7da7e..e24d372fb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/ReaderBackground.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/prefs/ReaderBackground.kt @@ -1,11 +1,13 @@ package org.koitharu.kotatsu.core.prefs import android.content.Context +import android.graphics.drawable.Drawable import android.view.ContextThemeWrapper import androidx.annotation.Keep import androidx.core.content.ContextCompat import androidx.core.graphics.drawable.toDrawable import org.koitharu.kotatsu.core.util.ext.getThemeDrawable +import org.koitharu.kotatsu.core.util.ext.isNightMode import com.google.android.material.R as materialR @Keep @@ -13,7 +15,7 @@ enum class ReaderBackground { DEFAULT, LIGHT, DARK, WHITE, BLACK; - fun resolve(context: Context) = when (this) { + fun resolve(context: Context): Drawable? = when (this) { DEFAULT -> context.getThemeDrawable(android.R.attr.windowBackground) LIGHT -> ContextThemeWrapper(context, materialR.style.ThemeOverlay_Material3_Light) .getThemeDrawable(android.R.attr.windowBackground) @@ -24,4 +26,14 @@ enum class ReaderBackground { WHITE -> ContextCompat.getColor(context, android.R.color.white).toDrawable() BLACK -> ContextCompat.getColor(context, android.R.color.black).toDrawable() } + + fun isLight(context: Context): Boolean = when (this) { + DEFAULT -> !context.resources.isNightMode + + LIGHT, + WHITE -> true + + DARK, + BLACK -> false + } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt index 185c3af4a..b01b62d9d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Theme.kt @@ -1,6 +1,8 @@ package org.koitharu.kotatsu.core.util.ext import android.content.Context +import android.content.res.Configuration +import android.content.res.Resources import android.content.res.TypedArray import android.graphics.Color import android.graphics.drawable.Drawable @@ -12,6 +14,9 @@ import androidx.core.content.ContextCompat import androidx.core.content.res.use import androidx.core.graphics.ColorUtils +val Resources.isNightMode: Boolean + get() = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES + fun Context.getThemeDrawable( @AttrRes resId: Int, ) = obtainStyledAttributes(intArrayOf(resId)).use { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt index 4cc5e354e..9204ed369 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt @@ -154,6 +154,9 @@ class ReaderActivity : .setAnchorView(viewBinding.appbarBottom) .show() } + viewModel.readerSettings.observe(this) { + viewBinding.infoBar.applyColorScheme(isBlackOnWhite = it.background.isLight(this)) + } viewModel.isZoomControlsEnabled.observe(this) { viewBinding.zoomControl.isVisible = it } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt index b2197014e..98456bbc4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderInfoBarView.kt @@ -20,11 +20,11 @@ import android.view.WindowInsets import androidx.annotation.AttrRes import androidx.core.content.ContextCompat import androidx.core.content.withStyledAttributes -import androidx.core.graphics.withScale import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.util.ext.getThemeColorStateList +import org.koitharu.kotatsu.core.util.ext.isNightMode import org.koitharu.kotatsu.core.util.ext.measureDimension import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.parsers.util.format @@ -34,6 +34,8 @@ import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import com.google.android.material.R as materialR +private const val ALPHA_TEXT = 200 +private const val ALPHA_BG = 180 class ReaderInfoBarView @JvmOverloads constructor( context: Context, @@ -52,16 +54,16 @@ class ReaderInfoBarView @JvmOverloads constructor( private val insetRightFallback: Int private val insetTopFallback: Int private val insetCornerFallback = getSystemUiDimensionOffset("rounded_corner_content_padding") - private val colorText = + private var colorText = (context.getThemeColorStateList(materialR.attr.colorOnSurface) - ?: ColorStateList.valueOf(Color.BLACK)).withAlpha(200) - private val colorOutline = + ?: ColorStateList.valueOf(Color.BLACK)).withAlpha(ALPHA_TEXT) + private var colorBackground = (context.getThemeColorStateList(materialR.attr.colorSurface) - ?: ColorStateList.valueOf(Color.WHITE)).withAlpha(200) + ?: ColorStateList.valueOf(Color.WHITE)).withAlpha(ALPHA_BG) private val batteryIcon = ContextCompat.getDrawable(context, R.drawable.ic_battery_outline) private var currentTextColor: Int = Color.TRANSPARENT - private var currentOutlineColor: Int = Color.TRANSPARENT + private var currentBackgroundColor: Int = Color.TRANSPARENT private var timeText = timeFormat.format(LocalTime.now()) private var batteryText = "" private var text: String = "" @@ -106,24 +108,28 @@ class ReaderInfoBarView @JvmOverloads constructor( override fun onDraw(canvas: Canvas) { super.onDraw(canvas) + canvas.drawColor(currentBackgroundColor) computeTextHeight() val h = innerHeight.toFloat() val ty = h / 2f + textBounds.height() / 2f - textBounds.bottom paint.textAlign = Paint.Align.LEFT - canvas.drawTextOutline( + paint.color = currentTextColor + paint.style = Paint.Style.FILL + canvas.drawText( text, (paddingLeft + insetLeft).toFloat(), paddingTop + insetTop + ty, + paint, ) if (isTimeVisible) { paint.textAlign = Paint.Align.RIGHT var endX = (width - paddingRight - insetRight).toFloat() - canvas.drawTextOutline(timeText, endX, paddingTop + insetTop + ty) + canvas.drawText(timeText, endX, paddingTop + insetTop + ty, paint) if (batteryText.isNotEmpty()) { paint.getTextBounds(timeText, 0, timeText.length, textBounds) endX -= textBounds.width() endX -= h * 0.6f - canvas.drawTextOutline(batteryText, endX, paddingTop + insetTop + ty) + canvas.drawText(batteryText, endX, paddingTop + insetTop + ty, paint) batteryIcon?.let { paint.getTextBounds(batteryText, 0, batteryText.length, textBounds) endX -= textBounds.width() @@ -134,7 +140,7 @@ class ReaderInfoBarView @JvmOverloads constructor( endX.toInt(), (iconCenter + h / 2).toInt(), ) - it.drawWithOutline(canvas) + it.draw(canvas) } } } @@ -185,13 +191,25 @@ class ReaderInfoBarView @JvmOverloads constructor( override fun drawableStateChanged() { currentTextColor = colorText.getColorForState(drawableState, colorText.defaultColor) - currentOutlineColor = colorOutline.getColorForState(drawableState, colorOutline.defaultColor) + currentBackgroundColor = colorBackground.getColorForState(drawableState, colorBackground.defaultColor) super.drawableStateChanged() if (batteryIcon != null && batteryIcon.isStateful && batteryIcon.setState(drawableState)) { invalidateDrawable(batteryIcon) } } + fun applyColorScheme(isBlackOnWhite: Boolean) { + val isDarkTheme = resources.isNightMode + colorText = (context.getThemeColorStateList( + if (isBlackOnWhite != isDarkTheme) materialR.attr.colorOnSurface else materialR.attr.colorOnSurfaceInverse, + ) ?: ColorStateList.valueOf(if (isBlackOnWhite) Color.BLACK else Color.WHITE)).withAlpha(ALPHA_TEXT) + colorBackground = (context.getThemeColorStateList( + if (isBlackOnWhite != isDarkTheme) materialR.attr.colorSurface else materialR.attr.colorSurfaceInverse, + ) ?: ColorStateList.valueOf(if (isBlackOnWhite) Color.WHITE else Color.BLACK)).withAlpha(ALPHA_BG) + drawableStateChanged() + } + + @SuppressLint("StringFormatMatches") fun update(state: ReaderUiState?) { text = if (state != null) { context.getString( @@ -222,32 +240,6 @@ class ReaderInfoBarView @JvmOverloads constructor( return textBounds.height() } - private fun Canvas.drawTextOutline(text: String, x: Float, y: Float) { - paint.color = currentOutlineColor - paint.style = Paint.Style.STROKE - drawText(text, x, y, paint) - paint.color = currentTextColor - paint.style = Paint.Style.FILL - drawText(text, x, y, paint) - } - - private fun Drawable.drawWithOutline(canvas: Canvas) { - if (bounds.isEmpty) { - return - } - var requiredScale = (bounds.width() + paint.strokeWidth * 2f) / bounds.width().toFloat() - setTint(currentOutlineColor) - canvas.withScale(requiredScale, requiredScale, bounds.exactCenterX(), bounds.exactCenterY()) { - draw(canvas) - } - requiredScale = 1f / requiredScale - canvas.withScale(requiredScale, requiredScale, bounds.exactCenterX(), bounds.exactCenterY()) { - draw(canvas) - } - setTint(currentTextColor) - draw(canvas) - } - private fun updateCutoutInsets(insetsCompat: WindowInsetsCompat?) { insetLeft = insetLeftFallback insetRight = insetRightFallback diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt index f93822540..03ea3677d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/config/ReaderSettings.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.core.prefs.ReaderBackground import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.util.ext.isLowRamDevice import org.koitharu.kotatsu.reader.domain.ReaderColorFilter @@ -34,6 +35,9 @@ class ReaderSettings( val zoomMode: ZoomMode get() = settings.zoomMode + val background: ReaderBackground + get() = settings.readerBackground + val colorFilter: ReaderColorFilter? get() = colorFilterFlow.value?.takeUnless { it.isEmpty } ?: settings.readerColorFilter @@ -51,8 +55,7 @@ class ReaderSettings( get() = settings.isPagesNumbersEnabled fun applyBackground(view: View) { - val bg = settings.readerBackground - view.background = bg.resolve(view.context) + view.background = background.resolve(view.context) } fun isPagesCropEnabled(isWebtoon: Boolean) = settings.isPagesCropEnabled(