Update reader info bar

This commit is contained in:
Koitharu
2025-01-19 14:53:32 +02:00
parent 914dd9670a
commit 6360731f34
5 changed files with 55 additions and 40 deletions

View File

@@ -1,11 +1,13 @@
package org.koitharu.kotatsu.core.prefs package org.koitharu.kotatsu.core.prefs
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable
import android.view.ContextThemeWrapper import android.view.ContextThemeWrapper
import androidx.annotation.Keep import androidx.annotation.Keep
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import org.koitharu.kotatsu.core.util.ext.getThemeDrawable import org.koitharu.kotatsu.core.util.ext.getThemeDrawable
import org.koitharu.kotatsu.core.util.ext.isNightMode
import com.google.android.material.R as materialR import com.google.android.material.R as materialR
@Keep @Keep
@@ -13,7 +15,7 @@ enum class ReaderBackground {
DEFAULT, LIGHT, DARK, WHITE, BLACK; 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) DEFAULT -> context.getThemeDrawable(android.R.attr.windowBackground)
LIGHT -> ContextThemeWrapper(context, materialR.style.ThemeOverlay_Material3_Light) LIGHT -> ContextThemeWrapper(context, materialR.style.ThemeOverlay_Material3_Light)
.getThemeDrawable(android.R.attr.windowBackground) .getThemeDrawable(android.R.attr.windowBackground)
@@ -24,4 +26,14 @@ enum class ReaderBackground {
WHITE -> ContextCompat.getColor(context, android.R.color.white).toDrawable() WHITE -> ContextCompat.getColor(context, android.R.color.white).toDrawable()
BLACK -> ContextCompat.getColor(context, android.R.color.black).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
}
} }

View File

@@ -1,6 +1,8 @@
package org.koitharu.kotatsu.core.util.ext package org.koitharu.kotatsu.core.util.ext
import android.content.Context import android.content.Context
import android.content.res.Configuration
import android.content.res.Resources
import android.content.res.TypedArray import android.content.res.TypedArray
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
@@ -12,6 +14,9 @@ import androidx.core.content.ContextCompat
import androidx.core.content.res.use import androidx.core.content.res.use
import androidx.core.graphics.ColorUtils 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( fun Context.getThemeDrawable(
@AttrRes resId: Int, @AttrRes resId: Int,
) = obtainStyledAttributes(intArrayOf(resId)).use { ) = obtainStyledAttributes(intArrayOf(resId)).use {

View File

@@ -154,6 +154,9 @@ class ReaderActivity :
.setAnchorView(viewBinding.appbarBottom) .setAnchorView(viewBinding.appbarBottom)
.show() .show()
} }
viewModel.readerSettings.observe(this) {
viewBinding.infoBar.applyColorScheme(isBlackOnWhite = it.background.isLight(this))
}
viewModel.isZoomControlsEnabled.observe(this) { viewModel.isZoomControlsEnabled.observe(this) {
viewBinding.zoomControl.isVisible = it viewBinding.zoomControl.isVisible = it
} }

View File

@@ -20,11 +20,11 @@ import android.view.WindowInsets
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.withStyledAttributes import androidx.core.content.withStyledAttributes
import androidx.core.graphics.withScale
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.getThemeColorStateList 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.measureDimension
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.parsers.util.format import org.koitharu.kotatsu.parsers.util.format
@@ -34,6 +34,8 @@ import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle import java.time.format.FormatStyle
import com.google.android.material.R as materialR import com.google.android.material.R as materialR
private const val ALPHA_TEXT = 200
private const val ALPHA_BG = 180
class ReaderInfoBarView @JvmOverloads constructor( class ReaderInfoBarView @JvmOverloads constructor(
context: Context, context: Context,
@@ -52,16 +54,16 @@ class ReaderInfoBarView @JvmOverloads constructor(
private val insetRightFallback: Int private val insetRightFallback: Int
private val insetTopFallback: Int private val insetTopFallback: Int
private val insetCornerFallback = getSystemUiDimensionOffset("rounded_corner_content_padding") private val insetCornerFallback = getSystemUiDimensionOffset("rounded_corner_content_padding")
private val colorText = private var colorText =
(context.getThemeColorStateList(materialR.attr.colorOnSurface) (context.getThemeColorStateList(materialR.attr.colorOnSurface)
?: ColorStateList.valueOf(Color.BLACK)).withAlpha(200) ?: ColorStateList.valueOf(Color.BLACK)).withAlpha(ALPHA_TEXT)
private val colorOutline = private var colorBackground =
(context.getThemeColorStateList(materialR.attr.colorSurface) (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 val batteryIcon = ContextCompat.getDrawable(context, R.drawable.ic_battery_outline)
private var currentTextColor: Int = Color.TRANSPARENT 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 timeText = timeFormat.format(LocalTime.now())
private var batteryText = "" private var batteryText = ""
private var text: String = "" private var text: String = ""
@@ -106,24 +108,28 @@ class ReaderInfoBarView @JvmOverloads constructor(
override fun onDraw(canvas: Canvas) { override fun onDraw(canvas: Canvas) {
super.onDraw(canvas) super.onDraw(canvas)
canvas.drawColor(currentBackgroundColor)
computeTextHeight() computeTextHeight()
val h = innerHeight.toFloat() val h = innerHeight.toFloat()
val ty = h / 2f + textBounds.height() / 2f - textBounds.bottom val ty = h / 2f + textBounds.height() / 2f - textBounds.bottom
paint.textAlign = Paint.Align.LEFT paint.textAlign = Paint.Align.LEFT
canvas.drawTextOutline( paint.color = currentTextColor
paint.style = Paint.Style.FILL
canvas.drawText(
text, text,
(paddingLeft + insetLeft).toFloat(), (paddingLeft + insetLeft).toFloat(),
paddingTop + insetTop + ty, paddingTop + insetTop + ty,
paint,
) )
if (isTimeVisible) { if (isTimeVisible) {
paint.textAlign = Paint.Align.RIGHT paint.textAlign = Paint.Align.RIGHT
var endX = (width - paddingRight - insetRight).toFloat() var endX = (width - paddingRight - insetRight).toFloat()
canvas.drawTextOutline(timeText, endX, paddingTop + insetTop + ty) canvas.drawText(timeText, endX, paddingTop + insetTop + ty, paint)
if (batteryText.isNotEmpty()) { if (batteryText.isNotEmpty()) {
paint.getTextBounds(timeText, 0, timeText.length, textBounds) paint.getTextBounds(timeText, 0, timeText.length, textBounds)
endX -= textBounds.width() endX -= textBounds.width()
endX -= h * 0.6f endX -= h * 0.6f
canvas.drawTextOutline(batteryText, endX, paddingTop + insetTop + ty) canvas.drawText(batteryText, endX, paddingTop + insetTop + ty, paint)
batteryIcon?.let { batteryIcon?.let {
paint.getTextBounds(batteryText, 0, batteryText.length, textBounds) paint.getTextBounds(batteryText, 0, batteryText.length, textBounds)
endX -= textBounds.width() endX -= textBounds.width()
@@ -134,7 +140,7 @@ class ReaderInfoBarView @JvmOverloads constructor(
endX.toInt(), endX.toInt(),
(iconCenter + h / 2).toInt(), (iconCenter + h / 2).toInt(),
) )
it.drawWithOutline(canvas) it.draw(canvas)
} }
} }
} }
@@ -185,13 +191,25 @@ class ReaderInfoBarView @JvmOverloads constructor(
override fun drawableStateChanged() { override fun drawableStateChanged() {
currentTextColor = colorText.getColorForState(drawableState, colorText.defaultColor) currentTextColor = colorText.getColorForState(drawableState, colorText.defaultColor)
currentOutlineColor = colorOutline.getColorForState(drawableState, colorOutline.defaultColor) currentBackgroundColor = colorBackground.getColorForState(drawableState, colorBackground.defaultColor)
super.drawableStateChanged() super.drawableStateChanged()
if (batteryIcon != null && batteryIcon.isStateful && batteryIcon.setState(drawableState)) { if (batteryIcon != null && batteryIcon.isStateful && batteryIcon.setState(drawableState)) {
invalidateDrawable(batteryIcon) 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?) { fun update(state: ReaderUiState?) {
text = if (state != null) { text = if (state != null) {
context.getString( context.getString(
@@ -222,32 +240,6 @@ class ReaderInfoBarView @JvmOverloads constructor(
return textBounds.height() 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?) { private fun updateCutoutInsets(insetsCompat: WindowInsetsCompat?) {
insetLeft = insetLeftFallback insetLeft = insetLeftFallback
insetRight = insetRightFallback insetRight = insetRightFallback

View File

@@ -18,6 +18,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.core.model.ZoomMode
import org.koitharu.kotatsu.core.prefs.AppSettings 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.prefs.ReaderMode
import org.koitharu.kotatsu.core.util.ext.isLowRamDevice import org.koitharu.kotatsu.core.util.ext.isLowRamDevice
import org.koitharu.kotatsu.reader.domain.ReaderColorFilter import org.koitharu.kotatsu.reader.domain.ReaderColorFilter
@@ -34,6 +35,9 @@ class ReaderSettings(
val zoomMode: ZoomMode val zoomMode: ZoomMode
get() = settings.zoomMode get() = settings.zoomMode
val background: ReaderBackground
get() = settings.readerBackground
val colorFilter: ReaderColorFilter? val colorFilter: ReaderColorFilter?
get() = colorFilterFlow.value?.takeUnless { it.isEmpty } ?: settings.readerColorFilter get() = colorFilterFlow.value?.takeUnless { it.isEmpty } ?: settings.readerColorFilter
@@ -51,8 +55,7 @@ class ReaderSettings(
get() = settings.isPagesNumbersEnabled get() = settings.isPagesNumbersEnabled
fun applyBackground(view: View) { fun applyBackground(view: View) {
val bg = settings.readerBackground view.background = background.resolve(view.context)
view.background = bg.resolve(view.context)
} }
fun isPagesCropEnabled(isWebtoon: Boolean) = settings.isPagesCropEnabled( fun isPagesCropEnabled(isWebtoon: Boolean) = settings.isPagesCropEnabled(