Reworking Reader Activity Menus

Reader settings sheet now in a tabbed layout
Reader settings sheet also contains filter options
Reader menu now holds the long tap options (Share, Save, Set as cover)
Reader menu also has option to go to manga details (was on toolbar tap)
Also has the option to jump straight to reader settings
Removing long tap gesture on Reader since no longer needed
This commit is contained in:
Jays2Kings 2021-03-24 17:10:31 -04:00
parent 6b06833fee
commit fe2543b9d5
35 changed files with 1082 additions and 1076 deletions

View File

@ -49,8 +49,6 @@ object PreferenceKeys {
const val readWithTapping = "reader_tap" const val readWithTapping = "reader_tap"
const val readWithLongTap = "reader_long_tap"
const val readWithVolumeKeys = "reader_volume_keys" const val readWithVolumeKeys = "reader_volume_keys"
const val readWithVolumeKeysInverted = "reader_volume_keys_inverted" const val readWithVolumeKeysInverted = "reader_volume_keys_inverted"

View File

@ -126,8 +126,6 @@ class PreferencesHelper(val context: Context) {
fun readWithTapping() = flowPrefs.getBoolean(Keys.readWithTapping, true) fun readWithTapping() = flowPrefs.getBoolean(Keys.readWithTapping, true)
fun readWithLongTap() = flowPrefs.getBoolean(Keys.readWithLongTap, true)
fun readWithVolumeKeys() = flowPrefs.getBoolean(Keys.readWithVolumeKeys, false) fun readWithVolumeKeys() = flowPrefs.getBoolean(Keys.readWithVolumeKeys, false)
fun readWithVolumeKeysInverted() = flowPrefs.getBoolean(Keys.readWithVolumeKeysInverted, false) fun readWithVolumeKeysInverted() = flowPrefs.getBoolean(Keys.readWithVolumeKeysInverted, false)

View File

@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.library.display
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import eu.kanade.tachiyomi.ui.library.LibraryController import eu.kanade.tachiyomi.util.bindToPreference
import kotlinx.android.synthetic.main.library_badges_layout.view.* import kotlinx.android.synthetic.main.library_badges_layout.view.*
class LibraryBadgesView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class LibraryBadgesView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.library.display
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import eu.kanade.tachiyomi.util.bindToPreference
import kotlinx.android.synthetic.main.library_category_layout.view.* import kotlinx.android.synthetic.main.library_category_layout.view.*
class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.library.display
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import eu.kanade.tachiyomi.util.bindToPreference
import kotlinx.android.synthetic.main.library_display_layout.view.* import kotlinx.android.synthetic.main.library_display_layout.view.*
class LibraryDisplayView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : class LibraryDisplayView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :

View File

@ -25,42 +25,4 @@ abstract class LibraryPreferenceView @JvmOverloads constructor(context: Context,
super.onFinishInflate() super.onFinishInflate()
initGeneralPreferences() initGeneralPreferences()
} }
/**
* Binds a checkbox or switch view with a boolean preference.
*/
internal fun CompoundButton.bindToPreference(pref: Preference<Boolean>, block: (() -> Unit)? = null) {
isChecked = pref.getOrDefault()
setOnCheckedChangeListener { _, isChecked ->
pref.set(isChecked)
block?.invoke()
}
}
/**
* Binds a checkbox or switch view with a boolean preference.
*/
internal fun CompoundButton.bindToPreference(
pref: com.tfcporciuncula.flow
.Preference<Boolean>,
block: ((Boolean) -> Unit)? = null
) {
isChecked = pref.get()
setOnCheckedChangeListener { _, isChecked ->
pref.set(isChecked)
block?.invoke(isChecked)
}
}
/**
* Binds a radio group with a int preference.
*/
internal fun RadioGroup.bindToPreference(pref: Preference<Int>, block: (() -> Unit)? = null) {
(getChildAt(pref.getOrDefault()) as RadioButton).isChecked = true
setOnCheckedChangeListener { _, checkedId ->
val index = indexOfChild(findViewById(checkedId))
pref.set(index)
block?.invoke()
}
}
} }

View File

@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog
import kotlinx.android.synthetic.main.tabbed_bottom_sheet.* import kotlinx.android.synthetic.main.tabbed_bottom_sheet.*
open class TabbedLibraryDisplaySheet(controller: LibraryController): open class TabbedLibraryDisplaySheet(controller: LibraryController):
TabbedBottomSheetDialog(controller) { TabbedBottomSheetDialog(controller.activity!!) {
private val displayView: LibraryDisplayView = inflate(controller.activity!!, R.layout.library_display_layout, null) as LibraryDisplayView private val displayView: LibraryDisplayView = inflate(controller.activity!!, R.layout.library_display_layout, null) as LibraryDisplayView
private val badgesView: LibraryBadgesView = inflate(controller.activity!!, R.layout.library_badges_layout, null) as LibraryBadgesView private val badgesView: LibraryBadgesView = inflate(controller.activity!!, R.layout.library_badges_layout, null) as LibraryBadgesView

View File

@ -693,6 +693,7 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
const val SHORTCUT_BROWSE = "eu.kanade.tachiyomi.SHOW_BROWSE" const val SHORTCUT_BROWSE = "eu.kanade.tachiyomi.SHOW_BROWSE"
const val SHORTCUT_DOWNLOADS = "eu.kanade.tachiyomi.SHOW_DOWNLOADS" const val SHORTCUT_DOWNLOADS = "eu.kanade.tachiyomi.SHOW_DOWNLOADS"
const val SHORTCUT_MANGA = "eu.kanade.tachiyomi.SHOW_MANGA" const val SHORTCUT_MANGA = "eu.kanade.tachiyomi.SHOW_MANGA"
const val SHORTCUT_READER_SETTINGS = "eu.kanade.tachiyomi.READER_SETTINGS"
const val SHORTCUT_EXTENSIONS = "eu.kanade.tachiyomi.EXTENSIONS" const val SHORTCUT_EXTENSIONS = "eu.kanade.tachiyomi.EXTENSIONS"
const val INTENT_SEARCH = "eu.kanade.tachiyomi.SEARCH" const val INTENT_SEARCH = "eu.kanade.tachiyomi.SEARCH"

View File

@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.notification.NotificationReceiver
import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
import eu.kanade.tachiyomi.ui.setting.SettingsReaderController
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
import eu.kanade.tachiyomi.util.view.gone import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.withFadeTransaction import eu.kanade.tachiyomi.util.view.withFadeTransaction
@ -35,7 +36,7 @@ class SearchActivity : MainActivity() {
} }
private fun popToRoot() { private fun popToRoot() {
if (intent.action == SHORTCUT_MANGA) { if (intent.action == SHORTCUT_MANGA || intent.action == SHORTCUT_READER_SETTINGS) {
onBackPressed() onBackPressed()
} else if (!router.handleBack()) { } else if (!router.handleBack()) {
val intent = Intent(this, MainActivity::class.java).apply { val intent = Intent(this, MainActivity::class.java).apply {
@ -97,6 +98,13 @@ class SearchActivity : MainActivity() {
.popChangeHandler(FadeChangeHandler()) .popChangeHandler(FadeChangeHandler())
) )
} }
SHORTCUT_READER_SETTINGS -> {
router.replaceTopController(
RouterTransaction.with(SettingsReaderController())
.pushChangeHandler(SimpleSwapChangeHandler())
.popChangeHandler(FadeChangeHandler())
)
}
else -> return false else -> return false
} }
return true return true
@ -112,5 +120,14 @@ class SearchActivity : MainActivity() {
action = SHORTCUT_MANGA action = SHORTCUT_MANGA
putExtra(MangaDetailsController.MANGA_EXTRA, id) putExtra(MangaDetailsController.MANGA_EXTRA, id)
} }
fun openReaderSettings(context: Context) = Intent(
context,
SearchActivity::class
.java
)
.apply {
action = SHORTCUT_READER_SETTINGS
}
} }
} }

View File

@ -43,6 +43,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Success
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter 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.settings.TabbedReaderSettingsSheet
import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.L2RPagerViewer import eu.kanade.tachiyomi.ui.reader.viewer.pager.L2RPagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
@ -279,20 +280,45 @@ class ReaderActivity :
return true return true
} }
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
val detailsItem = menu?.findItem(R.id.action_manga_details)
if (presenter.manga?.mangaType(this) != null) {
detailsItem?.title = getString(R.string._details,
presenter.manga?.mangaType(this)?.capitalize(Locale.ROOT) ?: "")
} else {
detailsItem?.title = getString(R.string.details)
}
return super.onPrepareOptionsMenu(menu)
}
/** /**
* Called when an item of the options menu was clicked. Used to handle clicks on our menu * Called when an item of the options menu was clicked. Used to handle clicks on our menu
* entries. * entries.
*/ */
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
coroutine?.cancel() coroutine?.cancel()
bottomSheet = when (item.itemId) { when (item.itemId) {
R.id.action_settings -> ReaderSettingsSheet(this) R.id.action_display_settings -> TabbedReaderSettingsSheet(this).show()
R.id.action_custom_filter -> ReaderColorFilterSheet(this) R.id.action_manga_details -> {
else -> return super.onOptionsItemSelected(item) presenter.manga?.id?.let { id ->
val intent = SearchActivity.openMangaIntent(this, id)
startActivity(intent)
} }
bottomSheet?.show() }
if (chapters_bottom_sheet.sheetBehavior.isExpanded()) { R.id.action_share_page, R.id.action_set_page_as_cover, R.id.action_save_page -> {
chapters_bottom_sheet.sheetBehavior?.collapse() val currentChapter = presenter.getCurrentChapter() ?: return true
val page = currentChapter.pages?.getOrNull(page_seekbar.progress) ?: return true
when (item.itemId) {
R.id.action_share_page -> shareImage(page)
R.id.action_set_page_as_cover -> showSetCoverPrompt(page)
R.id.action_save_page -> saveImage(page)
}
}
R.id.action_reader_settings -> {
val intent = SearchActivity.openReaderSettings(this)
startActivity(intent)
}
else -> return super.onOptionsItemSelected(item)
} }
return true return true
} }
@ -357,13 +383,6 @@ class ReaderActivity :
popToMain() popToMain()
} }
toolbar.setOnClickListener {
presenter.manga?.id?.let { id ->
val intent = SearchActivity.openMangaIntent(this, id)
startActivity(intent)
}
}
// Init listeners on bottom menu // Init listeners on bottom menu
page_seekbar.setOnSeekBarChangeListener( page_seekbar.setOnSeekBarChangeListener(
object : SimpleSeekBarListener() { object : SimpleSeekBarListener() {
@ -477,6 +496,7 @@ class ReaderActivity :
val prevViewer = viewer val prevViewer = viewer
val noDefault = manga.viewer == -1 val noDefault = manga.viewer == -1
val mangaViewer = presenter.getMangaViewer() val mangaViewer = presenter.getMangaViewer()
invalidateOptionsMenu()
val newViewer = when (mangaViewer) { val newViewer = when (mangaViewer) {
RIGHT_TO_LEFT -> R2LPagerViewer(this) RIGHT_TO_LEFT -> R2LPagerViewer(this)
VERTICAL -> VerticalPagerViewer(this) VERTICAL -> VerticalPagerViewer(this)
@ -615,41 +635,6 @@ class ReaderActivity :
page_seekbar.progress = page.index page_seekbar.progress = page.index
} }
/**
* Called from the viewer whenever a [page] is long clicked. A bottom sheet with a list of
* actions to perform is shown.
*/
fun onPageLongTap(page: ReaderPage) {
val items = listOf(
MaterialMenuSheet.MenuSheetItem(
0,
R.drawable.ic_photo_24dp,
R.string.set_as_cover
),
MaterialMenuSheet.MenuSheetItem(
1,
R.drawable.ic_share_24dp,
R.string.share
),
MaterialMenuSheet.MenuSheetItem(
2,
R.drawable.ic_save_24dp,
R.string.save
)
)
MaterialMenuSheet(this, items) { _, item ->
when (item) {
0 -> showSetCoverPrompt(page)
1 -> shareImage(page)
2 -> saveImage(page)
}
true
}.show()
if (chapters_bottom_sheet.sheetBehavior.isExpanded()) {
chapters_bottom_sheet.sheetBehavior?.collapse()
}
}
/** /**
* Called from the viewer when the given [chapter] should be preloaded. It should be called when * Called from the viewer when the given [chapter] should be preloaded. It should be called when
* the viewer is reaching the beginning or end of a chapter or the transition page is active. * the viewer is reaching the beginning or end of a chapter or the transition page is active.

View File

@ -1,194 +0,0 @@
package eu.kanade.tachiyomi.ui.reader
import android.content.res.Configuration
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import android.widget.Spinner
import androidx.annotation.ArrayRes
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tfcporciuncula.flow.Preference
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerViewer
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.hasSideNavBar
import eu.kanade.tachiyomi.util.system.isInNightMode
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.setBottomEdge
import eu.kanade.tachiyomi.util.view.setEdgeToEdge
import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.util.view.visibleIf
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import kotlinx.android.synthetic.main.reader_settings_sheet.*
import uy.kohesive.injekt.injectLazy
import kotlin.math.max
/**
* Sheet to show reader and viewer preferences.
*/
class ReaderSettingsSheet(private val activity: ReaderActivity) :
BottomSheetDialog(activity, R.style.BottomSheetDialogTheme) {
/**
* Preferences helper.
*/
private val preferences by injectLazy<PreferencesHelper>()
private var sheetBehavior: BottomSheetBehavior<*>
init {
// Use activity theme for this layout
val view = activity.layoutInflater.inflate(R.layout.reader_settings_sheet, null)
setContentView(view)
sheetBehavior = BottomSheetBehavior.from(view.parent as ViewGroup)
setEdgeToEdge(
activity,
view,
if (context.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE)
0 else -1
)
window?.navigationBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
!context.isInNightMode() &&
!activity.window.decorView.rootWindowInsets.hasSideNavBar()
) {
window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
}
val height = activity.window.decorView.rootWindowInsets.systemWindowInsetBottom
sheetBehavior.peekHeight = 550.dpToPx + height
sheetBehavior.addBottomSheetCallback(
object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, progress: Float) {
if (progress.isNaN())
pill.alpha = 0f
else
pill.alpha = (1 - max(0f, progress)) * 0.25f
}
override fun onStateChanged(p0: View, state: Int) {
if (state == BottomSheetBehavior.STATE_EXPANDED) {
sheetBehavior.skipCollapsed = true
}
}
}
)
}
/**
* 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 (val view = activity.viewer) {
is PagerViewer -> initPagerPreferences()
is WebtoonViewer -> initWebtoonPreferences(view.hasMargins)
}
setBottomEdge(constraint_layout, activity)
close_button.setOnClickListener {
dismiss()
}
settings_scroll_view.viewTreeObserver.addOnGlobalLayoutListener {
val isScrollable =
settings_scroll_view.height < constraint_layout.height +
settings_scroll_view.paddingTop + settings_scroll_view.paddingBottom
close_button.visibleIf(isScrollable)
pill.visibleIf(!isScrollable)
}
}
/**
* Init general reader preferences.
*/
private fun initGeneralPreferences() {
viewer.onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
activity.presenter.setMangaViewer(position)
val mangaViewer = activity.presenter.getMangaViewer()
if (mangaViewer == ReaderActivity.WEBTOON || mangaViewer == ReaderActivity.VERTICAL_PLUS) {
initWebtoonPreferences(mangaViewer == ReaderActivity.VERTICAL_PLUS)
} else {
initPagerPreferences()
}
}
viewer.setSelection(activity.presenter.manga?.viewer ?: 0, false)
rotation_mode.bindToPreference(preferences.rotation(), 1)
background_color.bindToPreference(preferences.readerTheme(), 0)
show_page_number.bindToPreference(preferences.showPageNumber())
fullscreen.bindToPreference(preferences.fullscreen())
keepscreen.bindToPreference(preferences.keepScreenOn())
always_show_chapter_transition.bindToPreference(preferences.alwaysShowChapterTransition())
}
/**
* Init the preferences for the pager reader.
*/
private fun initPagerPreferences() {
pager_prefs_group.visible()
webtoon_prefs_group.gone()
scale_type.bindToPreference(preferences.imageScaleType(), 1)
zoom_start.bindToPreference(preferences.zoomStart(), 1)
crop_borders.bindToPreference(preferences.cropBorders())
page_transitions.bindToPreference(preferences.pageTransitions())
}
/**
* Init the preferences for the webtoon reader.
*/
private fun initWebtoonPreferences(hasMargins: Boolean) {
webtoon_prefs_group.visible()
pager_prefs_group.gone()
crop_borders_webtoon.bindToPreference(if (hasMargins) preferences.cropBorders() else preferences.cropBordersWebtoon())
webtoon_side_padding.bindToIntPreference(preferences.webtoonSidePadding(), R.array.webtoon_side_padding_values)
webtoon_enable_zoom_out.bindToPreference(preferences.webtoonEnableZoomOut())
}
/**
* Binds a checkbox or switch view with a boolean preference.
*/
private fun CompoundButton.bindToPreference(pref: Preference<Boolean>) {
setOnCheckedChangeListener(null)
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 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] ?: 0)
}
setSelection(intValues.indexOf(pref.get()), false)
}
}

View File

@ -0,0 +1,25 @@
package eu.kanade.tachiyomi.ui.reader.settings
import android.content.Context
import android.util.AttributeSet
import android.widget.FrameLayout
import android.widget.LinearLayout
import com.google.android.material.tabs.TabLayout
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.library.LibraryController
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import uy.kohesive.injekt.injectLazy
abstract class BaseReaderSettingsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
FrameLayout(context, attrs) {
internal val preferences by injectLazy<PreferencesHelper>()
lateinit var activity: ReaderActivity
abstract fun initGeneralPreferences()
override fun onFinishInflate() {
super.onFinishInflate()
initGeneralPreferences()
}
}

View File

@ -1,74 +1,44 @@
package eu.kanade.tachiyomi.ui.reader package eu.kanade.tachiyomi.ui.reader.settings
import android.graphics.Color import android.content.Context
import android.os.Build import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar import android.widget.SeekBar
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import com.google.android.material.bottomsheet.BottomSheetBehavior import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import com.google.android.material.bottomsheet.BottomSheetDialog import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.system.hasSideNavBar
import eu.kanade.tachiyomi.util.system.isInNightMode
import eu.kanade.tachiyomi.util.view.expand
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.setBottomEdge
import eu.kanade.tachiyomi.util.view.setEdgeToEdge
import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import eu.kanade.tachiyomi.widget.SimpleSeekBarListener import eu.kanade.tachiyomi.widget.SimpleSeekBarListener
import kotlinx.android.synthetic.main.reader_color_filter.* import kotlinx.android.synthetic.main.reader_color_filter.*
import kotlinx.android.synthetic.main.reader_color_filter_sheet.* import kotlinx.android.synthetic.main.reader_color_filter.view.*
import kotlinx.android.synthetic.main.reader_color_filter.view.settings_scroll_view
import kotlinx.android.synthetic.main.reader_general_layout.view.*
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample import kotlinx.coroutines.flow.sample
import uy.kohesive.injekt.injectLazy
import kotlin.math.abs
/** class ReaderFilterView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
* Color filter sheet to toggle custom filter and brightness overlay. BaseReaderSettingsView(context, attrs) {
*/
class ReaderColorFilterSheet(private val activity: ReaderActivity) : BottomSheetDialog
(activity, R.style.BottomSheetDialogTheme) {
private val preferences by injectLazy<PreferencesHelper>()
private var sheetBehavior: BottomSheetBehavior<*>? = null
init {
val view = activity.layoutInflater.inflate(R.layout.reader_color_filter_sheet, null)
setContentView(view)
setEdgeToEdge(activity, view, 0)
window?.navigationBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
!context.isInNightMode() &&
!activity.window.decorView.rootWindowInsets.hasSideNavBar()
)
window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
setBottomEdge(brightness_seekbar, activity)
sheetBehavior = BottomSheetBehavior.from(view.parent as ViewGroup)
override fun initGeneralPreferences() {
activity = context as ReaderActivity
settings_scroll_view.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
preferences.colorFilter().asFlow() preferences.colorFilter().asFlow()
.onEach { setColorFilter(it, view) } .onEach { setColorFilter(it) }
.launchIn(activity.scope) .launchIn(activity.scope)
preferences.colorFilterMode().asFlow() preferences.colorFilterMode().asFlow()
.onEach { setColorFilter(preferences.colorFilter().get(), view) } .onEach { setColorFilter(preferences.colorFilter().get()) }
.launchIn(activity.scope) .launchIn(activity.scope)
preferences.customBrightness().asFlow() preferences.customBrightness().asFlow()
.onEach { setCustomBrightness(it, view) } .onEach { setCustomBrightness(it) }
.launchIn(activity.scope) .launchIn(activity.scope)
// Get color and update values // Get color and update values
val color = preferences.colorFilterValue().get() val color = preferences.colorFilterValue().get()
val brightness = preferences.customBrightnessValue().get() val brightness = preferences.customBrightnessValue().get()
val argb = setValues(color, view) val argb = setValues(color)
// Set brightness value // Set brightness value
txt_brightness_seekbar_value.text = brightness.toString() txt_brightness_seekbar_value.text = brightness.toString()
@ -91,14 +61,13 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BottomSheet
preferences.customBrightness().set(isChecked) preferences.customBrightness().set(isChecked)
} }
color_filter_mode.onItemSelectedListener = IgnoreFirstSpinnerListener { position -> color_filter_mode.bindToPreference(preferences.colorFilterMode())
preferences.colorFilterMode().set(position)
}
color_filter_mode.setSelection(preferences.colorFilterMode().get(), false)
seekbar_color_filter_alpha.setOnSeekBarChangeListener( seekbar_color_filter_alpha.setOnSeekBarChangeListener(
object : SimpleSeekBarListener() { object : SimpleSeekBarListener() {
override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) { override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) {
seekbar_color_filter_red.isEnabled = value > 0 && seekbar_color_filter_alpha.isEnabled
seekbar_color_filter_green.isEnabled = value > 0 && seekbar_color_filter_alpha.isEnabled
seekbar_color_filter_blue.isEnabled = value > 0 && seekbar_color_filter_alpha.isEnabled
if (fromUser) { if (fromUser) {
setColorValue(value, ALPHA_MASK, 24) setColorValue(value, ALPHA_MASK, 24)
} }
@ -147,39 +116,31 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BottomSheet
) )
} }
override fun onStart() {
super.onStart()
sheetBehavior?.skipCollapsed = true
sheetBehavior?.expand()
}
/** /**
* Set enabled status of seekBars belonging to color filter * Set enabled status of seekBars belonging to color filter
* @param enabled determines if seekBar gets enabled * @param enabled determines if seekBar gets enabled
* @param view view of the dialog
*/ */
private fun setColorFilterSeekBar(enabled: Boolean, view: View) = with(view) { private fun setColorFilterSeekBar(enabled: Boolean) {
seekbar_color_filter_red.isEnabled = enabled seekbar_color_filter_red.isEnabled = seekbar_color_filter_alpha.progress > 0 && enabled
seekbar_color_filter_green.isEnabled = enabled seekbar_color_filter_green.isEnabled = seekbar_color_filter_alpha.progress > 0 && enabled
seekbar_color_filter_blue.isEnabled = enabled seekbar_color_filter_blue.isEnabled = seekbar_color_filter_alpha.progress > 0 && enabled
seekbar_color_filter_alpha.isEnabled = enabled seekbar_color_filter_alpha.isEnabled = enabled
} }
/** /**
* Set enabled status of seekBars belonging to custom brightness * Set enabled status of seekBars belonging to custom brightness
* @param enabled value which determines if seekBar gets enabled * @param enabled value which determines if seekBar gets enabled
* @param view view of the dialog
*/ */
private fun setCustomBrightnessSeekBar(enabled: Boolean, view: View) = with(view) { private fun setCustomBrightnessSeekBar(enabled: Boolean) {
brightness_seekbar.isEnabled = enabled brightness_seekbar.isEnabled = enabled
} }
/** /**
* Set the text value's of color filter * Set the text value's of color filter
* @param color integer containing color information * @param color integer containing color information
* @param view view of the dialog
*/ */
fun setValues(color: Int, view: View): Array<Int> { private fun setValues(color: Int): Array<Int> {
val alpha = getAlphaFromColor(color) val alpha = getAlphaFromColor(color)
val red = getRedFromColor(color) val red = getRedFromColor(color)
val green = getGreenFromColor(color) val green = getGreenFromColor(color)
@ -197,18 +158,17 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BottomSheet
/** /**
* Manages the custom brightness value subscription * Manages the custom brightness value subscription
* @param enabled determines if the subscription get (un)subscribed * @param enabled determines if the subscription get (un)subscribed
* @param view view of the dialog
*/ */
private fun setCustomBrightness(enabled: Boolean, view: View) { private fun setCustomBrightness(enabled: Boolean) {
if (enabled) { if (enabled) {
preferences.customBrightnessValue().asFlow() preferences.customBrightnessValue().asFlow()
.sample(100) .sample(100)
.onEach { setCustomBrightnessValue(it, view) } .onEach { setCustomBrightnessValue(it) }
.launchIn(activity.scope) .launchIn(activity.scope)
} else { } else {
setCustomBrightnessValue(0, view, true) setCustomBrightnessValue(0, true)
} }
setCustomBrightnessSeekBar(enabled, view) setCustomBrightnessSeekBar(enabled)
} }
/** /**
@ -217,16 +177,8 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BottomSheet
* From 1 to 100 it sets that value as brightness. * From 1 to 100 it sets that value as brightness.
* 0 sets system brightness and hides the overlay. * 0 sets system brightness and hides the overlay.
*/ */
private fun setCustomBrightnessValue(value: Int, view: View, isDisabled: Boolean = false) = with(view) { private fun setCustomBrightnessValue(value: Int, isDisabled: Boolean = false) {
// Set black overlay visibility. // Set black overlay visibility.
if (value < 0) {
brightness_overlay.visible()
val alpha = (abs(value) * 2.56).toInt()
brightness_overlay.setBackgroundColor(Color.argb(alpha, 0, 0, 0))
} else {
brightness_overlay.gone()
}
if (!isDisabled) { if (!isDisabled) {
txt_brightness_seekbar_value.text = value.toString() txt_brightness_seekbar_value.text = value.toString()
} }
@ -237,27 +189,22 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BottomSheet
* @param enabled determines if the subscription get (un)subscribed * @param enabled determines if the subscription get (un)subscribed
* @param view view of the dialog * @param view view of the dialog
*/ */
private fun setColorFilter(enabled: Boolean, view: View) { private fun setColorFilter(enabled: Boolean) {
if (enabled) { if (enabled) {
preferences.colorFilterValue().asFlow() preferences.colorFilterValue().asFlow()
.sample(100) .sample(100)
.onEach { setColorFilterValue(it, view) } .onEach { setColorFilterValue(it) }
.launchIn(activity.scope) .launchIn(activity.scope)
} else {
color_overlay.gone()
} }
setColorFilterSeekBar(enabled, view) setColorFilterSeekBar(enabled)
} }
/** /**
* Sets the color filter overlay of the screen. Determined by HEX of integer * Sets the color filter overlay of the screen. Determined by HEX of integer
* @param color hex of color. * @param color hex of color.
* @param view view of the dialog
*/ */
private fun setColorFilterValue(@ColorInt color: Int, view: View) = with(view) { private fun setColorFilterValue(@ColorInt color: Int) {
color_overlay.visible() setValues(color)
color_overlay.setFilterColor(color, preferences.colorFilterMode().get())
setValues(color, view)
} }
/** /**
@ -290,15 +237,6 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BottomSheet
return color shr 16 and 0xFF return color shr 16 and 0xFF
} }
/**
* Returns the green value from the Color Hex
* @param color color hex as int
* @return green of color
*/
fun getGreenFromColor(color: Int): Int {
return color shr 8 and 0xFF
}
/** /**
* Returns the blue value from the Color Hex * Returns the blue value from the Color Hex
* @param color color hex as int * @param color color hex as int
@ -322,3 +260,12 @@ class ReaderColorFilterSheet(private val activity: ReaderActivity) : BottomSheet
const val BLUE_MASK: Long = 0x000000FF const val BLUE_MASK: Long = 0x000000FF
} }
} }
/**
* Returns the green value from the Color Hex
* @param color color hex as int
* @return green of color
*/
fun ReaderFilterView.getGreenFromColor(color: Int): Int {
return color shr 8 and 0xFF
}

View File

@ -0,0 +1,46 @@
package eu.kanade.tachiyomi.ui.reader.settings
import android.content.Context
import android.util.AttributeSet
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.bindToPreference
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import kotlinx.android.synthetic.main.reader_general_layout.view.*
class ReaderGeneralView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
BaseReaderSettingsView(context, attrs) {
lateinit var sheet: TabbedReaderSettingsSheet
override fun initGeneralPreferences() {
settings_scroll_view.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
viewer_series.onItemSelectedListener = { position ->
activity.presenter.setMangaViewer(position)
val mangaViewer = activity.presenter.getMangaViewer()
if (mangaViewer == ReaderActivity.WEBTOON || mangaViewer == ReaderActivity.VERTICAL_PLUS) {
initWebtoonPreferences()
} else {
initPagerPreferences()
}
}
viewer_series.setSelection((context as? ReaderActivity)?.presenter?.manga?.viewer ?: 0)
rotation_mode.bindToPreference(preferences.rotation(), 1)
background_color.bindToPreference(preferences.readerTheme(), 0)
show_page_number.bindToPreference(preferences.showPageNumber())
fullscreen.bindToPreference(preferences.fullscreen())
keepscreen.bindToPreference(preferences.keepScreenOn())
always_show_chapter_transition.bindToPreference(preferences.alwaysShowChapterTransition())
}
/**
* Init the preferences for the webtoon reader.
*/
private fun initWebtoonPreferences() {
sheet.updateTabs(true)
}
private fun initPagerPreferences() {
sheet.updateTabs(false)
}
}

View File

@ -0,0 +1,49 @@
package eu.kanade.tachiyomi.ui.reader.settings
import android.content.Context
import android.util.AttributeSet
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.bindToIntPreference
import eu.kanade.tachiyomi.util.bindToPreference
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
import eu.kanade.tachiyomi.util.view.visibleIf
import kotlinx.android.synthetic.main.reader_paged_layout.view.*
import kotlinx.android.synthetic.main.reader_paged_layout.view.crop_borders_webtoon
import kotlinx.android.synthetic.main.reader_paged_layout.view.settings_scroll_view
import kotlinx.android.synthetic.main.reader_paged_layout.view.webtoon_enable_zoom_out
import kotlinx.android.synthetic.main.reader_paged_layout.view.webtoon_side_padding
class ReaderPagedView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
BaseReaderSettingsView(context, attrs) {
override fun initGeneralPreferences() {
scale_type.bindToPreference(preferences.imageScaleType(), 1)
zoom_start.bindToPreference(preferences.zoomStart(), 1)
crop_borders.bindToPreference(preferences.cropBorders())
page_transitions.bindToPreference(preferences.pageTransitions())
settings_scroll_view.setOnApplyWindowInsetsListener(RecyclerWindowInsetsListener)
val mangaViewer = (context as ReaderActivity).presenter.getMangaViewer()
val isWebtoonView = mangaViewer == ReaderActivity.WEBTOON || mangaViewer == ReaderActivity.VERTICAL_PLUS
val hasMargins = mangaViewer == ReaderActivity.VERTICAL_PLUS
crop_borders_webtoon.bindToPreference(if (hasMargins) preferences.cropBorders() else preferences.cropBordersWebtoon())
webtoon_side_padding.bindToIntPreference(preferences.webtoonSidePadding(), R.array.webtoon_side_padding_values)
webtoon_enable_zoom_out.bindToPreference(preferences.webtoonEnableZoomOut())
updatePagedGroup(!isWebtoonView)
}
fun updatePrefs() {
val mangaViewer = activity.presenter.getMangaViewer()
val isWebtoonView = mangaViewer == ReaderActivity.WEBTOON || mangaViewer == ReaderActivity.VERTICAL_PLUS
val hasMargins = mangaViewer == ReaderActivity.VERTICAL_PLUS
crop_borders_webtoon.bindToPreference(if (hasMargins) preferences.cropBorders() else preferences.cropBordersWebtoon())
updatePagedGroup(!isWebtoonView)
}
fun updatePagedGroup(show: Boolean) {
listOf(scale_type, zoom_start, crop_borders, page_transitions).forEach { it.visibleIf(show) }
listOf(crop_borders_webtoon, webtoon_side_padding, webtoon_enable_zoom_out).forEach { it.visibleIf(!show) }
}
}

View File

@ -0,0 +1,134 @@
package eu.kanade.tachiyomi.ui.reader.settings
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.MenuItem
import android.widget.FrameLayout
import androidx.annotation.ArrayRes
import androidx.appcompat.widget.PopupMenu
import androidx.core.view.get
import com.tfcporciuncula.flow.Preference
import eu.kanade.tachiyomi.R
import kotlinx.android.synthetic.main.reader_preference.view.*
class ReaderPreferenceView @JvmOverloads constructor(context: Context, attrs: AttributeSet?) :
FrameLayout(context, attrs) {
private var entries = emptyList<String>()
private var selectedPosition = 0
private var pref: Preference<Int>? = null
private var prefOffset = 0
var onItemSelectedListener: ((Int) -> Unit)? = null
set(value) {
field = value
if (value != null) {
val popup = makeSettingsPopup()
setOnTouchListener(popup.dragToOpenListener)
setOnClickListener {
popup.show()
}
}
}
init {
inflate(context, R.layout.reader_preference, this)
val a = context.obtainStyledAttributes(attrs, R.styleable.ReaderPreferenceView, 0, 0)
val str = a.getString(R.styleable.ReaderPreferenceView_title) ?: ""
title_view.text = str
val entries = (a.getTextArray(R.styleable.ReaderPreferenceView_android_entries) ?: emptyArray()).map { it.toString() }
this.entries = entries
detail_view.text = entries.firstOrNull().orEmpty()
a.recycle()
}
fun setSelection(selection: Int) {
selectedPosition = selection
detail_view.text = entries.getOrNull(selection).orEmpty()
}
fun bindToPreference(pref: Preference<Int>, offset: Int = 0, block: ((Int) -> Unit)? = null) {
setSelection(pref.get() - offset)
this.pref = pref
prefOffset = offset
val popup = makeSettingsPopup(pref, prefOffset, block)
setOnTouchListener(popup.dragToOpenListener)
setOnClickListener {
popup.show()
}
}
fun bindToIntPreference(pref: Preference<Int>, @ArrayRes intValuesResource: Int, block: ((Int) -> Unit)? = null) {
setSelection(pref.get())
this.pref = pref
prefOffset = 0
val intValues = resources.getStringArray(intValuesResource).map { it.toIntOrNull() }
val popup = makeSettingsPopup(pref, intValues, block)
setOnTouchListener(popup.dragToOpenListener)
setOnClickListener {
popup.show()
}
}
private fun makeSettingsPopup(preference: Preference<Int>, intValues: List<Int?>, block: ((Int) -> Unit)? = null): PopupMenu {
val popup = popup()
// Set a listener so we are notified if a menu item is clicked
popup.setOnMenuItemClickListener { menuItem ->
val pos = popup.menuClicked(menuItem)
preference.set(intValues[pos] ?: 0)
block?.invoke(pos)
true
}
return popup
}
private fun makeSettingsPopup(preference: Preference<Int>, offset: Int = 0, block: ((Int) -> Unit)? = null): PopupMenu {
val popup = popup()
// Set a listener so we are notified if a menu item is clicked
popup.setOnMenuItemClickListener { menuItem ->
val pos = popup.menuClicked(menuItem)
preference.set(pos + offset)
block?.invoke(pos)
true
}
return popup
}
private fun makeSettingsPopup(): PopupMenu {
val popup = popup()
// Set a listener so we are notified if a menu item is clicked
popup.setOnMenuItemClickListener { menuItem ->
val pos = popup.menuClicked(menuItem)
onItemSelectedListener?.invoke(pos)
true
}
return popup
}
private fun PopupMenu.menuClicked(menuItem: MenuItem): Int {
val pos = menuItem.itemId
menu[selectedPosition].isCheckable = false
menu[selectedPosition].isChecked = false
setSelection(pos)
menu[pos].isCheckable = true
menu[pos].isChecked = true
return pos
}
private fun popup(): PopupMenu {
val popup = PopupMenu(context, this, Gravity.END)
entries.forEachIndexed { index, entry ->
popup.menu.add(0, index, 0, entry)
}
popup.menu[selectedPosition].isCheckable = true
popup.menu[selectedPosition].isChecked = true
return popup
}
}

View File

@ -0,0 +1,99 @@
package eu.kanade.tachiyomi.ui.reader.settings
import android.view.View
import com.google.android.material.tabs.TabLayout
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.library.display.LibraryBadgesView
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.visInvisIf
import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.util.view.visibleIf
import eu.kanade.tachiyomi.widget.TabbedBottomSheetDialog
import kotlinx.android.synthetic.main.reader_activity.*
import kotlinx.android.synthetic.main.reader_color_filter.view.*
import kotlinx.android.synthetic.main.recycler_with_scroller.view.*
import kotlinx.android.synthetic.main.tabbed_bottom_sheet.*
class TabbedReaderSettingsSheet(val readerActivity: ReaderActivity): TabbedBottomSheetDialog(
readerActivity
) {
private val generalView: ReaderGeneralView = View.inflate(
readerActivity,
R.layout.reader_general_layout,
null
) as ReaderGeneralView
private val pagedView: ReaderPagedView = View.inflate(
readerActivity,
R.layout.reader_paged_layout,
null
) as ReaderPagedView
private val filterView: ReaderFilterView = View.inflate(
readerActivity,
R.layout.reader_color_filter,
null
) as ReaderFilterView
var showWebview: Boolean = {
val mangaViewer = readerActivity.presenter.getMangaViewer()
mangaViewer == ReaderActivity.WEBTOON || mangaViewer == ReaderActivity.VERTICAL_PLUS
}()
override var offset = 0
override fun getTabViews(): List<View> = listOf(
generalView,
pagedView,
filterView
)
override fun getTabTitles(): List<Int> = listOf(
R.string.general,
if (showWebview) R.string.webtoon else R.string.paged,
R.string.filter
)
init {
generalView.activity = readerActivity
pagedView.activity = readerActivity
filterView.activity = readerActivity
generalView.sheet = this
menu.gone()
val attrs = window?.attributes
val ogDim = attrs?.dimAmount ?: 0.25f
pager.adapter?.notifyDataSetChanged()
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
window?.setDimAmount(if (tab?.position == 2) 0f else ogDim)
val view = getTabViews()[tab?.position ?: 0]
view.settings_scroll_view?.isNestedScrollingEnabled = true
view.settings_scroll_view?.requestLayout()
readerActivity.appbar.visInvisIf(tab?.position != 2)
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
val view = getTabViews()[tab?.position ?: 0]
view.settings_scroll_view?.isNestedScrollingEnabled = false
view.settings_scroll_view?.requestLayout()
}
override fun onTabReselected(tab: TabLayout.Tab?) {
val view = getTabViews()[tab?.position ?: 0]
view.settings_scroll_view?.isNestedScrollingEnabled = true
view.settings_scroll_view?.requestLayout()
}
})
}
override fun dismiss() {
super.dismiss()
readerActivity.appbar.visible()
}
fun updateTabs(isWebtoon: Boolean) {
showWebview = isWebtoon
pager.adapter?.notifyDataSetChanged()
pagedView.updatePrefs()
}
}

View File

@ -18,7 +18,6 @@ abstract class ViewerConfig(preferences: PreferencesHelper) {
var imagePropertyChangedListener: (() -> Unit)? = null var imagePropertyChangedListener: (() -> Unit)? = null
var tappingEnabled = true var tappingEnabled = true
var longTapEnabled = true
var doubleTapAnimDuration = 500 var doubleTapAnimDuration = 500
var volumeKeysEnabled = false var volumeKeysEnabled = false
var volumeKeysInverted = false var volumeKeysInverted = false
@ -28,9 +27,6 @@ abstract class ViewerConfig(preferences: PreferencesHelper) {
preferences.readWithTapping() preferences.readWithTapping()
.register({ tappingEnabled = it }) .register({ tappingEnabled = it })
preferences.readWithLongTap()
.register({ longTapEnabled = it })
preferences.doubleTapAnimSpeed() preferences.doubleTapAnimSpeed()
.register({ doubleTapAnimDuration = it }) .register({ doubleTapAnimDuration = it })

View File

@ -89,16 +89,6 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer {
else -> activity.toggleMenu() else -> activity.toggleMenu()
} }
} }
pager.longTapListener = f@{
if (activity.menuVisible || config.longTapEnabled) {
val item = adapter.items.getOrNull(pager.currentItem)
if (item is ReaderPage) {
activity.onPageLongTap(item)
return@f true
}
}
false
}
config.imagePropertyChangedListener = { config.imagePropertyChangedListener = {
refreshAdapter() refreshAdapter()

View File

@ -106,20 +106,6 @@ class WebtoonViewer(val activity: ReaderActivity, val hasMargins: Boolean = fals
else -> activity.toggleMenu() else -> activity.toggleMenu()
} }
} }
recycler.longTapListener = f@{ event ->
if (activity.menuVisible || config.longTapEnabled) {
val child = recycler.findChildViewUnder(event.x, event.y)
if (child != null) {
val position = recycler.getChildAdapterPosition(child)
val item = adapter.items.getOrNull(position)
if (item is ReaderPage) {
activity.onPageLongTap(item)
return@f true
}
}
}
false
}
config.imagePropertyChangedListener = { config.imagePropertyChangedListener = {
refreshAdapter() refreshAdapter()

View File

@ -188,11 +188,6 @@ class SettingsReaderController : SettingsController() {
titleRes = R.string.tapping titleRes = R.string.tapping
defaultValue = true defaultValue = true
} }
switchPreference {
key = Keys.readWithLongTap
titleRes = R.string.long_tap_dialog
defaultValue = true
}
switchPreference { switchPreference {
key = Keys.readWithVolumeKeys key = Keys.readWithVolumeKeys
titleRes = R.string.volume_keys titleRes = R.string.volume_keys

View File

@ -1,6 +1,15 @@
package eu.kanade.tachiyomi.util package eu.kanade.tachiyomi.util
import android.content.SharedPreferences import android.content.SharedPreferences
import android.widget.CompoundButton
import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.Spinner
import androidx.annotation.ArrayRes
import androidx.appcompat.widget.AppCompatSpinner
import com.f2prateek.rx.preferences.Preference
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
@ -40,3 +49,68 @@ inline fun <reified T> SharedPreferences.getItem(key: String, default: T): T {
else -> throw IllegalArgumentException("Generic type not handled: ${T::class.java.name}") else -> throw IllegalArgumentException("Generic type not handled: ${T::class.java.name}")
} }
} }
/**
* Binds a checkbox or switch view with a boolean preference.
*/
fun CompoundButton.bindToPreference(pref: Preference<Boolean>, block: (() -> Unit)? = null) {
isChecked = pref.getOrDefault()
setOnCheckedChangeListener { _, isChecked ->
pref.set(isChecked)
block?.invoke()
}
}
/**
* Binds a checkbox or switch view with a boolean preference.
*/
fun CompoundButton.bindToPreference(
pref: com.tfcporciuncula.flow
.Preference<Boolean>,
block: ((Boolean) -> Unit)? = null
) {
isChecked = pref.get()
setOnCheckedChangeListener { _, isChecked ->
pref.set(isChecked)
block?.invoke(isChecked)
}
}
/**
* Binds a radio group with a int preference.
*/
fun RadioGroup.bindToPreference(pref: Preference<Int>, block: (() -> Unit)? = null) {
(getChildAt(pref.getOrDefault()) as RadioButton).isChecked = true
setOnCheckedChangeListener { _, checkedId ->
val index = indexOfChild(findViewById(checkedId))
pref.set(index)
block?.invoke()
}
}
/**
* Binds a spinner to an int preference with an optional offset for the value.
*/
fun Spinner.bindToPreference(
pref: com.tfcporciuncula.flow.Preference<Int>,
offset: Int = 0
) {
onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
pref.set(position + offset)
}
setSelection(pref.get() - offset, 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.
*/
fun Spinner.bindToIntPreference(pref: com.tfcporciuncula.flow.Preference<Int>, @ArrayRes intValuesResource: Int) {
val intValues = resources.getStringArray(intValuesResource).map { it.toIntOrNull() }
onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
pref.set(intValues[position] ?: 0)
}
setSelection(intValues.indexOf(pref.get()), false)
}

View File

@ -1,27 +1,32 @@
package eu.kanade.tachiyomi.widget package eu.kanade.tachiyomi.widget
import android.app.Activity
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.Gravity
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialog
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.BaseController
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.view.expand import eu.kanade.tachiyomi.util.view.expand
import eu.kanade.tachiyomi.util.view.setEdgeToEdge import eu.kanade.tachiyomi.util.view.setEdgeToEdge
import eu.kanade.tachiyomi.util.view.updateLayoutParams
import kotlinx.android.synthetic.main.library_list_controller.*
import kotlinx.android.synthetic.main.tabbed_bottom_sheet.* import kotlinx.android.synthetic.main.tabbed_bottom_sheet.*
abstract class TabbedBottomSheetDialog(private val controller: BaseController) : abstract class TabbedBottomSheetDialog(private val activity: Activity) :
BottomSheetDialog BottomSheetDialog
(controller.activity!!, R.style.BottomSheetDialogTheme) { (activity, R.style.BottomSheetDialogTheme) {
private var sheetBehavior: BottomSheetBehavior<*> private var sheetBehavior: BottomSheetBehavior<*>
val activity = controller.activity!! open var offset = -1
init { init {
// Use activity theme for this layout // Use activity theme for this layout
val view = activity.layoutInflater.inflate(R.layout.tabbed_bottom_sheet, null) val view = activity.layoutInflater.inflate(R.layout.tabbed_bottom_sheet, null)
@ -29,7 +34,9 @@ abstract class TabbedBottomSheetDialog(private val controller: BaseController) :
setContentView(view) setContentView(view)
sheetBehavior = BottomSheetBehavior.from(view.parent as ViewGroup) sheetBehavior = BottomSheetBehavior.from(view.parent as ViewGroup)
setEdgeToEdge(activity, view) setEdgeToEdge(activity, view)
val height = activity.window.decorView.rootWindowInsets.systemWindowInsetBottom
val height = activity.window.decorView.rootWindowInsets.systemWindowInsetTop
pager.maxHeight = activity.window.decorView.height - height - 125.dpToPx
val adapter = TabbedSheetAdapter() val adapter = TabbedSheetAdapter()
pager.offscreenPageLimit = 2 pager.offscreenPageLimit = 2
@ -41,6 +48,7 @@ abstract class TabbedBottomSheetDialog(private val controller: BaseController) :
super.onStart() super.onStart()
sheetBehavior.skipCollapsed = true sheetBehavior.skipCollapsed = true
sheetBehavior.expand() sheetBehavior.expand()
val height = activity.window.decorView.rootWindowInsets.systemWindowInsetTop
} }
abstract fun getTabViews(): List<View> abstract fun getTabViews(): List<View>
@ -64,6 +72,13 @@ abstract class TabbedBottomSheetDialog(private val controller: BaseController) :
} }
class MeasuredViewPager @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null): ViewPager(context, attrs) { class MeasuredViewPager @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null): ViewPager(context, attrs) {
var maxHeight = 0
set(value) {
field = value
requestLayout()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
var heightMeasureSpec = heightMeasureSpec var heightMeasureSpec = heightMeasureSpec
super.onMeasure(widthMeasureSpec, heightMeasureSpec) super.onMeasure(widthMeasureSpec, heightMeasureSpec)
@ -84,6 +99,9 @@ class MeasuredViewPager @JvmOverloads constructor(context: Context, attrs: Attri
if (height != 0) { if (height != 0) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) + (rootWindowInsets?.systemWindowInsetBottom ?: 0) heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) + (rootWindowInsets?.systemWindowInsetBottom ?: 0)
} }
if (maxHeight < height) {
heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST) + (rootWindowInsets?.systemWindowInsetBottom ?: 0)
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec) super.onMeasure(widthMeasureSpec, heightMeasureSpec)
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

View File

@ -2,7 +2,8 @@
android:width="24dp" android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportWidth="24.0" android:viewportWidth="24.0"
android:viewportHeight="24.0"> android:viewportHeight="24.0"
android:tint="?attr/actionBarTintColor">
<path <path
android:fillColor="#FF000000" android:fillColor="#FF000000"
android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z"/> android:pathData="M18,2H6c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM6,4h5v8l-2.5,-1.5L6,12V4z"/>

View File

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:colorBackground"
android:baselineAligned="false"
android:orientation="horizontal">
<FrameLayout
android:id="@+id/frame"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@id/scroll"
app:layout_constraintEnd_toStartOf="@id/scroll"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/scroll">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:src="@drawable/filter_mock" />
<View
android:id="@+id/brightness_overlay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
<eu.kanade.tachiyomi.ui.reader.ReaderColorFilterView
android:id="@+id/color_overlay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" />
</FrameLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/scroll"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/frame">
<include layout="@layout/reader_color_filter" />
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,10 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <eu.kanade.tachiyomi.ui.reader.settings.ReaderFilterView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/filter_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bottom_sheet_rounded_background"
android:forceDarkAllowed="false">
<androidx.core.widget.NestedScrollView
android:id="@+id/settings_scroll_view"
android:layout_width="match_parent"
android:clipToPadding="false"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraint_layout" android:id="@+id/constraint_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="16dp"> android:paddingStart="@dimen/material_component_dialogs_padding_around_content_area"
android:paddingTop="0dp"
android:paddingEnd="@dimen/material_component_dialogs_padding_around_content_area">
<androidx.legacy.widget.Space <androidx.legacy.widget.Space
android:id="@+id/spinner_end" android:id="@+id/spinner_end"
@ -161,24 +177,15 @@
<!-- Filter mode --> <!-- Filter mode -->
<TextView <eu.kanade.tachiyomi.ui.reader.settings.ReaderPreferenceView
android:id="@+id/color_filter_mode_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/color_filter_blend_mode"
app:layout_constraintBaseline_toBaselineOf="@id/color_filter_mode"
app:layout_constraintEnd_toStartOf="@id/color_filter_mode"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/color_filter_mode" android:id="@+id/color_filter_mode"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" app:title="@string/color_filter_blend_mode"
android:entries="@array/color_filter_modes" android:entries="@array/color_filter_modes"
app:layout_constraintEnd_toEndOf="@id/spinner_end" app:layout_constraintTop_toBottomOf="@id/seekbar_color_filter_alpha"
app:layout_constraintStart_toEndOf="@id/bottom_line" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/seekbar_color_filter_alpha" /> app:layout_constraintEnd_toEndOf="@id/spinner_end" />
<!-- Brightness --> <!-- Brightness -->
@ -188,7 +195,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:text="@string/use_custom_brightness" android:text="@string/use_custom_brightness"
app:layout_constraintTop_toBottomOf="@id/color_filter_mode_text" /> app:layout_constraintTop_toBottomOf="@id/color_filter_mode" />
<!-- Brightness value --> <!-- Brightness value -->
@ -234,3 +241,5 @@
app:layout_constraintGuide_percent="0.5" /> app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</eu.kanade.tachiyomi.ui.reader.settings.ReaderFilterView>

View File

@ -1,41 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/BottomSheetDialogTheme"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bottom_sheet_rounded_background"
android:clipToPadding="false"
android:orientation="vertical">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/filter_mock" />
<View
android:id="@+id/brightness_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
<eu.kanade.tachiyomi.ui.reader.ReaderColorFilterView
android:id="@+id/color_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
</FrameLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="@layout/reader_color_filter" />
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<eu.kanade.tachiyomi.ui.reader.settings.ReaderGeneralView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/filter_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bottom_sheet_rounded_background"
android:forceDarkAllowed="false">
<androidx.core.widget.NestedScrollView
android:id="@+id/settings_scroll_view"
android:layout_width="match_parent"
android:clipToPadding="false"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/constraint_layout"
style="@style/BottomSheetDialogTheme"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingStart="@dimen/material_component_dialogs_padding_around_content_area"
android:paddingTop="0dp"
android:paddingEnd="@dimen/material_component_dialogs_padding_around_content_area">
<eu.kanade.tachiyomi.ui.reader.settings.ReaderPreferenceView
android:id="@+id/viewer_series"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:title="@string/viewer_for_this_series"
android:entries="@array/viewers_selector" />
<eu.kanade.tachiyomi.ui.reader.settings.ReaderPreferenceView
android:id="@+id/rotation_mode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:title="@string/rotation"
android:entries="@array/rotation_type" />
<eu.kanade.tachiyomi.ui.reader.settings.ReaderPreferenceView
android:id="@+id/background_color"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:title="@string/background_color"
android:entries="@array/reader_themes" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/show_page_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/show_page_number"
android:textColor="?android:attr/textColorPrimary" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/fullscreen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/fullscreen"
android:textColor="?android:attr/textColorPrimary" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/keepscreen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/keep_screen_on"
android:textColor="?android:attr/textColorPrimary" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/always_show_chapter_transition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/always_show_chapter_transition"
android:textColor="?android:attr/textColorPrimary" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</eu.kanade.tachiyomi.ui.reader.settings.ReaderGeneralView>

View File

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<eu.kanade.tachiyomi.ui.reader.settings.ReaderPagedView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/filter_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bottom_sheet_rounded_background"
android:forceDarkAllowed="false">
<androidx.core.widget.NestedScrollView
android:id="@+id/settings_scroll_view"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/material_component_dialogs_padding_around_content_area"
android:orientation="vertical"
android:paddingTop="0dp"
android:paddingEnd="@dimen/material_component_dialogs_padding_around_content_area">
<eu.kanade.tachiyomi.ui.reader.settings.ReaderPreferenceView
android:id="@+id/scale_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:title="@string/scale_type"
android:entries="@array/image_scale_type" />
<eu.kanade.tachiyomi.ui.reader.settings.ReaderPreferenceView
android:id="@+id/zoom_start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:title="@string/zoom_start_position"
android:entries="@array/zoom_start" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/crop_borders"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/crop_borders"
android:textColor="?android:attr/textColorPrimary" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/page_transitions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@string/page_transitions"
android:textColor="?android:attr/textColorPrimary" />
<!-- Webtoon Prefs -->
<eu.kanade.tachiyomi.ui.reader.settings.ReaderPreferenceView
android:id="@+id/webtoon_side_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:title="@string/pref_webtoon_side_padding"
android:entries="@array/webtoon_side_padding" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/crop_borders_webtoon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/crop_borders"
android:textColor="?android:attr/textColorPrimary"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/webtoon_enable_zoom_out"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/enable_zoom_out"
android:textColor="?android:attr/textColorPrimary"
app:layout_constraintTop_toBottomOf="@id/webtoon_side_padding" />
<androidx.constraintlayout.widget.Group
android:id="@+id/pager_prefs_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="scale_type,zoom_start,crop_borders,page_transitions"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group
android:id="@+id/webtoon_prefs_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="crop_borders_webtoon,webtoon_side_padding,webtoon_enable_zoom_out" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</eu.kanade.tachiyomi.ui.reader.settings.ReaderPagedView>

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/square_ripple"
xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title_view"
style="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="?android:attr/textColorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
tools:text="Title"
android:maxLines="1" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/detail_view"
style="@style/TextAppearance.MaterialComponents.Body2"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/start_barrier"
app:layout_constraintTop_toTopOf="parent"
tools:text="Details" />
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="1dp"
app:tint="?android:attr/textColorSecondary"
android:src="@drawable/ic_expand_more_24dp"
app:layout_constraintStart_toEndOf="@id/detail_view"
app:layout_constraintTop_toTopOf="@id/detail_view"
app:layout_constraintBottom_toBottomOf="@id/detail_view"
/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/start_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="right"
app:constraint_referenced_ids="bottom_line,title_view" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/bottom_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,328 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/filter_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/bottom_sheet_rounded_background"
android:forceDarkAllowed="false">
<androidx.core.widget.NestedScrollView
android:id="@+id/settings_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraint_layout"
style="@style/BottomSheetDialogTheme"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:clipToPadding="false"
android:orientation="vertical"
android:paddingStart="@dimen/material_component_dialogs_padding_around_content_area"
android:paddingTop="16dp"
android:paddingEnd="@dimen/material_component_dialogs_padding_around_content_area">
<!-- General preferences -->
<TextView
android:id="@+id/general_prefs"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/general"
android:textColor="?attr/colorAccent"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.legacy.widget.Space
android:id="@+id/spinner_end"
android:layout_width="16dp"
android:layout_height="match_parent"
app:layout_constraintStart_toEndOf="parent" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/viewer_for_this_series"
app:layout_constraintBaseline_toBaselineOf="@id/viewer"
app:layout_constraintEnd_toStartOf="@id/bottom_line"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/viewer"
android:layout_width="0dp"
android:layout_height="24dp"
android:layout_marginTop="16dp"
android:entries="@array/viewers_selector"
app:layout_constraintEnd_toEndOf="@id/spinner_end"
app:layout_constraintStart_toEndOf="@id/bottom_line"
app:layout_constraintTop_toBottomOf="@id/general_prefs" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/rotation"
app:layout_constraintBaseline_toBaselineOf="@id/rotation_mode"
app:layout_constraintEnd_toStartOf="@id/bottom_line"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/rotation_mode"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:entries="@array/rotation_type"
app:layout_constraintEnd_toEndOf="@id/spinner_end"
app:layout_constraintStart_toEndOf="@id/bottom_line"
app:layout_constraintTop_toBottomOf="@id/viewer" />
<TextView
android:id="@+id/background_color_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/background_color"
app:layout_constraintBaseline_toBaselineOf="@id/background_color"
app:layout_constraintEnd_toStartOf="@id/background_color"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/background_color"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:entries="@array/reader_themes"
app:layout_constraintEnd_toEndOf="@id/spinner_end"
app:layout_constraintStart_toEndOf="@id/bottom_line"
app:layout_constraintTop_toBottomOf="@id/rotation_mode" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/show_page_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/show_page_number"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/background_color" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/true_color"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@string/true_32bit_color"
android:textColor="?android:attr/textColorSecondary"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/show_page_number"
tools:visibility="visible" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/fullscreen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@string/fullscreen"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/true_color" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/keepscreen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@string/keep_screen_on"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/fullscreen" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/always_show_chapter_transition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/always_show_chapter_transition"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/keepscreen" />
<androidx.legacy.widget.Space
android:id="@+id/end_general_preferences"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="@id/always_show_chapter_transition" />
<!-- Pager preferences -->
<TextView
android:id="@+id/pager_prefs"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/paged"
android:textColor="?attr/colorAccent"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/end_general_preferences" />
<TextView
android:id="@+id/zoom_start_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/scale_type"
app:layout_constraintBaseline_toBaselineOf="@id/scale_type"
app:layout_constraintEnd_toStartOf="@id/bottom_line"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/scale_type"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:entries="@array/image_scale_type"
app:layout_constraintEnd_toEndOf="@id/spinner_end"
app:layout_constraintStart_toEndOf="@id/bottom_line"
app:layout_constraintTop_toBottomOf="@id/pager_prefs" />
<TextView
android:id="@+id/scale_type_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/zoom_start_position"
app:layout_constraintBaseline_toBaselineOf="@id/zoom_start"
app:layout_constraintEnd_toStartOf="@id/bottom_line"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/zoom_start"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:entries="@array/zoom_start"
app:layout_constraintEnd_toEndOf="@id/spinner_end"
app:layout_constraintStart_toEndOf="@id/bottom_line"
app:layout_constraintTop_toBottomOf="@id/scale_type" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/crop_borders"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/crop_borders"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/zoom_start" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/page_transitions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="@string/page_transitions"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/crop_borders" />
<!-- Webtoon preferences -->
<TextView
android:id="@+id/webtoon_prefs"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/webtoon"
android:textColor="?attr/colorAccent"
android:textStyle="bold"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/end_general_preferences" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/crop_borders_webtoon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/crop_borders"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/webtoon_prefs" />
<TextView
android:id="@+id/webtoon_side_padding_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/pref_webtoon_side_padding"
app:layout_constraintBaseline_toBaselineOf="@id/webtoon_side_padding"
app:layout_constraintEnd_toStartOf="@id/bottom_line"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/webtoon_side_padding"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp"
android:entries="@array/webtoon_side_padding"
app:layout_constraintEnd_toEndOf="@id/spinner_end"
app:layout_constraintRight_toRightOf="@id/spinner_end"
app:layout_constraintStart_toEndOf="@id/bottom_line"
app:layout_constraintTop_toBottomOf="@id/crop_borders_webtoon" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/webtoon_enable_zoom_out"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/enable_zoom_out"
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/webtoon_side_padding" />
<!-- Groups of preferences -->
<androidx.constraintlayout.widget.Group
android:id="@+id/pager_prefs_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="pager_prefs,scale_type,scale_type_text,
zoom_start_text,zoom_start,crop_borders,page_transitions,background_color,background_color_label,background_color_label,background_color"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group
android:id="@+id/webtoon_prefs_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="webtoon_prefs,crop_borders_webtoon,
webtoon_side_padding_text,webtoon_side_padding,webtoon_enable_zoom_out" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/bottom_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<ImageView
android:id="@+id/pill"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|top"
android:layout_marginTop="5dp"
android:alpha="0.25"
android:contentDescription="@string/drag_handle"
android:src="@drawable/draggable_pill"
android:tint="?android:attr/textColorPrimary" />
<ImageView
android:id="@+id/close_button"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="end"
android:layout_marginTop="12dp"
android:layout_marginEnd="12dp"
android:background="@drawable/round_ripple"
android:clickable="true"
android:contentDescription="@string/close"
android:focusable="true"
android:src="@drawable/ic_close_24dp"
android:tint="@color/gray_button" />
</FrameLayout>

View File

@ -3,16 +3,40 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item <item
android:id="@+id/action_custom_filter" android:id="@+id/action_display_settings"
android:icon="@drawable/ic_brightness_night_24dp" android:icon="@drawable/ic_tune_24dp"
android:title="@string/custom_filter" android:title="@string/display_options"
app:showAsAction="ifRoom" /> app:showAsAction="ifRoom" />
<item <item
android:id="@+id/action_settings" android:id="@+id/action_manga_details"
android:icon="@drawable/ic_book_24dp"
android:title="@string/details"
app:showAsAction="ifRoom" />
<item
android:id="@+id/action_share_page"
android:icon="@drawable/ic_share_24dp"
android:title="@string/share_page"
app:showAsAction="never" />
<item
android:id="@+id/action_save_page"
android:icon="@drawable/ic_save_24dp"
android:title="@string/save_page"
app:showAsAction="never" />
<item
android:id="@+id/action_set_page_as_cover"
android:icon="@drawable/ic_photo_24dp"
android:title="@string/set_page_as_cover"
app:showAsAction="never" />
<item
android:id="@+id/action_reader_settings"
android:icon="@drawable/ic_settings_24dp" android:icon="@drawable/ic_settings_24dp"
android:title="@string/settings" android:title="@string/reader_settings"
app:showAsAction="ifRoom" app:showAsAction="never" />
/>
</menu> </menu>

View File

@ -15,4 +15,10 @@
<attr name="readerBackground" format="color"/> <attr name="readerBackground" format="color"/>
<attr name="tabBarIconColor" format="reference|integer"/> <attr name="tabBarIconColor" format="reference|integer"/>
<attr name="tabBarIconInactive" format="reference|integer"/> <attr name="tabBarIconInactive" format="reference|integer"/>
<declare-styleable name="ReaderPreferenceView">
<attr name="title" format="reference|string"/>
<attr name="android:entries"/>
<attr name="summary" format="reference|string" />
</declare-styleable>
</resources> </resources>

View File

@ -278,6 +278,11 @@
<!-- Reader --> <!-- Reader -->
<string name="custom_filter">Custom filter</string> <string name="custom_filter">Custom filter</string>
<string name="set_as_cover">Set as cover</string> <string name="set_as_cover">Set as cover</string>
<string name="set_page_as_cover">Set page as cover</string>
<string name="share_page">Share page</string>
<string name="save_page">Save page</string>
<string name="_details">%1$s details</string>
<string name="reader_settings">Reader settings</string>
<string name="set_as_default_for_all">Set as default for all</string> <string name="set_as_default_for_all">Set as default for all</string>
<string name="cover_updated">Cover updated</string> <string name="cover_updated">Cover updated</string>
<string name="page_">Page %1$d</string> <string name="page_">Page %1$d</string>