Update reader info bar
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
Reference in New Issue
Block a user