Adding webtoon side margins + continuous vertical

Co-Authored-By: arkon <arkon@users.noreply.github.com>
Co-Authored-By: morcefaster <51055657+morcefaster@users.noreply.github.com>
This commit is contained in:
Jay 2020-04-17 00:04:44 -04:00
parent 907c4b77a8
commit 0af41005f8
12 changed files with 158 additions and 27 deletions

View File

@ -51,6 +51,8 @@ object PreferenceKeys {
const val readWithVolumeKeysInverted = "reader_volume_keys_inverted"
const val webtoonSidePadding = "webtoon_side_padding"
const val updateOnlyNonCompleted = "pref_update_only_non_completed_key"
const val autoUpdateTrack = "pref_auto_update_manga_sync_key"

View File

@ -95,6 +95,8 @@ class PreferencesHelper(val context: Context) {
fun cropBordersWebtoon() = rxPrefs.getBoolean(Keys.cropBordersWebtoon, false)
fun webtoonSidePadding() = rxPrefs.getInteger(Keys.webtoonSidePadding, 0)
fun readWithTapping() = rxPrefs.getBoolean(Keys.readWithTapping, true)
fun readWithLongTap() = rxPrefs.getBoolean(Keys.readWithLongTap, true)

View File

@ -144,6 +144,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>(),
const val RIGHT_TO_LEFT = 2
const val VERTICAL = 3
const val WEBTOON = 4
const val VERTICAL_PLUS = 5
fun newIntent(context: Context, manga: Manga, chapter: Chapter):
Intent {
@ -402,6 +403,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>(),
RIGHT_TO_LEFT -> R2LPagerViewer(this)
VERTICAL -> VerticalPagerViewer(this)
WEBTOON -> WebtoonViewer(this)
VERTICAL_PLUS -> WebtoonViewer(this, isContinuous = false)
else -> L2RPagerViewer(this)
}

View File

@ -8,6 +8,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.CompoundButton
import android.widget.Spinner
import androidx.annotation.ArrayRes
import com.f2prateek.rx.preferences.Preference
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
@ -55,7 +56,7 @@ class ReaderSettingsSheet(private val activity: ReaderActivity) :
.getOrDefault() == 0 && activity.window.decorView.rootWindowInsets.systemWindowInsetRight == 0 && activity.window.decorView.rootWindowInsets.systemWindowInsetLeft == 0
) window?.decorView?.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
val height = activity.window.decorView.rootWindowInsets.systemWindowInsetBottom
sheetBehavior.peekHeight = 500.dpToPx + height
sheetBehavior.peekHeight = 550.dpToPx + height
sheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, progress: Float) {
@ -87,7 +88,7 @@ class ReaderSettingsSheet(private val activity: ReaderActivity) :
}
setBottomEdge(
if (activity.viewer is PagerViewer) page_transitions else crop_borders_webtoon, activity
if (activity.viewer is PagerViewer) page_transitions else webtoon_side_padding, activity
)
close_button.setOnClickListener {
@ -138,6 +139,7 @@ class ReaderSettingsSheet(private val activity: ReaderActivity) :
webtoon_prefs_group.visible()
pager_prefs_group.gone()
crop_borders_webtoon.bindToPreference(preferences.cropBordersWebtoon())
webtoon_side_padding.bindToIntPreference(preferences.webtoonSidePadding(), R.array.webtoon_side_padding_values)
}
/**
@ -162,4 +164,17 @@ class ReaderSettingsSheet(private val activity: ReaderActivity) :
}
setSelection(pref.getOrDefault() - 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])
}
setSelection(intValues.indexOf(pref.getOrDefault()), false)
}
}

View File

@ -4,6 +4,7 @@ import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
@ -12,7 +13,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
/**
* RecyclerView Adapter used by this [viewer] to where [ViewerChapters] updates are posted.
*/
class WebtoonAdapter(val viewer: WebtoonViewer) : androidx.recyclerview.widget.RecyclerView.Adapter<androidx.recyclerview.widget.RecyclerView.ViewHolder>() {
class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
/**
* List of currently set items.
@ -20,6 +21,7 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : androidx.recyclerview.widget.R
var items: List<Any> = emptyList()
private set
var currentChapter: ReaderChapter? = null
/**
* Updates this adapter with the given [chapters]. It handles setting a few pages of the
* next/previous chapter to allow seamless transitions.
@ -48,6 +50,8 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : androidx.recyclerview.widget.R
newItems.addAll(currPages)
}
currentChapter = chapters.currChapter
// Add next chapter transition and pages.
if (forceTransition || chapters.nextChapter?.state !is ReaderChapter.State.Loaded) {
newItems.add(ChapterTransition.Next(chapters.currChapter, chapters.nextChapter))
@ -89,7 +93,7 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : androidx.recyclerview.widget.R
/**
* Creates a new view holder for an item with the given [viewType].
*/
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
PAGE_VIEW -> {
val view = FrameLayout(parent.context)
@ -106,7 +110,7 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : androidx.recyclerview.widget.R
/**
* Binds an existing view [holder] with the item at the given [position].
*/
override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int) {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = items[position]
when (holder) {
is WebtoonPageHolder -> holder.bind(item as ReaderPage)
@ -117,7 +121,7 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : androidx.recyclerview.widget.R
/**
* Recycles an existing view [holder] before adding it to the view pool.
*/
override fun onViewRecycled(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder) {
override fun onViewRecycled(holder: RecyclerView.ViewHolder) {
when (holder) {
is WebtoonPageHolder -> holder.recycle()
is WebtoonTransitionHolder -> holder.recycle()

View File

@ -37,6 +37,9 @@ class WebtoonConfig(preferences: PreferencesHelper = Injekt.get()) {
var alwaysShowChapterTransition = true
private set
var sidePadding = 0
private set
init {
preferences.readWithTapping()
.register({ tappingEnabled = it })
@ -58,6 +61,9 @@ class WebtoonConfig(preferences: PreferencesHelper = Injekt.get()) {
preferences.alwaysShowChapterTransition()
.register({ alwaysShowChapterTransition = it })
preferences.webtoonSidePadding()
.register({ sidePadding = it }, { imagePropertyChangedListener?.invoke() })
}
fun unsubscribe() {

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon
import android.annotation.SuppressLint
import android.content.Intent
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.net.Uri
import android.view.Gravity
@ -120,6 +121,19 @@ class WebtoonPageHolder(
fun bind(page: ReaderPage) {
this.page = page
observeStatus()
refreshLayoutParams()
}
private fun refreshLayoutParams() {
frame.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT).apply {
if (!viewer.isContinuous) {
bottomMargin = 15.dpToPx
}
val margin = Resources.getSystem().displayMetrics.widthPixels * (viewer.config.sidePadding / 100f)
marginEnd = margin.toInt()
marginStart = margin.toInt()
}
}
/**

View File

@ -14,11 +14,13 @@ import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
import eu.kanade.tachiyomi.ui.reader.viewer.BaseViewer
import rx.subscriptions.CompositeSubscription
import timber.log.Timber
import kotlin.math.max
import kotlin.math.min
/**
* Implementation of a [BaseViewer] to display pages with a [RecyclerView].
*/
class WebtoonViewer(val activity: ReaderActivity) : BaseViewer {
class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = true) : BaseViewer {
/**
* Recycler view used by this viewer.
@ -70,10 +72,11 @@ class WebtoonViewer(val activity: ReaderActivity) : BaseViewer {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
val position = layoutManager.findLastEndVisibleItemPosition()
val item = adapter.items.getOrNull(position)
val allowPreload = checkAllowPreload(item as? ReaderPage)
if (item != null && currentPage != item) {
currentPage = item
when (item) {
is ReaderPage -> onPageSelected(item, position)
is ReaderPage -> onPageSelected(item, allowPreload)
is ChapterTransition -> onTransitionSelected(item)
}
}
@ -113,10 +116,34 @@ class WebtoonViewer(val activity: ReaderActivity) : BaseViewer {
false
}
config.imagePropertyChangedListener = {
refreshAdapter()
}
frame.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
frame.addView(recycler)
}
private fun checkAllowPreload(page: ReaderPage?): Boolean {
// Page is transition page - preload allowed
page == null ?: return true
// Initial opening - preload allowed
currentPage == null ?: return true
val nextItem = adapter.items.getOrNull(adapter.items.count() - 1)
val nextChapter = (nextItem as? ChapterTransition.Next)?.to ?: (nextItem as? ReaderPage)?.chapter
// Allow preload for
// 1. Going between pages of same chapter
// 2. Next chapter page
return when (page!!.chapter) {
(currentPage as? ReaderPage)?.chapter -> true
nextChapter -> true
else -> false
}
}
/**
* Returns the view this viewer uses.
*/
@ -137,18 +164,20 @@ class WebtoonViewer(val activity: ReaderActivity) : BaseViewer {
* Called from the RecyclerView listener when a [page] is marked as active. It notifies the
* activity of the change and requests the preload of the next chapter if this is the last page.
*/
private fun onPageSelected(page: ReaderPage, position: Int) {
private fun onPageSelected(page: ReaderPage, allowPreload: Boolean) {
val pages = page.chapter.pages!! // Won't be null because it's the loaded chapter
Timber.d("onPageSelected: ${page.number}/${pages.size}")
activity.onPageSelected(page)
// Preload next chapter once we're within the last 3 pages of the current chapter
val inPreloadRange = pages.size - page.number < 3
if (inPreloadRange) {
if (inPreloadRange && allowPreload && page.chapter == adapter.currentChapter) {
Timber.d("Request preload next chapter because we're at page ${page.number} of ${pages.size}")
val transition = adapter.items.getOrNull(pages.size + 1) as? ChapterTransition.Next
if (transition?.to != null) {
activity.requestPreloadChapter(transition.to)
val nextItem = adapter.items.getOrNull(adapter.items.size - 1)
val transitionChapter = (nextItem as? ChapterTransition.Next)?.to ?: (nextItem as?ReaderPage)?.chapter
if (transitionChapter != null) {
Timber.d("Requesting to preload chapter ${transitionChapter.chapter.chapter_number}")
activity.requestPreloadChapter(transitionChapter)
}
}
}
@ -255,4 +284,15 @@ class WebtoonViewer(val activity: ReaderActivity) : BaseViewer {
override fun handleGenericMotionEvent(event: MotionEvent): Boolean {
return false
}
/**
* Notifies adapter of changes around the current page to trigger a relayout in the recycler.
* Used when an image configuration is changed.
*/
private fun refreshAdapter() {
val position = layoutManager.findLastEndVisibleItemPosition()
adapter.notifyItemRangeChanged(
max(0, position - 2),
min(position + 2, adapter.itemCount - 1))
}
}

View File

@ -14,8 +14,8 @@ class SettingsReaderController : SettingsController() {
key = Keys.defaultViewer
titleRes = R.string.default_viewer
entriesRes = arrayOf(R.string.left_to_right_viewer, R.string.right_to_left_viewer,
R.string.vertical_viewer, R.string.webtoon)
entryRange = 1..4
R.string.vertical_viewer, R.string.webtoon, R.string.continuous_vertical)
entryRange = 1..5
defaultValue = 1
}
intListPreference(activity) {

View File

@ -98,7 +98,7 @@
app:layout_constraintStart_toEndOf="@id/bottom_line"
app:layout_constraintTop_toBottomOf="@id/rotation_mode" />
<androidx.appcompat.widget.SwitchCompat
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/show_page_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -107,7 +107,7 @@
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/background_color" />
<androidx.appcompat.widget.SwitchCompat
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/true_color"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -118,7 +118,7 @@
app:layout_constraintTop_toBottomOf="@id/show_page_number"
tools:visibility="visible" />
<androidx.appcompat.widget.SwitchCompat
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/fullscreen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -127,7 +127,7 @@
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/true_color" />
<androidx.appcompat.widget.SwitchCompat
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/keepscreen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -136,7 +136,7 @@
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/fullscreen" />
<androidx.appcompat.widget.SwitchCompat
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/always_show_chapter_transition"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -201,7 +201,7 @@
app:layout_constraintStart_toEndOf="@id/bottom_line"
app:layout_constraintTop_toBottomOf="@id/scale_type" />
<androidx.appcompat.widget.SwitchCompat
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/crop_borders"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -210,7 +210,7 @@
android:textColor="?android:attr/textColorSecondary"
app:layout_constraintTop_toBottomOf="@id/zoom_start" />
<androidx.appcompat.widget.SwitchCompat
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/page_transitions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -233,16 +233,37 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/end_general_preferences" />
<androidx.appcompat.widget.SwitchCompat
<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_constraintBottom_toBottomOf="parent"
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_constraintEnd_toStartOf="@id/bottom_line"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBaseline_toBaselineOf="@id/webtoon_side_padding"/>
<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"
app:layout_constraintEnd_toEndOf="@id/spinner_end"
app:layout_constraintStart_toEndOf="@id/bottom_line"
app:layout_constraintBottom_toBottomOf="parent"
android:entries="@array/webtoon_side_padding"
app:layout_constraintRight_toRightOf="@id/spinner_end"
app:layout_constraintTop_toBottomOf="@id/crop_borders_webtoon"/>
<!-- Groups of preferences -->
<androidx.constraintlayout.widget.Group
@ -250,7 +271,8 @@
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"
app:constraint_referenced_ids="pager_prefs,scale_type,scale_type_text,
zoom_start_text,zoom_start,crop_borders,page_transitions"
tools:visibility="visible" />
<androidx.constraintlayout.widget.Group
@ -258,7 +280,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="webtoon_prefs,crop_borders_webtoon" />
app:constraint_referenced_ids="webtoon_prefs,crop_borders_webtoon,
webtoon_side_padding_text,webtoon_side_padding" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/bottom_line"

View File

@ -7,6 +7,7 @@
<item>@string/right_to_left_viewer</item>
<item>@string/vertical_viewer</item>
<item>@string/webtoon</item>
<item>@string/continuous_vertical</item>
</string-array>
<string-array name="reader_themes">
@ -25,6 +26,22 @@
<item>@string/smart_fit</item>
</string-array>
<string-array name="webtoon_side_padding">
<item>@string/webtoon_side_padding_0</item>
<item>@string/webtoon_side_padding_10</item>
<item>@string/webtoon_side_padding_15</item>
<item>@string/webtoon_side_padding_20</item>
<item>@string/webtoon_side_padding_25</item>
</string-array>
<string-array name="webtoon_side_padding_values">
<item>0</item>
<item>10</item>
<item>15</item>
<item>20</item>
<item>25</item>
</string-array>
<string-array name="zoom_start">
<item>@string/automatic</item>
<item>@string/left</item>
@ -44,5 +61,4 @@
<item>@string/multiply</item>
<item>@string/screen</item>
</string-array>
</resources>

View File

@ -291,6 +291,7 @@
<string name="left_to_right_viewer">Left to right</string>
<string name="right_to_left_viewer">Right to left</string>
<string name="vertical_viewer">Vertical</string>
<string name="continuous_vertical">Continuous vertical</string>
<string name="pager_viewer">Pager</string>
<string name="scale_type">Scale type</string>
<string name="fit_screen">Fit screen</string>
@ -309,6 +310,12 @@
<string name="smart_based_on_page">Smart (based on page)</string>
<string name="smart_based_on_page_and_theme">Smart (based on page and theme)</string>
<string name="always_show_chapter_transition">Always show chapter transition</string>
<string name="pref_webtoon_side_padding">Side padding</string>
<string name="webtoon_side_padding_0">None</string>
<string name="webtoon_side_padding_10">10%</string>
<string name="webtoon_side_padding_15">15%</string>
<string name="webtoon_side_padding_20">20%</string>
<string name="webtoon_side_padding_25">25%</string>
<!-- Manga details -->
<string name="about_this_">About this %1$s</string>