ReaderTransitionView: Use context theme color for texts (#5738)

* Put themed reader context in adapter

This avoids creating themed context everytime the page holder is created, this
also allows the transition view to use the same themed context.

* Check against app night mode to create themed reader context

* ReaderTransitionView: Use context theme color for texts

The whole reader will need to be recreated when changing reader background while
webtoon mode is used, because recreating just the RecyclerView without messing
up the scroll position is impossible (I hope I just missed something).
This commit is contained in:
Ivan Iskandar 2021-08-19 20:10:05 +07:00 committed by GitHub
parent 27133520fc
commit 914b686c8e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 64 additions and 59 deletions

View file

@ -8,22 +8,19 @@ import androidx.core.text.bold
import androidx.core.text.buildSpannedString import androidx.core.text.buildSpannedString
import androidx.core.view.isVisible import androidx.core.view.isVisible
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.ReaderTransitionViewBinding import eu.kanade.tachiyomi.databinding.ReaderTransitionViewBinding
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
import eu.kanade.tachiyomi.util.system.isNightMode
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
LinearLayout(context, attrs) { LinearLayout(context, attrs) {
private val binding: ReaderTransitionViewBinding private val binding: ReaderTransitionViewBinding =
ReaderTransitionViewBinding.inflate(LayoutInflater.from(context), this, true)
init { init {
binding = ReaderTransitionViewBinding.inflate(LayoutInflater.from(context), this, true)
layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
} }
fun bind(transition: ChapterTransition) { fun bind(transition: ChapterTransition) {
when (transition) { when (transition) {
is ChapterTransition.Prev -> bindPrevChapterTransition(transition) is ChapterTransition.Prev -> bindPrevChapterTransition(transition)
@ -31,26 +28,6 @@ class ReaderTransitionView @JvmOverloads constructor(context: Context, attrs: At
} }
missingChapterWarning(transition) missingChapterWarning(transition)
val color = when (Injekt.get<PreferencesHelper>().readerTheme().get()) {
0 -> context.getColor(android.R.color.black)
3 -> context.getColor(automaticTextColor())
else -> context.getColor(android.R.color.white)
}
listOf(binding.upperText, binding.warningText, binding.lowerText).forEach {
it.setTextColor(color)
}
}
/**
* Picks text color for [ReaderActivity] based on light/dark theme preference
*/
private fun automaticTextColor(): Int {
return if (context.isNightMode()) {
android.R.color.white
} else {
android.R.color.black
}
} }
/** /**

View file

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ActionBar import android.app.ActionBar
import android.content.Context
import android.graphics.PointF import android.graphics.PointF
import android.graphics.drawable.Animatable import android.graphics.drawable.Animatable
import android.view.GestureDetector import android.view.GestureDetector
@ -30,7 +31,6 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig.ZoomType
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.widget.ViewPagerAdapter import eu.kanade.tachiyomi.widget.ViewPagerAdapter
import rx.Observable import rx.Observable
@ -46,9 +46,10 @@ import java.util.concurrent.TimeUnit
*/ */
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class PagerPageHolder( class PagerPageHolder(
readerThemedContext: Context,
val viewer: PagerViewer, val viewer: PagerViewer,
val page: ReaderPage val page: ReaderPage
) : FrameLayout(viewer.activity), ViewPagerAdapter.PositionableView { ) : FrameLayout(readerThemedContext), ViewPagerAdapter.PositionableView {
/** /**
* Item that identifies this view. Needed by the adapter to not recreate views. * Item that identifies this view. Needed by the adapter to not recreate views.
@ -97,12 +98,6 @@ class PagerPageHolder(
*/ */
private var readImageHeaderSubscription: Subscription? = null private var readImageHeaderSubscription: Subscription? = null
/**
* Context that has been wrapped to use the correct theme values based on the
* current app theme and reader background color
*/
private val readerThemedContext = context.createReaderThemeContext(viewer.config.theme)
val stateChangedListener = object : SubsamplingScaleImageView.OnStateChangedListener { val stateChangedListener = object : SubsamplingScaleImageView.OnStateChangedListener {
override fun onScaleChanged(newScale: Float, origin: Int) { override fun onScaleChanged(newScale: Float, origin: Int) {
viewer.activity.hideMenu() viewer.activity.hideMenu()
@ -423,7 +418,7 @@ class PagerPageHolder(
private fun initRetryButton(): PagerButton { private fun initRetryButton(): PagerButton {
if (retryButton != null) return retryButton!! if (retryButton != null) return retryButton!!
retryButton = PagerButton(readerThemedContext, viewer).apply { retryButton = PagerButton(context, viewer).apply {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
gravity = Gravity.CENTER gravity = Gravity.CENTER
} }
@ -450,7 +445,7 @@ class PagerPageHolder(
} }
decodeErrorLayout = decodeLayout decodeErrorLayout = decodeLayout
TextView(readerThemedContext).apply { TextView(context).apply {
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(margins, margins, margins, margins) setMargins(margins, margins, margins, margins)
} }
@ -460,7 +455,7 @@ class PagerPageHolder(
decodeLayout.addView(this) decodeLayout.addView(this)
} }
PagerButton(readerThemedContext, viewer).apply { PagerButton(context, viewer).apply {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(margins, margins, margins, margins) setMargins(margins, margins, margins, margins)
} }
@ -474,7 +469,7 @@ class PagerPageHolder(
val imageUrl = page.imageUrl val imageUrl = page.imageUrl
if (imageUrl.orEmpty().startsWith("http", true)) { if (imageUrl.orEmpty().startsWith("http", true)) {
PagerButton(readerThemedContext, viewer).apply { PagerButton(context, viewer).apply {
layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply { layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
setMargins(margins, margins, margins, margins) setMargins(margins, margins, margins, margins)
} }

View file

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.ui.reader.viewer.pager package eu.kanade.tachiyomi.ui.reader.viewer.pager
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -23,9 +24,10 @@ import rx.android.schedulers.AndroidSchedulers
*/ */
@SuppressLint("ViewConstructor") @SuppressLint("ViewConstructor")
class PagerTransitionHolder( class PagerTransitionHolder(
readerThemedContext: Context,
val viewer: PagerViewer, val viewer: PagerViewer,
val transition: ChapterTransition val transition: ChapterTransition
) : LinearLayout(viewer.activity), ViewPagerAdapter.PositionableView { ) : LinearLayout(readerThemedContext), ViewPagerAdapter.PositionableView {
/** /**
* Item that identifies this view. Needed by the adapter to not recreate views. * Item that identifies this view. Needed by the adapter to not recreate views.

View file

@ -77,7 +77,6 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
pager.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) pager.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
pager.offscreenPageLimit = 1 pager.offscreenPageLimit = 1
pager.id = R.id.reader_pager pager.id = R.id.reader_pager
pager.adapter = adapter
pager.addOnPageChangeListener( pager.addOnPageChangeListener(
object : ViewPager.SimpleOnPageChangeListener() { object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
@ -326,6 +325,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
*/ */
private fun refreshAdapter() { private fun refreshAdapter() {
val currentItem = pager.currentItem val currentItem = pager.currentItem
adapter.refresh()
pager.adapter = adapter pager.adapter = adapter
pager.setCurrentItem(currentItem, false) pager.setCurrentItem(currentItem, false)
} }

View file

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.viewer.hasMissingChapters import eu.kanade.tachiyomi.ui.reader.viewer.hasMissingChapters
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
import eu.kanade.tachiyomi.widget.ViewPagerAdapter import eu.kanade.tachiyomi.widget.ViewPagerAdapter
import timber.log.Timber import timber.log.Timber
@ -32,6 +33,12 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
var currentChapter: ReaderChapter? = null var currentChapter: ReaderChapter? = null
/**
* Context that has been wrapped to use the correct theme values based on the
* current app theme and reader background color
*/
private var readerThemedContext = viewer.activity.createReaderThemeContext(viewer.config.theme)
/** /**
* Updates this adapter with the given [chapters]. It handles setting a few pages of the * Updates this adapter with the given [chapters]. It handles setting a few pages of the
* next/previous chapter to allow seamless transitions and inverting the pages if the viewer * next/previous chapter to allow seamless transitions and inverting the pages if the viewer
@ -130,8 +137,8 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
*/ */
override fun createView(container: ViewGroup, position: Int): View { override fun createView(container: ViewGroup, position: Int): View {
return when (val item = items[position]) { return when (val item = items[position]) {
is ReaderPage -> PagerPageHolder(viewer, item) is ReaderPage -> PagerPageHolder(readerThemedContext, viewer, item)
is ChapterTransition -> PagerTransitionHolder(viewer, item) is ChapterTransition -> PagerTransitionHolder(readerThemedContext, viewer, item)
else -> throw NotImplementedError("Holder for ${item.javaClass} not implemented") else -> throw NotImplementedError("Holder for ${item.javaClass} not implemented")
} }
} }
@ -188,4 +195,8 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
items.removeAll(insertPages) items.removeAll(insertPages)
notifyDataSetChanged() notifyDataSetChanged()
} }
fun refresh() {
readerThemedContext = viewer.activity.createReaderThemeContext(viewer.config.theme)
}
} }

View file

@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.viewer.hasMissingChapters import eu.kanade.tachiyomi.ui.reader.viewer.hasMissingChapters
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
/** /**
* RecyclerView Adapter used by this [viewer] to where [ViewerChapters] updates are posted. * RecyclerView Adapter used by this [viewer] to where [ViewerChapters] updates are posted.
@ -24,6 +25,12 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter<RecyclerV
var currentChapter: ReaderChapter? = null var currentChapter: ReaderChapter? = null
/**
* Context that has been wrapped to use the correct theme values based on the
* current app theme and reader background color
*/
private var readerThemedContext = viewer.activity.createReaderThemeContext(viewer.config.theme)
/** /**
* Updates this adapter with the given [chapters]. It handles setting a few pages of the * Updates this adapter with the given [chapters]. It handles setting a few pages of the
* next/previous chapter to allow seamless transitions. * next/previous chapter to allow seamless transitions.
@ -77,6 +84,10 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter<RecyclerV
result.dispatchUpdatesTo(this) result.dispatchUpdatesTo(this)
} }
fun refresh() {
readerThemedContext = viewer.activity.createReaderThemeContext(viewer.config.theme)
}
/** /**
* Returns the amount of items of the adapter. * Returns the amount of items of the adapter.
*/ */
@ -101,11 +112,11 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter<RecyclerV
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) { return when (viewType) {
PAGE_VIEW -> { PAGE_VIEW -> {
val view = FrameLayout(parent.context) val view = FrameLayout(readerThemedContext)
WebtoonPageHolder(view, viewer) WebtoonPageHolder(view, viewer)
} }
TRANSITION_VIEW -> { TRANSITION_VIEW -> {
val view = LinearLayout(parent.context) val view = LinearLayout(readerThemedContext)
WebtoonTransitionHolder(view, viewer) WebtoonTransitionHolder(view, viewer)
} }
else -> error("Unknown view type") else -> error("Unknown view type")

View file

@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.navigation.KindlishNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.LNavigation
import eu.kanade.tachiyomi.ui.reader.viewer.navigation.RightAndLeftNavigation import eu.kanade.tachiyomi.ui.reader.viewer.navigation.RightAndLeftNavigation
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.drop import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -22,14 +23,15 @@ class WebtoonConfig(
preferences: PreferencesHelper = Injekt.get() preferences: PreferencesHelper = Injekt.get()
) : ViewerConfig(preferences, scope) { ) : ViewerConfig(preferences, scope) {
var themeChangedListener: (() -> Unit)? = null
var imageCropBorders = false var imageCropBorders = false
private set private set
var sidePadding = 0 var sidePadding = 0
private set private set
var theme = preferences.readerTheme().get() val theme = preferences.readerTheme().get()
private set
init { init {
preferences.cropBordersWebtoon() preferences.cropBordersWebtoon()
@ -54,8 +56,11 @@ class WebtoonConfig(
preferences.dualPageInvertWebtoon() preferences.dualPageInvertWebtoon()
.register({ dualPageInvert = it }, { imagePropertyChangedListener?.invoke() }) .register({ dualPageInvert = it }, { imagePropertyChangedListener?.invoke() })
preferences.readerTheme() preferences.readerTheme().asFlow()
.register({ theme = it }, { imagePropertyChangedListener?.invoke() }) .drop(1)
.distinctUntilChanged()
.onEach { themeChangedListener?.invoke() }
.launchIn(scope)
} }
override var navigator: ViewerNavigation = defaultNavigation() override var navigator: ViewerNavigation = defaultNavigation()

View file

@ -27,7 +27,6 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator import eu.kanade.tachiyomi.ui.reader.viewer.ReaderProgressIndicator
import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.createReaderThemeContext
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPx
import rx.Observable import rx.Observable
import rx.Subscription import rx.Subscription
@ -346,8 +345,7 @@ class WebtoonPageHolder(
progressContainer = FrameLayout(context) progressContainer = FrameLayout(context)
frame.addView(progressContainer, MATCH_PARENT, parentHeight) frame.addView(progressContainer, MATCH_PARENT, parentHeight)
val indicatorContext = context.createReaderThemeContext(viewer.config.theme) val progress = ReaderProgressIndicator(context).apply {
val progress = ReaderProgressIndicator(indicatorContext).apply {
updateLayoutParams<FrameLayout.LayoutParams> { updateLayoutParams<FrameLayout.LayoutParams> {
gravity = Gravity.CENTER_HORIZONTAL gravity = Gravity.CENTER_HORIZONTAL
updateMargins(top = parentHeight / 4) updateMargins(top = parentHeight / 4)

View file

@ -48,6 +48,11 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
*/ */
private val layoutManager = WebtoonLayoutManager(activity) private val layoutManager = WebtoonLayoutManager(activity)
/**
* Configuration used by this viewer, like allow taps, or crop image borders.
*/
val config = WebtoonConfig(scope)
/** /**
* Adapter of the recycler view. * Adapter of the recycler view.
*/ */
@ -63,11 +68,6 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
*/ */
private var currentPage: Any? = null private var currentPage: Any? = null
/**
* Configuration used by this viewer, like allow taps, or crop image borders.
*/
val config = WebtoonConfig(scope)
/** /**
* Subscriptions to keep while this viewer is used. * Subscriptions to keep while this viewer is used.
*/ */
@ -138,6 +138,10 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
refreshAdapter() refreshAdapter()
} }
config.themeChangedListener = {
activity.recreate()
}
config.navigationModeChangedListener = { config.navigationModeChangedListener = {
val showOnStart = config.navigationOverlayOnStart || config.forceNavigationOverlay val showOnStart = config.navigationOverlayOnStart || config.forceNavigationOverlay
activity.binding.navigationOverlay.setNavigation(config.navigator, config.tappingEnabled, showOnStart) activity.binding.navigationOverlay.setNavigation(config.navigator, config.tappingEnabled, showOnStart)
@ -338,6 +342,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr
*/ */
private fun refreshAdapter() { private fun refreshAdapter() {
val position = layoutManager.findLastEndVisibleItemPosition() val position = layoutManager.findLastEndVisibleItemPosition()
adapter.refresh()
adapter.notifyItemRangeChanged( adapter.notifyItemRangeChanged(
max(0, position - 3), max(0, position - 3),
min(position + 3, adapter.itemCount - 1) min(position + 3, adapter.itemCount - 1)

View file

@ -310,7 +310,7 @@ fun Context.isNightMode(): Boolean {
fun Context.createReaderThemeContext(readerThemeSelected: Int): Context { fun Context.createReaderThemeContext(readerThemeSelected: Int): Context {
val isDarkBackground = when (readerThemeSelected) { val isDarkBackground = when (readerThemeSelected) {
1, 2 -> true // Black, Gray 1, 2 -> true // Black, Gray
3 -> isNightMode() // Automatic bg uses activity background by default 3 -> applicationContext.isNightMode() // Automatic bg uses activity background by default
else -> false // White else -> false // White
} }
val expected = if (isDarkBackground) Configuration.UI_MODE_NIGHT_YES else Configuration.UI_MODE_NIGHT_NO val expected = if (isDarkBackground) Configuration.UI_MODE_NIGHT_YES else Configuration.UI_MODE_NIGHT_NO

View file

@ -12,7 +12,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:textSize="17.5sp" android:textAppearance="?attr/textAppearanceSubtitle1"
tools:text="Top" /> tools:text="Top" />
<LinearLayout <LinearLayout
@ -35,6 +35,7 @@
android:id="@+id/warning_text" android:id="@+id/warning_text"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceSubtitle1"
tools:text="Warning" /> tools:text="Warning" />
</LinearLayout> </LinearLayout>
@ -43,7 +44,7 @@
android:id="@+id/lower_text" android:id="@+id/lower_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="17.5sp" android:textAppearance="?attr/textAppearanceSubtitle1"
tools:text="Bottom" /> tools:text="Bottom" />
</LinearLayout> </LinearLayout>