diff --git a/app/build.gradle b/app/build.gradle index 4a94d66b2..2199ca611 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,7 @@ android { minSdkVersion 21 targetSdkVersion 30 versionCode gitCommits - versionName '0.5.4' + versionName '1.0-a1' kapt { arguments { @@ -63,8 +63,8 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.0' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1' implementation 'androidx.core:core-ktx:1.5.0-alpha04' implementation 'androidx.activity:activity-ktx:1.2.0-beta01' diff --git a/app/src/main/java/org/koitharu/kotatsu/core/model/ZoomMode.kt b/app/src/main/java/org/koitharu/kotatsu/core/model/ZoomMode.kt new file mode 100644 index 000000000..9c1069a19 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/core/model/ZoomMode.kt @@ -0,0 +1,6 @@ +package org.koitharu.kotatsu.core.model + +enum class ZoomMode { + + FIT_CENTER, FIT_HEIGHT, FIT_WIDTH, KEEP_START +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt index bdb01526e..5965c8a9f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt @@ -6,11 +6,13 @@ import org.koitharu.kotatsu.core.model.MangaTag import org.koitharu.kotatsu.core.model.SortOrder import org.koitharu.kotatsu.domain.MangaLoaderContext -abstract class RemoteMangaRepository(protected val loaderContext: MangaLoaderContext) : MangaRepository { +abstract class RemoteMangaRepository( + protected val loaderContext: MangaLoaderContext +) : MangaRepository { protected abstract val source: MangaSource - protected val conf by lazy(LazyThreadSafetyMode.NONE) { + protected val conf by lazy { loaderContext.getSettings(source) } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt index c77fbe102..f2f049e99 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt @@ -7,6 +7,7 @@ import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.utils.ext.* +import java.util.* abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository( loaderContext @@ -14,7 +15,7 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe protected abstract val defaultDomain: String - override val sortOrders = arraySetOf( + override val sortOrders: Set = EnumSet.of( SortOrder.NEWEST, SortOrder.POPULARITY, SortOrder.ALPHABETICAL diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt index 9dae7fc60..cc807e2cd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt @@ -7,12 +7,14 @@ import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.utils.ext.* +import java.util.* +import kotlin.collections.ArrayList class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) { override val source = MangaSource.DESUME - override val sortOrders = arraySetOf( + override val sortOrders: Set = EnumSet.of( SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt index 0da7e8ee8..348debe44 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt @@ -7,13 +7,14 @@ import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.utils.ext.* +import java.util.* abstract class GroupleRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) { protected abstract val defaultDomain: String - override val sortOrders = arraySetOf( + override val sortOrders: Set = EnumSet.of( SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.NEWEST, SortOrder.RATING //FIXME SortOrder.ALPHABETICAL diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt index e612d8813..d614c0c97 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt @@ -4,13 +4,14 @@ import androidx.collection.ArraySet import androidx.collection.arraySetOf import org.json.JSONArray import org.json.JSONObject -import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.ParseException import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.utils.ext.* +import java.util.* +import kotlin.collections.ArrayList open class MangaLibRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) { @@ -19,7 +20,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) : override val source = MangaSource.MANGALIB - override val sortOrders = arraySetOf( + override val sortOrders: Set = EnumSet.of( SortOrder.RATING, SortOrder.ALPHABETICAL, SortOrder.POPULARITY, diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt index 31696b5a7..6222d1c74 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt @@ -15,7 +15,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) : override val source = MangaSource.MANGATOWN - override val sortOrders = arraySetOf( + override val sortOrders: Set = EnumSet.of( SortOrder.ALPHABETICAL, SortOrder.RATING, SortOrder.POPULARITY, diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt index 244f986d0..7205aa1fa 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt @@ -7,6 +7,7 @@ import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.utils.ext.* +import java.util.* class MangareadRepository( loaderContext: MangaLoaderContext @@ -14,7 +15,10 @@ class MangareadRepository( override val source = MangaSource.MANGAREAD - override val sortOrders = arraySetOf(SortOrder.UPDATED, SortOrder.POPULARITY) + override val sortOrders: Set = EnumSet.of( + SortOrder.UPDATED, + SortOrder.POPULARITY + ) override suspend fun getList( offset: Int, diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NudeMoonRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NudeMoonRepository.kt index a630f339c..e43a37a64 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NudeMoonRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NudeMoonRepository.kt @@ -7,13 +7,18 @@ import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.SourceSettings import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.utils.ext.* +import java.util.* import java.util.regex.Pattern class NudeMoonRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) { override val source = MangaSource.NUDEMOON - override val sortOrders = arraySetOf(SortOrder.NEWEST, SortOrder.POPULARITY, SortOrder.RATING) + override val sortOrders: Set = EnumSet.of( + SortOrder.NEWEST, + SortOrder.POPULARITY, + SortOrder.RATING + ) init { loaderContext.insertCookies( @@ -35,9 +40,7 @@ class NudeMoonRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit tag != null -> "https://$domain/tags/${tag.key}&rowstart=$offset" else -> "https://$domain/all_manga?${getSortKey(sortOrder)}&rowstart=$offset" } - val doc = loaderContext.httpGet(url) { - addHeader("Cookie", "NMfYa=1; nm_mobile=0;") - }.parseHtml() + val doc = loaderContext.httpGet(url).parseHtml() val root = doc.body().run { selectFirst("td.shoutbox") ?: selectFirst("td.main-bg") } ?: throw ParseException("Cannot find root") diff --git a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt index 8cd4aeb46..ea8ef8652 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/prefs/AppSettings.kt @@ -7,6 +7,7 @@ import androidx.appcompat.app.AppCompatDelegate import androidx.collection.arraySetOf import androidx.core.content.edit import androidx.preference.PreferenceManager +import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.core.parser.LocalMangaRepository import org.koitharu.kotatsu.utils.delegates.prefs.* import java.io.File @@ -18,13 +19,13 @@ class AppSettings private constructor(private val prefs: SharedPreferences) : PreferenceManager.getDefaultSharedPreferences(context) ) - var listMode by EnumPreferenceDelegate( + var listMode by IntEnumPreferenceDelegate( ListMode::class.java, KEY_LIST_MODE, ListMode.DETAILED_LIST ) - var defaultSection by EnumPreferenceDelegate( + var defaultSection by IntEnumPreferenceDelegate( AppSection::class.java, KEY_APP_SECTION, AppSection.HISTORY @@ -66,6 +67,12 @@ class AppSettings private constructor(private val prefs: SharedPreferences) : val isPreferRtlReader by BoolPreferenceDelegate(KEY_READER_PREFER_RTL, false) + val zoomMode by EnumPreferenceDelegate( + ZoomMode::class.java, + KEY_ZOOM_MODE, + ZoomMode.FIT_CENTER + ) + val trackSources by StringSetPreferenceDelegate( KEY_TRACK_SOURCES, arraySetOf(TRACK_FAVOURITES, TRACK_HISTORY) @@ -143,5 +150,6 @@ class AppSettings private constructor(private val prefs: SharedPreferences) : const val KEY_APP_PASSWORD = "app_password" const val KEY_PROTECT_APP = "protect_app" const val KEY_APP_VERSION = "app_version" + const val KEY_ZOOM_MODE = "zoom_mode" } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/domain/MangaLoaderContext.kt b/app/src/main/java/org/koitharu/kotatsu/domain/MangaLoaderContext.kt index 556f952cd..c2c884026 100644 --- a/app/src/main/java/org/koitharu/kotatsu/domain/MangaLoaderContext.kt +++ b/app/src/main/java/org/koitharu/kotatsu/domain/MangaLoaderContext.kt @@ -13,20 +13,16 @@ open class MangaLoaderContext : KoinComponent { private val okHttp by inject() private val cookieJar by inject() - suspend fun httpGet(url: String, block: (Request.Builder.() -> Unit)? = null): Response { + suspend fun httpGet(url: String): Response { val request = Request.Builder() .get() .url(url) - if (block != null) { - request.block() - } return okHttp.newCall(request.build()).await() } suspend fun httpPost( url: String, - form: Map, - block: (Request.Builder.() -> Unit)? = null + form: Map ): Response { val body = FormBody.Builder() form.forEach { (k, v) -> @@ -35,16 +31,12 @@ open class MangaLoaderContext : KoinComponent { val request = Request.Builder() .post(body.build()) .url(url) - if (block != null) { - request.block() - } return okHttp.newCall(request.build()).await() } suspend fun httpPost( url: String, - payload: String, - block: (Request.Builder.() -> Unit)? = null + payload: String ): Response { val body = FormBody.Builder() payload.split('&').forEach { @@ -58,9 +50,6 @@ open class MangaLoaderContext : KoinComponent { val request = Request.Builder() .post(body.build()) .url(url) - if (block != null) { - request.block() - } return okHttp.newCall(request.build()).await() } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderAction.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderAction.kt deleted file mode 100644 index 4b430063a..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderAction.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.koitharu.kotatsu.ui.reader - -enum class ReaderAction { - REPLACE, PREPEND, APPEND -} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt index 57dd7cd44..adce552bc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/ReaderActivity.kt @@ -96,7 +96,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh ViewCompat.setOnApplyWindowInsetsListener(rootLayout, this) settings.subscribe(this) - loadSettings() + loadSwitchSettings() orientationHelper.observeAutoOrientation() .onEach { toolbar_bottom.menu.findItem(R.id.action_screen_rotate).isVisible = !it @@ -370,7 +370,11 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { - loadSettings() + when (key) { + AppSettings.KEY_READER_SWITCHERS -> loadSwitchSettings() + AppSettings.KEY_READER_ANIMATION, + AppSettings.KEY_ZOOM_MODE -> reader?.recreateAdapter() + } } private fun showWaitWhileLoading() { @@ -410,7 +414,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh .build() } - private fun loadSettings() { + private fun loadSwitchSettings() { settings.readerPageSwitch.let { isTapSwitchEnabled = it.contains(AppSettings.PAGE_SWITCH_TAPS) isVolumeKeysSwitchEnabled = it.contains(AppSettings.PAGE_SWITCH_VOLUME_KEYS) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/AbstractReader.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/AbstractReader.kt index 446488e40..1421b80ef 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/AbstractReader.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/AbstractReader.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.ui.reader.base import android.content.Context import android.os.Bundle import android.view.View +import androidx.annotation.CallSuper import androidx.collection.LongSparseArray import androidx.core.view.postDelayed import kotlinx.coroutines.CancellationException @@ -131,6 +132,11 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout super.onDestroy() } + @CallSuper + open fun recreateAdapter() { + adapter = onCreateAdapter(pages) + } + fun getPages(): List? { val chapterId = (pages.getOrNull(getCurrentItem()) ?: return null).chapterId // TODO optimize diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/PageHolderDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/PageHolderDelegate.kt index 7e39afffe..5d98f5abd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/PageHolderDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/base/PageHolderDelegate.kt @@ -4,7 +4,10 @@ import android.net.Uri import androidx.core.net.toUri import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import kotlinx.coroutines.* +import org.koin.core.component.inject import org.koitharu.kotatsu.core.model.MangaPage +import org.koitharu.kotatsu.core.model.ZoomMode +import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.ui.reader.PageLoader import org.koitharu.kotatsu.utils.ext.launchAfter import org.koitharu.kotatsu.utils.ext.launchInstead @@ -14,8 +17,10 @@ import java.io.IOException class PageHolderDelegate( private val loader: PageLoader, private val callback: Callback -) : SubsamplingScaleImageView.DefaultOnImageEventListener(), CoroutineScope by loader { +) : SubsamplingScaleImageView.DefaultOnImageEventListener(), + CoroutineScope by loader { + private val settings by loader.inject() private var state = State.EMPTY private var job: Job? = null private var file: File? = null @@ -36,7 +41,7 @@ class PageHolderDelegate( override fun onReady() { state = State.SHOWING - callback.onImageShowing() + callback.onImageShowing(settings.zoomMode) } override fun onImageLoaded() { @@ -79,7 +84,7 @@ class PageHolderDelegate( state = State.LOADED callback.onImageReady(file.toUri()) } catch (e: CancellationException) { - //do nothing + // do nothing } catch (e: Exception) { state = State.ERROR callback.onError(e) @@ -99,7 +104,7 @@ class PageHolderDelegate( fun onImageReady(uri: Uri) - fun onImageShowing() + fun onImageShowing(zoom: ZoomMode) fun onImageShown() } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedPageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedPageHolder.kt new file mode 100644 index 000000000..c21253cd2 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedPageHolder.kt @@ -0,0 +1,48 @@ +package org.koitharu.kotatsu.ui.reader.reversed + +import android.graphics.PointF +import android.view.ViewGroup +import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView +import kotlinx.android.synthetic.main.item_page.* +import org.koitharu.kotatsu.core.model.ZoomMode +import org.koitharu.kotatsu.ui.reader.PageLoader +import org.koitharu.kotatsu.ui.reader.standard.PageHolder + +class ReversedPageHolder(parent: ViewGroup, loader: PageLoader) : PageHolder(parent, loader) { + + override fun onImageShowing(zoom: ZoomMode) { + ssiv.maxScale = 2f * maxOf( + ssiv.width / ssiv.sWidth.toFloat(), + ssiv.height / ssiv.sHeight.toFloat() + ) + when (zoom) { + ZoomMode.FIT_CENTER -> { + ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE) + ssiv.resetScaleAndCenter() + } + ZoomMode.FIT_HEIGHT -> { + ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM) + ssiv.minScale = ssiv.height / ssiv.sHeight.toFloat() + ssiv.setScaleAndCenter( + ssiv.minScale, + PointF(ssiv.sWidth.toFloat(), ssiv.sHeight / 2f) + ) + } + ZoomMode.FIT_WIDTH -> { + ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM) + ssiv.minScale = ssiv.width / ssiv.sWidth.toFloat() + ssiv.setScaleAndCenter( + ssiv.minScale, + PointF(ssiv.sWidth / 2f, 0f) + ) + } + ZoomMode.KEEP_START -> { + ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE) + ssiv.setScaleAndCenter( + ssiv.maxScale, + PointF(ssiv.sWidth.toFloat(), 0f) + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedPagesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedPagesAdapter.kt index f7021e9a1..c4de634fd 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedPagesAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedPagesAdapter.kt @@ -5,14 +5,13 @@ import org.koitharu.kotatsu.ui.base.list.BaseViewHolder import org.koitharu.kotatsu.ui.reader.PageLoader import org.koitharu.kotatsu.ui.reader.base.BaseReaderAdapter import org.koitharu.kotatsu.ui.reader.base.ReaderPage -import org.koitharu.kotatsu.ui.reader.standard.PageHolder class ReversedPagesAdapter( pages: List, private val loader: PageLoader ) : BaseReaderAdapter(pages) { - override fun onCreateViewHolder(parent: ViewGroup) = PageHolder(parent, loader) + override fun onCreateViewHolder(parent: ViewGroup) = ReversedPageHolder(parent, loader) override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { super.onBindViewHolder(holder, reversed(position)) diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedReaderFragment.kt index 5af1676d5..2bed0925a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/reversed/ReversedReaderFragment.kt @@ -15,6 +15,7 @@ import org.koitharu.kotatsu.ui.reader.base.ReaderPage import org.koitharu.kotatsu.ui.reader.standard.PageAnimTransformer import org.koitharu.kotatsu.ui.reader.standard.PagerPaginationListener import org.koitharu.kotatsu.utils.ext.doOnPageChanged +import org.koitharu.kotatsu.utils.ext.swapAdapter import org.koitharu.kotatsu.utils.ext.withArgs class ReversedReaderFragment : AbstractReader(R.layout.fragment_reader_standard), @@ -56,6 +57,11 @@ class ReversedReaderFragment : AbstractReader(R.layout.fragment_reader_standard) return ReversedPagesAdapter(dataSet, loader) } + override fun recreateAdapter() { + super.recreateAdapter() + pager.swapAdapter(adapter) + } + override fun getCurrentItem() = reversed(pager.currentItem) override fun setCurrentItem(position: Int, isSmooth: Boolean) { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PageHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PageHolder.kt index aae73b011..4d127639c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PageHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PageHolder.kt @@ -1,19 +1,22 @@ package org.koitharu.kotatsu.ui.reader.standard +import android.graphics.PointF import android.net.Uri import android.view.View import android.view.ViewGroup import androidx.core.view.isVisible import com.davemorrissey.labs.subscaleview.ImageSource +import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import kotlinx.android.synthetic.main.item_page.* import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.ui.base.list.BaseViewHolder import org.koitharu.kotatsu.ui.reader.PageLoader import org.koitharu.kotatsu.ui.reader.base.PageHolderDelegate import org.koitharu.kotatsu.ui.reader.base.ReaderPage import org.koitharu.kotatsu.utils.ext.getDisplayMessage -class PageHolder(parent: ViewGroup, loader: PageLoader) : +open class PageHolder(parent: ViewGroup, loader: PageLoader) : BaseViewHolder(parent, R.layout.item_page), PageHolderDelegate.Callback, View.OnClickListener { @@ -43,12 +46,40 @@ class PageHolder(parent: ViewGroup, loader: PageLoader) : ssiv.setImage(ImageSource.uri(uri)) } - override fun onImageShowing() { + override fun onImageShowing(zoom: ZoomMode) { ssiv.maxScale = 2f * maxOf( ssiv.width / ssiv.sWidth.toFloat(), ssiv.height / ssiv.sHeight.toFloat() ) - ssiv.resetScaleAndCenter() + when (zoom) { + ZoomMode.FIT_CENTER -> { + ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE) + ssiv.resetScaleAndCenter() + } + ZoomMode.FIT_HEIGHT -> { + ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM) + ssiv.minScale = ssiv.height / ssiv.sHeight.toFloat() + ssiv.setScaleAndCenter( + ssiv.minScale, + PointF(0f, ssiv.sHeight / 2f) + ) + } + ZoomMode.FIT_WIDTH -> { + ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM) + ssiv.minScale = ssiv.width / ssiv.sWidth.toFloat() + ssiv.setScaleAndCenter( + ssiv.minScale, + PointF(ssiv.sWidth / 2f, 0f) + ) + } + ZoomMode.KEEP_START -> { + ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE) + ssiv.setScaleAndCenter( + ssiv.maxScale, + PointF(0f, 0f) + ) + } + } } override fun onImageShown() { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagerReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagerReaderFragment.kt index 486ace9bb..b327bef84 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagerReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/standard/PagerReaderFragment.kt @@ -13,6 +13,7 @@ import org.koitharu.kotatsu.ui.reader.base.AbstractReader import org.koitharu.kotatsu.ui.reader.base.BaseReaderAdapter import org.koitharu.kotatsu.ui.reader.base.ReaderPage import org.koitharu.kotatsu.utils.ext.doOnPageChanged +import org.koitharu.kotatsu.utils.ext.swapAdapter import org.koitharu.kotatsu.utils.ext.withArgs class PagerReaderFragment : AbstractReader(R.layout.fragment_reader_standard), @@ -52,6 +53,11 @@ class PagerReaderFragment : AbstractReader(R.layout.fragment_reader_standard), return PagesAdapter(dataSet, loader) } + override fun recreateAdapter() { + super.recreateAdapter() + pager.swapAdapter(adapter) + } + override fun getCurrentItem() = pager.currentItem override fun setCurrentItem(position: Int, isSmooth: Boolean) { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonHolder.kt index 9dc235b98..acbc8c4a5 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonHolder.kt @@ -8,6 +8,7 @@ import com.davemorrissey.labs.subscaleview.ImageSource import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import kotlinx.android.synthetic.main.item_page_webtoon.* import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.ui.base.list.BaseViewHolder import org.koitharu.kotatsu.ui.reader.PageLoader import org.koitharu.kotatsu.ui.reader.base.PageHolderDelegate @@ -46,7 +47,7 @@ class WebtoonHolder(parent: ViewGroup, private val loader: PageLoader) : ssiv.setImage(ImageSource.uri(uri)) } - override fun onImageShowing() { + override fun onImageShowing(zoom: ZoomMode) { ssiv.maxScale = 2f * ssiv.width / ssiv.sWidth.toFloat() ssiv.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM) ssiv.minScale = ssiv.width / ssiv.sWidth.toFloat() diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonReaderFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonReaderFragment.kt index 29fd10751..bf5ba37d9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonReaderFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/reader/wetoon/WebtoonReaderFragment.kt @@ -32,6 +32,11 @@ class WebtoonReaderFragment : AbstractReader(R.layout.fragment_reader_webtoon) { return WebtoonAdapter(dataSet, loader) } + override fun recreateAdapter() { + super.recreateAdapter() + recyclerView.swapAdapter(adapter, true) + } + override fun onDestroyView() { paginationListener = null super.onDestroyView() diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/MainSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/MainSettingsFragment.kt index 71b5cc484..d759b2599 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/settings/MainSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/MainSettingsFragment.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.launch import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.ListMode import org.koitharu.kotatsu.ui.base.BasePreferenceFragment @@ -26,6 +27,7 @@ import org.koitharu.kotatsu.ui.settings.utils.MultiSummaryProvider import org.koitharu.kotatsu.ui.tracker.TrackWorker import org.koitharu.kotatsu.utils.ext.getStorageName import org.koitharu.kotatsu.utils.ext.md5 +import org.koitharu.kotatsu.utils.ext.names import org.koitharu.kotatsu.utils.ext.viewLifecycleScope import java.io.File @@ -60,6 +62,11 @@ class MainSettingsFragment : BasePreferenceFragment(R.string.settings), summary = settings.getStorageDir(context)?.getStorageName(context) ?: getString(R.string.not_available) } + findPreference(AppSettings.KEY_ZOOM_MODE)?.let { + it.entryValues = ZoomMode.values().names() + it.setDefaultValue(ZoomMode.FIT_CENTER.name) + it.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance() + } findPreference(AppSettings.KEY_PROTECT_APP)?.isChecked = !settings.appPassword.isNullOrEmpty() findPreference(AppSettings.KEY_APP_VERSION)?.run { diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/settings/ReaderSettingsFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/settings/ReaderSettingsFragment.kt index 130b39341..ba37ccd50 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/settings/ReaderSettingsFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/settings/ReaderSettingsFragment.kt @@ -2,11 +2,14 @@ package org.koitharu.kotatsu.ui.settings import android.os.Bundle import android.view.View +import androidx.preference.ListPreference import androidx.preference.MultiSelectListPreference import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.ZoomMode import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.ui.base.BasePreferenceFragment import org.koitharu.kotatsu.ui.settings.utils.MultiSummaryProvider +import org.koitharu.kotatsu.utils.ext.names class ReaderSettingsFragment : BasePreferenceFragment(R.string.reader_settings) { @@ -19,5 +22,10 @@ class ReaderSettingsFragment : BasePreferenceFragment(R.string.reader_settings) findPreference(AppSettings.KEY_READER_SWITCHERS)?.let { it.summaryProvider = MultiSummaryProvider(R.string.gestures_only) } + findPreference(AppSettings.KEY_ZOOM_MODE)?.let { + it.entryValues = ZoomMode.values().names() + it.setDefaultValue(ZoomMode.FIT_CENTER.name) + it.summaryProvider = ListPreference.SimpleSummaryProvider.getInstance() + } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/delegates/prefs/EnumPreferenceDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/utils/delegates/prefs/EnumPreferenceDelegate.kt index 646c1db10..b164dcb8e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/delegates/prefs/EnumPreferenceDelegate.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/delegates/prefs/EnumPreferenceDelegate.kt @@ -9,20 +9,19 @@ class EnumPreferenceDelegate>( private val cls: Class, private val key: String, private val defValue: E -) : - ReadWriteProperty { +) : ReadWriteProperty { override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): E { - val ord = thisRef.getInt(key, -1) - if (ord == -1) { + val name = thisRef.getString(key, null) + if (name === null) { return defValue } - return cls.enumConstants?.firstOrNull { it.ordinal == ord } ?: defValue + return cls.enumConstants?.find { it.name == name } ?: defValue } override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: E) { thisRef.edit { - putInt(key, value.ordinal) + putString(key, value.name) } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/delegates/prefs/IntEnumPreferenceDelegate.kt b/app/src/main/java/org/koitharu/kotatsu/utils/delegates/prefs/IntEnumPreferenceDelegate.kt new file mode 100644 index 000000000..cc3dc96f1 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/utils/delegates/prefs/IntEnumPreferenceDelegate.kt @@ -0,0 +1,28 @@ +package org.koitharu.kotatsu.utils.delegates.prefs + +import android.content.SharedPreferences +import androidx.core.content.edit +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +@Deprecated("") +class IntEnumPreferenceDelegate>( + private val cls: Class, + private val key: String, + private val defValue: E +) : ReadWriteProperty { + + override fun getValue(thisRef: SharedPreferences, property: KProperty<*>): E { + val ord = thisRef.getInt(key, -1) + if (ord == -1) { + return defValue + } + return cls.enumConstants?.getOrNull(ord) ?: defValue + } + + override fun setValue(thisRef: SharedPreferences, property: KProperty<*>, value: E) { + thisRef.edit { + putInt(key, value.ordinal) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CollectionExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CollectionExt.kt index f1e895bf0..29ff62f07 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CollectionExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CollectionExt.kt @@ -66,4 +66,10 @@ inline fun Collection.associateByLong(selector: (T) -> Long): LongSparseA result.put(selector(item), item) } return result -} \ No newline at end of file +} + +inline fun Array.mapToArray(transform: (T) -> R): Array = Array(size) { i -> + transform(get(i)) +} + +fun > Array.names() = mapToArray { it.name } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt index da85fc752..8f31764f8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/ViewExt.kt @@ -200,4 +200,12 @@ fun RecyclerView.findCenterViewPosition(): Int { val centerY = height / 2f val view = findChildViewUnder(centerX, centerY) ?: return RecyclerView.NO_POSITION return getChildAdapterPosition(view) +} + +fun ViewPager2.swapAdapter(newAdapter: RecyclerView.Adapter<*>?) { + val position = currentItem + adapter = newAdapter + if (adapter != null && position != RecyclerView.NO_POSITION) { + currentItem = position + } } \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 07453c1cf..c89b6adc3 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -163,4 +163,9 @@ Предпочитать режим Справа налево Вы можете настроить режим чтения для каждой манги отдельно Создать категорию + Масштабирование + Вписать в экран + Подогнать по высоте + Подогнать по ширине + Исходный размер \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index ff41472f7..5cb3fd8b2 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -9,6 +9,12 @@ @string/taps_on_edges @string/volume_buttons + + @string/zoom_mode_fit_center + @string/zoom_mode_fit_height + @string/zoom_mode_fit_width + @string/zoom_mode_keep_start + @string/favourites @string/history diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5bac0e792..9e7d47704 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -165,4 +165,9 @@ You can set up the reading mode for each manga separately New category Create issue on GitHub + Scale mode + Fit center + Fit to height + Fit to width + Keep at start \ No newline at end of file diff --git a/app/src/main/res/xml/pref_main.xml b/app/src/main/res/xml/pref_main.xml index 6e9183c72..25f665614 100644 --- a/app/src/main/res/xml/pref_main.xml +++ b/app/src/main/res/xml/pref_main.xml @@ -70,6 +70,12 @@ android:title="@string/pages_animation" app:iconSpaceReserved="false" /> + + + +