Merge reader settings and color filter sheets

Heavily influenced by fe2543b9d5 (diff-8f47d7b7b53769ac18c28fe9978140c6bef44709879567acab2c6ef3270cd3a8)
This commit is contained in:
arkon
2021-03-25 23:10:22 -04:00
parent a01792ac9a
commit e0b7698d40
14 changed files with 484 additions and 475 deletions

View File

@ -20,7 +20,7 @@ class LibrarySettingsSheet(
router: Router,
private val trackManager: TrackManager = Injekt.get(),
onGroupClickListener: (ExtendedNavigationView.Group) -> Unit
) : TabbedBottomSheetDialog(router) {
) : TabbedBottomSheetDialog(router.activity!!) {
val filters: Filter
private val sort: Sort

View File

@ -17,7 +17,7 @@ class ChaptersSettingsSheet(
private val router: Router,
private val presenter: MangaPresenter,
onGroupClickListener: (ExtendedNavigationView.Group) -> Unit
) : TabbedBottomSheetDialog(router) {
) : TabbedBottomSheetDialog(router.activity!!) {
val filters: Filter
private val sort: Sort

View File

@ -45,7 +45,6 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReaderColorFilterSheet
import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsSheet
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer
@ -365,17 +364,6 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
}
.launchIn(lifecycleScope)
binding.actionCustomFilter.setOnClickListener {
val sheet = ReaderColorFilterSheet(this)
// Remove dimmed backdrop so changes can be previewed
.apply { window?.setDimAmount(0f) }
// Hide toolbars while sheet is open for better preview
sheet.setOnDismissListener { setMenuVisibility(true) }
setMenuVisibility(false)
sheet.show()
}
binding.actionSettings.setOnClickListener {
ReaderSettingsSheet(this).show()
}
@ -393,7 +381,7 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
* Sets the visibility of the menu according to [visible] and with an optional parameter to
* [animate] the views.
*/
private fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
menuVisible = visible
if (visible) {
if (preferences.fullscreen().get()) {

View File

@ -1,4 +1,4 @@
package eu.kanade.tachiyomi.ui.reader.setting
package eu.kanade.tachiyomi.ui.reader
import android.content.Context
import android.graphics.Canvas

View File

@ -1,20 +1,21 @@
package eu.kanade.tachiyomi.ui.reader.setting
import android.view.ViewGroup
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.SeekBar
import androidx.annotation.ColorInt
import androidx.core.graphics.alpha
import androidx.core.graphics.blue
import androidx.core.graphics.green
import androidx.core.graphics.red
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.lifecycleScope
import com.google.android.material.bottomsheet.BottomSheetBehavior
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.ReaderColorFilterSheetBinding
import eu.kanade.tachiyomi.databinding.ReaderColorFilterSettingsBinding
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import eu.kanade.tachiyomi.widget.SimpleSeekBarListener
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample
@ -23,30 +24,27 @@ import uy.kohesive.injekt.injectLazy
/**
* Color filter sheet to toggle custom filter and brightness overlay.
*/
class ReaderColorFilterSheet(private val activity: ReaderActivity) : BaseBottomSheetDialog(activity) {
class ReaderColorFilterSettings @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
NestedScrollView(context, attrs) {
private val preferences: PreferencesHelper by injectLazy()
private var sheetBehavior: BottomSheetBehavior<*>? = null
private val binding = ReaderColorFilterSheetBinding.inflate(activity.layoutInflater, null, false)
private val binding = ReaderColorFilterSettingsBinding.inflate(LayoutInflater.from(context), this, false)
init {
setContentView(binding.root)
sheetBehavior = BottomSheetBehavior.from(binding.root.parent as ViewGroup)
addView(binding.root)
preferences.colorFilter().asFlow()
.onEach { setColorFilter(it) }
.launchIn(activity.lifecycleScope)
.launchIn((context as ReaderActivity).lifecycleScope)
preferences.colorFilterMode().asFlow()
.onEach { setColorFilter(preferences.colorFilter().get()) }
.launchIn(activity.lifecycleScope)
.launchIn(context.lifecycleScope)
preferences.customBrightness().asFlow()
.onEach { setCustomBrightness(it) }
.launchIn(activity.lifecycleScope)
.launchIn(context.lifecycleScope)
// Get color and update values
val color = preferences.colorFilterValue().get()
@ -131,12 +129,6 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BaseBottomS
)
}
override fun onStart() {
super.onStart()
sheetBehavior?.skipCollapsed = true
sheetBehavior?.state = BottomSheetBehavior.STATE_EXPANDED
}
/**
* Set enabled status of seekBars belonging to color filter
* @param enabled determines if seekBar gets enabled
@ -184,7 +176,7 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BaseBottomS
preferences.customBrightnessValue().asFlow()
.sample(100)
.onEach { setCustomBrightnessValue(it) }
.launchIn(activity.lifecycleScope)
.launchIn((context as ReaderActivity).lifecycleScope)
} else {
setCustomBrightnessValue(0, true)
}
@ -212,7 +204,7 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BaseBottomS
preferences.colorFilterValue().asFlow()
.sample(100)
.onEach { setColorFilterValue(it) }
.launchIn(activity.lifecycleScope)
.launchIn((context as ReaderActivity).lifecycleScope)
}
setColorFilterSeekBar(enabled)
}

View File

@ -0,0 +1,167 @@
package eu.kanade.tachiyomi.ui.reader.setting
import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.CompoundButton
import android.widget.Spinner
import androidx.annotation.ArrayRes
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.lifecycleScope
import com.tfcporciuncula.flow.Preference
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.databinding.ReaderGeneralSettingsBinding
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import kotlinx.coroutines.flow.launchIn
import uy.kohesive.injekt.injectLazy
/**
* Sheet to show reader and viewer preferences.
*/
class ReaderGeneralSettings @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
NestedScrollView(context, attrs) {
private val preferences: PreferencesHelper by injectLazy()
private val binding = ReaderGeneralSettingsBinding.inflate(LayoutInflater.from(context), this, false)
init {
addView(binding.root)
initGeneralPreferences()
when ((context as ReaderActivity).viewer) {
is PagerViewer -> initPagerPreferences()
is WebtoonViewer -> initWebtoonPreferences()
}
}
/**
* Init general reader preferences.
*/
private fun initGeneralPreferences() {
binding.viewer.onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
(context as ReaderActivity).presenter.setMangaViewer(position)
val mangaViewer = (context as ReaderActivity).presenter.getMangaViewer()
if (mangaViewer == ReadingModeType.WEBTOON.prefValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.prefValue) {
initWebtoonPreferences()
} else {
initPagerPreferences()
}
}
binding.viewer.setSelection((context as ReaderActivity).presenter.manga?.viewer ?: 0, false)
binding.rotationMode.bindToPreference(preferences.rotation(), 1)
binding.backgroundColor.bindToIntPreference(preferences.readerTheme(), R.array.reader_themes_values)
binding.showPageNumber.bindToPreference(preferences.showPageNumber())
binding.fullscreen.bindToPreference(preferences.fullscreen())
binding.keepscreen.bindToPreference(preferences.keepScreenOn())
binding.longTap.bindToPreference(preferences.readWithLongTap())
binding.alwaysShowChapterTransition.bindToPreference(preferences.alwaysShowChapterTransition())
binding.pageTransitions.bindToPreference(preferences.pageTransitions())
// If the preference is explicitly disabled, that means the setting was configured since there is a cutout
if ((context as ReaderActivity).hasCutout || !preferences.cutoutShort().get()) {
binding.cutoutShort.isVisible = true
binding.cutoutShort.bindToPreference(preferences.cutoutShort())
}
}
/**
* Init the preferences for the pager reader.
*/
private fun initPagerPreferences() {
binding.webtoonPrefsGroup.root.isVisible = false
binding.pagerPrefsGroup.root.isVisible = true
binding.pagerPrefsGroup.tappingPrefsGroup.isVisible = preferences.readWithTapping().get()
binding.pagerPrefsGroup.tappingInverted.bindToPreference(preferences.pagerNavInverted())
binding.pagerPrefsGroup.pagerNav.bindToPreference(preferences.navigationModePager())
binding.pagerPrefsGroup.scaleType.bindToPreference(preferences.imageScaleType(), 1)
binding.pagerPrefsGroup.zoomStart.bindToPreference(preferences.zoomStart(), 1)
binding.pagerPrefsGroup.cropBorders.bindToPreference(preferences.cropBorders())
// Makes so that dual page invert gets hidden away when turning of dual page split
binding.dualPageSplit.bindToPreference(preferences.dualPageSplitPaged())
preferences.dualPageSplitPaged()
.asImmediateFlow { binding.dualPageInvert.isVisible = it }
.launchIn((context as ReaderActivity).lifecycleScope)
binding.dualPageInvert.bindToPreference(preferences.dualPageInvertPaged())
}
/**
* Init the preferences for the webtoon reader.
*/
private fun initWebtoonPreferences() {
binding.pagerPrefsGroup.root.isVisible = false
binding.webtoonPrefsGroup.root.isVisible = true
binding.webtoonPrefsGroup.tappingPrefsGroup.isVisible = preferences.readWithTapping().get()
binding.webtoonPrefsGroup.tappingInverted.bindToPreference(preferences.webtoonNavInverted())
binding.webtoonPrefsGroup.webtoonNav.bindToPreference(preferences.navigationModeWebtoon())
binding.webtoonPrefsGroup.cropBordersWebtoon.bindToPreference(preferences.cropBordersWebtoon())
binding.webtoonPrefsGroup.webtoonSidePadding.bindToIntPreference(preferences.webtoonSidePadding(), R.array.webtoon_side_padding_values)
// Makes so that dual page invert gets hidden away when turning of dual page split
binding.dualPageSplit.bindToPreference(preferences.dualPageSplitWebtoon())
preferences.dualPageSplitWebtoon()
.asImmediateFlow { binding.dualPageInvert.isVisible = it }
.launchIn((context as ReaderActivity).lifecycleScope)
binding.dualPageInvert.bindToPreference(preferences.dualPageInvertWebtoon())
}
/**
* Binds a checkbox or switch view with a boolean preference.
*/
private fun CompoundButton.bindToPreference(pref: Preference<Boolean>) {
isChecked = pref.get()
setOnCheckedChangeListener { _, isChecked -> pref.set(isChecked) }
}
/**
* Binds a spinner to an int preference with an optional offset for the value.
*/
private fun Spinner.bindToPreference(pref: Preference<Int>, offset: Int = 0) {
onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
pref.set(position + offset)
}
setSelection(pref.get() - offset, false)
}
/**
* Binds a spinner to an enum preference.
*/
private inline fun <reified T : Enum<T>> Spinner.bindToPreference(pref: Preference<T>) {
val enumConstants = T::class.java.enumConstants
onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
enumConstants?.get(position)?.let { pref.set(it) }
}
enumConstants?.indexOf(pref.get())?.let { setSelection(it, false) }
}
/**
* Binds a spinner to an int preference. The position of the spinner item must
* correlate with the [intValues] resource item (in arrays.xml), which is a <string-array>
* of int values that will be parsed here and applied to the preference.
*/
private fun Spinner.bindToIntPreference(pref: Preference<Int>, @ArrayRes intValuesResource: Int) {
val intValues = resources.getStringArray(intValuesResource).map { it.toIntOrNull() }
onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
pref.set(intValues[position]!!)
}
setSelection(intValues.indexOf(pref.get()), false)
}
}

View File

@ -1,174 +1,36 @@
package eu.kanade.tachiyomi.ui.reader.setting
import android.os.Bundle
import android.widget.CompoundButton
import android.widget.Spinner
import androidx.annotation.ArrayRes
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.lifecycleScope
import com.tfcporciuncula.flow.Preference
import com.google.android.material.tabs.TabLayout
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import eu.kanade.tachiyomi.databinding.ReaderSettingsSheetBinding
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
import kotlinx.coroutines.flow.launchIn
import uy.kohesive.injekt.injectLazy
import eu.kanade.tachiyomi.widget.SimpleTabSelectedListener
import eu.kanade.tachiyomi.widget.sheet.TabbedBottomSheetDialog
/**
* Sheet to show reader and viewer preferences.
*/
class ReaderSettingsSheet(private val activity: ReaderActivity) : BaseBottomSheetDialog(activity) {
class ReaderSettingsSheet(private val activity: ReaderActivity) : TabbedBottomSheetDialog(activity) {
private val preferences: PreferencesHelper by injectLazy()
private val generalSettings = ReaderGeneralSettings(activity)
private val colorFilterSettings = ReaderColorFilterSettings(activity)
private val binding = ReaderSettingsSheetBinding.inflate(activity.layoutInflater, null, false)
private val sheetBackgroundDim = window?.attributes?.dimAmount ?: 0.25f
init {
val scroll = NestedScrollView(activity)
scroll.addView(binding.root)
setContentView(scroll)
}
/**
* Called when the sheet is created. It initializes the listeners and values of the preferences.
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initGeneralPreferences()
when (activity.viewer) {
is PagerViewer -> initPagerPreferences()
is WebtoonViewer -> initWebtoonPreferences()
}
}
/**
* Init general reader preferences.
*/
private fun initGeneralPreferences() {
binding.viewer.onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
activity.presenter.setMangaViewer(position)
val mangaViewer = activity.presenter.getMangaViewer()
if (mangaViewer == ReadingModeType.WEBTOON.prefValue || mangaViewer == ReadingModeType.CONTINUOUS_VERTICAL.prefValue) {
initWebtoonPreferences()
} else {
initPagerPreferences()
binding.tabs.addOnTabSelectedListener(object : SimpleTabSelectedListener() {
// Remove dimmed backdrop so color filter changes can be previewed
override fun onTabSelected(tab: TabLayout.Tab?) {
val isFilterTab = tab?.position == 1
window?.setDimAmount(if (isFilterTab) 0f else sheetBackgroundDim)
activity.setMenuVisibility(!isFilterTab)
}
}
binding.viewer.setSelection(activity.presenter.manga?.viewer ?: 0, false)
binding.rotationMode.bindToPreference(preferences.rotation(), 1)
binding.backgroundColor.bindToIntPreference(preferences.readerTheme(), R.array.reader_themes_values)
binding.showPageNumber.bindToPreference(preferences.showPageNumber())
binding.fullscreen.bindToPreference(preferences.fullscreen())
binding.keepscreen.bindToPreference(preferences.keepScreenOn())
binding.longTap.bindToPreference(preferences.readWithLongTap())
binding.alwaysShowChapterTransition.bindToPreference(preferences.alwaysShowChapterTransition())
binding.pageTransitions.bindToPreference(preferences.pageTransitions())
// If the preference is explicitly disabled, that means the setting was configured since there is a cutout
if (activity.hasCutout || !preferences.cutoutShort().get()) {
binding.cutoutShort.isVisible = true
binding.cutoutShort.bindToPreference(preferences.cutoutShort())
}
})
}
/**
* Init the preferences for the pager reader.
*/
private fun initPagerPreferences() {
binding.webtoonPrefsGroup.root.isVisible = false
binding.pagerPrefsGroup.root.isVisible = true
override fun getTabViews() = listOf(
generalSettings,
colorFilterSettings,
)
binding.pagerPrefsGroup.tappingPrefsGroup.isVisible = preferences.readWithTapping().get()
binding.pagerPrefsGroup.tappingInverted.bindToPreference(preferences.pagerNavInverted())
binding.pagerPrefsGroup.pagerNav.bindToPreference(preferences.navigationModePager())
binding.pagerPrefsGroup.scaleType.bindToPreference(preferences.imageScaleType(), 1)
binding.pagerPrefsGroup.zoomStart.bindToPreference(preferences.zoomStart(), 1)
binding.pagerPrefsGroup.cropBorders.bindToPreference(preferences.cropBorders())
// Makes so that dual page invert gets hidden away when turning of dual page split
binding.dualPageSplit.bindToPreference(preferences.dualPageSplitPaged())
preferences.dualPageSplitPaged()
.asImmediateFlow { binding.dualPageInvert.isVisible = it }
.launchIn(activity.lifecycleScope)
binding.dualPageInvert.bindToPreference(preferences.dualPageInvertPaged())
}
/**
* Init the preferences for the webtoon reader.
*/
private fun initWebtoonPreferences() {
binding.pagerPrefsGroup.root.isVisible = false
binding.webtoonPrefsGroup.root.isVisible = true
binding.webtoonPrefsGroup.tappingPrefsGroup.isVisible = preferences.readWithTapping().get()
binding.webtoonPrefsGroup.tappingInverted.bindToPreference(preferences.webtoonNavInverted())
binding.webtoonPrefsGroup.webtoonNav.bindToPreference(preferences.navigationModeWebtoon())
binding.webtoonPrefsGroup.cropBordersWebtoon.bindToPreference(preferences.cropBordersWebtoon())
binding.webtoonPrefsGroup.webtoonSidePadding.bindToIntPreference(preferences.webtoonSidePadding(), R.array.webtoon_side_padding_values)
// Makes so that dual page invert gets hidden away when turning of dual page split
binding.dualPageSplit.bindToPreference(preferences.dualPageSplitWebtoon())
preferences.dualPageSplitWebtoon()
.asImmediateFlow { binding.dualPageInvert.isVisible = it }
.launchIn(activity.lifecycleScope)
binding.dualPageInvert.bindToPreference(preferences.dualPageInvertWebtoon())
}
/**
* Binds a checkbox or switch view with a boolean preference.
*/
private fun CompoundButton.bindToPreference(pref: Preference<Boolean>) {
isChecked = pref.get()
setOnCheckedChangeListener { _, isChecked -> pref.set(isChecked) }
}
/**
* Binds a spinner to an int preference with an optional offset for the value.
*/
private fun Spinner.bindToPreference(pref: Preference<Int>, offset: Int = 0) {
onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
pref.set(position + offset)
}
setSelection(pref.get() - offset, false)
}
/**
* Binds a spinner to an enum preference.
*/
private inline fun <reified T : Enum<T>> Spinner.bindToPreference(pref: Preference<T>) {
val enumConstants = T::class.java.enumConstants
onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
enumConstants?.get(position)?.let { pref.set(it) }
}
enumConstants?.indexOf(pref.get())?.let { setSelection(it, false) }
}
/**
* Binds a spinner to an int preference. The position of the spinner item must
* correlate with the [intValues] resource item (in arrays.xml), which is a <string-array>
* of int values that will be parsed here and applied to the preference.
*/
private fun Spinner.bindToIntPreference(pref: Preference<Int>, @ArrayRes intValuesResource: Int) {
val intValues = resources.getStringArray(intValuesResource).map { it.toIntOrNull() }
onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
pref.set(intValues[position]!!)
}
setSelection(intValues.indexOf(pref.get()), false)
}
override fun getTabTitles() = listOf(
R.string.action_settings,
R.string.custom_filter,
)
}

View File

@ -0,0 +1,14 @@
package eu.kanade.tachiyomi.widget
import com.google.android.material.tabs.TabLayout
open class SimpleTabSelectedListener : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabReselected(tab: TabLayout.Tab?) {
}
}

View File

@ -1,14 +1,14 @@
package eu.kanade.tachiyomi.widget.sheet
import android.app.Activity
import android.view.View
import android.view.ViewGroup
import com.bluelinelabs.conductor.Router
import eu.kanade.tachiyomi.databinding.CommonTabbedSheetBinding
import eu.kanade.tachiyomi.widget.ViewPagerAdapter
abstract class TabbedBottomSheetDialog(private val router: Router) : BaseBottomSheetDialog(router.activity!!) {
abstract class TabbedBottomSheetDialog(private val activity: Activity) : BaseBottomSheetDialog(activity) {
val binding: CommonTabbedSheetBinding = CommonTabbedSheetBinding.inflate(router.activity!!.layoutInflater)
val binding: CommonTabbedSheetBinding = CommonTabbedSheetBinding.inflate(activity.layoutInflater)
init {
val adapter = LibrarySettingsSheetAdapter()
@ -34,7 +34,7 @@ abstract class TabbedBottomSheetDialog(private val router: Router) : BaseBottomS
}
override fun getPageTitle(position: Int): CharSequence {
return router.activity!!.resources!!.getString(getTabTitles()[position])
return activity.resources!!.getString(getTabTitles()[position])
}
}
}