Option to prefer Rtl reader
This commit is contained in:
@@ -70,7 +70,7 @@ dependencies {
|
||||
implementation 'androidx.activity:activity-ktx:1.2.0-beta01'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.3.0-beta01'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-beta01'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.0-alpha06'
|
||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01'
|
||||
|
||||
@@ -88,6 +88,11 @@ class AppSettings private constructor(resources: Resources, private val prefs: S
|
||||
false
|
||||
)
|
||||
|
||||
val isPreferRtlReader by BoolPreferenceDelegate(
|
||||
resources.getString(R.string.key_reader_prefer_rtl),
|
||||
false
|
||||
)
|
||||
|
||||
val trackSources by StringSetPreferenceDelegate(
|
||||
resources.getString(R.string.key_track_sources),
|
||||
arraySetOf(TRACK_FAVOURITES, TRACK_HISTORY)
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.koitharu.kotatsu.core.prefs
|
||||
|
||||
enum class ReaderMode(val id: Int) {
|
||||
|
||||
UNKNOWN(0),
|
||||
STANDARD(1),
|
||||
WEBTOON(2),
|
||||
REVERSED(3);
|
||||
|
||||
@@ -10,7 +10,6 @@ import org.koin.core.component.KoinComponent
|
||||
import org.koin.core.component.get
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.utils.ext.await
|
||||
import org.koitharu.kotatsu.utils.ext.medianOrNull
|
||||
import java.io.InputStream
|
||||
@@ -24,7 +23,7 @@ object MangaUtils : KoinComponent {
|
||||
*/
|
||||
@WorkerThread
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
suspend fun determineReaderMode(pages: List<MangaPage>): ReaderMode? {
|
||||
suspend fun determineMangaIsWebtoon(pages: List<MangaPage>): Boolean? {
|
||||
try {
|
||||
val page = pages.medianOrNull() ?: return null
|
||||
val url = page.source.repository.getPageFullUrl(page)
|
||||
@@ -45,10 +44,7 @@ object MangaUtils : KoinComponent {
|
||||
getBitmapSize(it.body?.byteStream())
|
||||
}
|
||||
}
|
||||
return when {
|
||||
size.width * 2 < size.height -> ReaderMode.WEBTOON
|
||||
else -> ReaderMode.STANDARD
|
||||
}
|
||||
return size.width * 2 < size.height
|
||||
} catch (e: Exception) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
e.printStackTrace()
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package org.koitharu.kotatsu.ui.base
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.LayoutRes
|
||||
import coil.ImageLoader
|
||||
import moxy.MvpAppCompatFragment
|
||||
import org.koin.android.ext.android.inject
|
||||
import org.koitharu.kotatsu.utils.delegates.ParcelableArgumentDelegate
|
||||
import org.koitharu.kotatsu.utils.delegates.StringArgumentDelegate
|
||||
|
||||
abstract class BaseFragment(
|
||||
@LayoutRes contentLayoutId: Int
|
||||
@@ -15,11 +12,6 @@ abstract class BaseFragment(
|
||||
|
||||
protected val coil by inject<ImageLoader>()
|
||||
|
||||
fun stringArg(name: String) = StringArgumentDelegate(name)
|
||||
|
||||
@Deprecated("Use extension", replaceWith = ReplaceWith("parcelableArgument(name)"))
|
||||
fun <T : Parcelable> arg(name: String) = ParcelableArgumentDelegate<T>(name)
|
||||
|
||||
open fun getTitle(): CharSequence? = null
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
|
||||
@@ -125,7 +125,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
|
||||
replace(R.id.container, ReversedReaderFragment.newInstance(state))
|
||||
}
|
||||
}
|
||||
else -> if (currentReader !is PagerReaderFragment) {
|
||||
ReaderMode.STANDARD -> if (currentReader !is PagerReaderFragment) {
|
||||
supportFragmentManager.commit {
|
||||
replace(R.id.container, PagerReaderFragment.newInstance(state))
|
||||
}
|
||||
@@ -135,7 +135,7 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
|
||||
when (mode) {
|
||||
ReaderMode.WEBTOON -> R.drawable.ic_script
|
||||
ReaderMode.REVERSED -> R.drawable.ic_read_reversed
|
||||
else -> R.drawable.ic_book_page
|
||||
ReaderMode.STANDARD -> R.drawable.ic_book_page
|
||||
}
|
||||
)
|
||||
appbar_top.postDelayed(1000) {
|
||||
@@ -158,70 +158,70 @@ class ReaderActivity : BaseFullscreenActivity(), ReaderView, ChaptersDialog.OnCh
|
||||
outState.putParcelable(EXTRA_STATE, state)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.action_reader_mode -> {
|
||||
ReaderConfigDialog.show(
|
||||
supportFragmentManager, when (reader) {
|
||||
is PagerReaderFragment -> ReaderMode.STANDARD
|
||||
is WebtoonReaderFragment -> ReaderMode.WEBTOON
|
||||
is ReversedReaderFragment -> ReaderMode.REVERSED
|
||||
else -> ReaderMode.UNKNOWN
|
||||
}
|
||||
)
|
||||
true
|
||||
}
|
||||
R.id.action_settings -> {
|
||||
startActivity(SimpleSettingsActivity.newReaderSettingsIntent(this))
|
||||
true
|
||||
}
|
||||
R.id.action_chapters -> {
|
||||
ChaptersDialog.show(
|
||||
supportFragmentManager,
|
||||
state.manga.chapters.orEmpty(),
|
||||
state.chapterId
|
||||
)
|
||||
true
|
||||
}
|
||||
R.id.action_screen_rotate -> {
|
||||
orientationHelper.toggleOrientation()
|
||||
true
|
||||
}
|
||||
R.id.action_pages_thumbs -> {
|
||||
if (reader?.hasItems == true) {
|
||||
val pages = reader?.getPages()
|
||||
if (!pages.isNullOrEmpty()) {
|
||||
PagesThumbnailsSheet.show(
|
||||
supportFragmentManager, pages,
|
||||
state.chapter?.name ?: title?.toString().orEmpty()
|
||||
)
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_reader_mode -> {
|
||||
ReaderConfigDialog.show(
|
||||
supportFragmentManager, when (reader) {
|
||||
is PagerReaderFragment -> ReaderMode.STANDARD
|
||||
is WebtoonReaderFragment -> ReaderMode.WEBTOON
|
||||
is ReversedReaderFragment -> ReaderMode.REVERSED
|
||||
else -> {
|
||||
showWaitWhileLoading()
|
||||
return false
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
R.id.action_settings -> {
|
||||
startActivity(SimpleSettingsActivity.newReaderSettingsIntent(this))
|
||||
}
|
||||
R.id.action_chapters -> {
|
||||
ChaptersDialog.show(
|
||||
supportFragmentManager,
|
||||
state.manga.chapters.orEmpty(),
|
||||
state.chapterId
|
||||
)
|
||||
}
|
||||
R.id.action_screen_rotate -> {
|
||||
orientationHelper.toggleOrientation()
|
||||
}
|
||||
R.id.action_pages_thumbs -> {
|
||||
if (reader?.hasItems == true) {
|
||||
val pages = reader?.getPages()
|
||||
if (!pages.isNullOrEmpty()) {
|
||||
PagesThumbnailsSheet.show(
|
||||
supportFragmentManager, pages,
|
||||
state.chapter?.name ?: title?.toString().orEmpty()
|
||||
)
|
||||
} else {
|
||||
showWaitWhileLoading()
|
||||
}
|
||||
} else {
|
||||
showWaitWhileLoading()
|
||||
}
|
||||
} else {
|
||||
showWaitWhileLoading()
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.action_save_page -> {
|
||||
if (reader?.hasItems == true) {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
onActivityResult(true)
|
||||
R.id.action_save_page -> {
|
||||
if (reader?.hasItems == true) {
|
||||
if (ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
onActivityResult(true)
|
||||
} else {
|
||||
registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission(),
|
||||
this
|
||||
).launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
} else {
|
||||
registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission(),
|
||||
this
|
||||
).launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
showWaitWhileLoading()
|
||||
}
|
||||
} else {
|
||||
showWaitWhileLoading()
|
||||
}
|
||||
true
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActivityResult(result: Boolean) {
|
||||
|
||||
@@ -18,9 +18,8 @@ class ReaderConfigDialog : AlertDialogFragment(R.layout.dialog_reader_config),
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mode = arguments?.getInt(ARG_MODE, ReaderMode.UNKNOWN.id)
|
||||
mode = arguments?.getInt(ARG_MODE)
|
||||
?.let { ReaderMode.valueOf(it) }
|
||||
?.takeUnless { it == ReaderMode.UNKNOWN }
|
||||
?: ReaderMode.STANDARD
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.koin.core.component.inject
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaPage
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderMode
|
||||
import org.koitharu.kotatsu.domain.MangaDataRepository
|
||||
import org.koitharu.kotatsu.domain.MangaUtils
|
||||
@@ -28,27 +29,29 @@ import org.koitharu.kotatsu.utils.ext.mimeType
|
||||
class ReaderPresenter : BasePresenter<ReaderView>() {
|
||||
|
||||
private val dataRepository by inject<MangaDataRepository>()
|
||||
private val appSettings by inject<AppSettings>()
|
||||
|
||||
fun init(manga: Manga) {
|
||||
presenterScope.launch {
|
||||
viewState.onLoadingStateChanged(isLoading = true)
|
||||
try {
|
||||
val mode = withContext(Dispatchers.IO) {
|
||||
val mode = withContext(Dispatchers.Default) {
|
||||
val repo = manga.source.repository
|
||||
val chapter =
|
||||
(manga.chapters ?: throw RuntimeException("Chapters is null")).random()
|
||||
var mode = dataRepository.getReaderMode(manga.id)
|
||||
if (mode == null) {
|
||||
val pages = repo.getPages(chapter)
|
||||
mode = MangaUtils.determineReaderMode(pages)
|
||||
if (mode != null) {
|
||||
val isWebtoon = MangaUtils.determineMangaIsWebtoon(pages)
|
||||
mode = getReaderMode(isWebtoon)
|
||||
if (isWebtoon != null) {
|
||||
dataRepository.savePreferences(
|
||||
manga = manga,
|
||||
mode = mode
|
||||
)
|
||||
}
|
||||
}
|
||||
mode ?: ReaderMode.UNKNOWN
|
||||
mode
|
||||
}
|
||||
viewState.onInitReader(manga, mode)
|
||||
} catch (_: CancellationException) {
|
||||
@@ -101,6 +104,12 @@ class ReaderPresenter : BasePresenter<ReaderView>() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getReaderMode(isWebtoon: Boolean?) = when {
|
||||
isWebtoon == true -> ReaderMode.WEBTOON
|
||||
appSettings.isPreferRtlReader -> ReaderMode.REVERSED
|
||||
else -> ReaderMode.STANDARD
|
||||
}
|
||||
|
||||
companion object : KoinComponent {
|
||||
|
||||
fun saveState(state: ReaderState) {
|
||||
|
||||
@@ -3,14 +3,16 @@ package org.koitharu.kotatsu.ui.search
|
||||
import moxy.ktx.moxyPresenter
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.ui.list.MangaListFragment
|
||||
import org.koitharu.kotatsu.utils.ext.parcelableArgument
|
||||
import org.koitharu.kotatsu.utils.ext.stringArgument
|
||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||
|
||||
class SearchFragment : MangaListFragment<Unit>() {
|
||||
|
||||
private val presenter by moxyPresenter(factory = ::SearchPresenter)
|
||||
|
||||
private val query by stringArg(ARG_QUERY)
|
||||
private val source by arg<MangaSource>(ARG_SOURCE)
|
||||
private val query by stringArgument(ARG_QUERY)
|
||||
private val source by parcelableArgument<MangaSource>(ARG_SOURCE)
|
||||
|
||||
override fun onRequestMoreItems(offset: Int) {
|
||||
presenter.loadList(source, query.orEmpty(), offset)
|
||||
|
||||
@@ -2,14 +2,15 @@ package org.koitharu.kotatsu.ui.search.global
|
||||
|
||||
import moxy.ktx.moxyPresenter
|
||||
import org.koitharu.kotatsu.ui.list.MangaListFragment
|
||||
import org.koitharu.kotatsu.utils.ext.stringArgument
|
||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||
|
||||
|
||||
class GlobalSearchFragment: MangaListFragment<Unit>() {
|
||||
class GlobalSearchFragment : MangaListFragment<Unit>() {
|
||||
|
||||
private val presenter by moxyPresenter(factory = ::GlobalSearchPresenter)
|
||||
|
||||
private val query by stringArg(ARG_QUERY)
|
||||
private val query by stringArgument(ARG_QUERY)
|
||||
|
||||
override fun onRequestMoreItems(offset: Int) {
|
||||
if (offset == 0) {
|
||||
|
||||
@@ -20,4 +20,9 @@ inline fun <T : Parcelable> Fragment.parcelableArgument(name: String) =
|
||||
lazy<T>(LazyThreadSafetyMode.NONE) {
|
||||
requireArguments().getParcelable(name)
|
||||
?: error("No argument $name passed in ${javaClass.simpleName}")
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun Fragment.stringArgument(name: String) = lazy(LazyThreadSafetyMode.NONE) {
|
||||
arguments?.getString(name)
|
||||
}
|
||||
@@ -160,4 +160,6 @@
|
||||
<string name="update_check_failed">Ошибка при проверке обновления</string>
|
||||
<string name="no_update_available">Нет доступных обновлений</string>
|
||||
<string name="right_to_left">Справа налево</string>
|
||||
<string name="prefer_rtl_reader">Предпочитать режим Справа налево</string>
|
||||
<string name="prefer_rtl_reader_summary">Вы можете настроить режим чтения для каждой манги отдельно</string>
|
||||
</resources>
|
||||
@@ -23,6 +23,7 @@
|
||||
<string name="key_notifications_vibrate">notifications_vibrate</string>
|
||||
<string name="key_notifications_light">notifications_light</string>
|
||||
<string name="key_reader_animation">reader_animation</string>
|
||||
<string name="key_reader_prefer_rtl">reader_prefer_rtl</string>
|
||||
<string name="key_app_password">app_password</string>
|
||||
<string name="key_protect_app">protect_app</string>
|
||||
<string name="key_app_version">app_version</string>
|
||||
|
||||
@@ -161,4 +161,6 @@
|
||||
<string name="update_check_failed">Update check failed</string>
|
||||
<string name="no_update_available">No updates available</string>
|
||||
<string name="right_to_left">Right to left</string>
|
||||
<string name="prefer_rtl_reader">Prefer Right to left reader</string>
|
||||
<string name="prefer_rtl_reader_summary">You can set up the reading mode for each manga separately</string>
|
||||
</resources>
|
||||
@@ -70,6 +70,13 @@
|
||||
android:title="@string/pages_animation"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="@string/key_reader_prefer_rtl"
|
||||
android:summary="@string/prefer_rtl_reader_summary"
|
||||
android:title="@string/prefer_rtl_reader"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:title="@string/new_chapters"
|
||||
app:allowDividerAbove="true"
|
||||
|
||||
@@ -13,8 +13,15 @@
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:title="@string/pages_animation"
|
||||
android:key="@string/key_reader_animation"
|
||||
android:title="@string/pages_animation"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="@string/key_reader_prefer_rtl"
|
||||
android:summary="@string/prefer_rtl_reader_summary"
|
||||
android:title="@string/prefer_rtl_reader"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
Reference in New Issue
Block a user