diff --git a/app/build.gradle b/app/build.gradle index faa3a2bf8..7bf81f718 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -82,7 +82,7 @@ afterEvaluate { } dependencies { //noinspection GradleDependency - implementation('com.github.KotatsuApp:kotatsu-parsers:7ed8c9f787') { + implementation('com.github.KotatsuApp:kotatsu-parsers:f923acc5a7') { exclude group: 'org.json', module: 'json' } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/AlternativesUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/AlternativesUseCase.kt index 6985bad9a..a993eb31c 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/AlternativesUseCase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/AlternativesUseCase.kt @@ -12,6 +12,7 @@ import org.koitharu.kotatsu.core.util.ext.almostEquals import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaListFilter +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import javax.inject.Inject @@ -57,7 +58,7 @@ class AlternativesUseCase @Inject constructor( } private suspend fun getSources(ref: MangaSource): List { - val result = ArrayList(MangaSource.entries.size - 2) + val result = ArrayList(MangaParserSource.entries.size - 2) result.addAll(sourcesRepository.getEnabledSources()) result.sortByDescending { it.priority(ref) } result.addAll(sourcesRepository.getDisabledSources().sortedByDescending { it.priority(ref) }) @@ -78,8 +79,10 @@ class AlternativesUseCase @Inject constructor( private fun MangaSource.priority(ref: MangaSource): Int { var res = 0 - if (locale == ref.locale) res += 2 - if (contentType == ref.contentType) res++ + if (this is MangaParserSource && ref is MangaParserSource) { + if (locale == ref.locale) res += 2 + if (contentType == ref.contentType) res++ + } return res } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt index fa467541e..3622b2488 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt @@ -10,6 +10,7 @@ import coil.request.ImageRequest import coil.transform.CircleCropTransformation import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.parser.favicon.faviconUri import org.koitharu.kotatsu.core.ui.image.ChipIconTarget import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver @@ -63,7 +64,7 @@ fun alternativeAD( } binding.progressView.setPercent(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads) binding.chipSource.also { chip -> - chip.text = item.manga.source.title + chip.text = item.manga.source.getTitle(chip.context) ImageRequest.Builder(context) .data(item.manga.source.faviconUri()) .lifecycle(lifecycleOwner) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt index 03d099a54..aefe5ce3b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt @@ -13,6 +13,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.parser.MangaIntent import org.koitharu.kotatsu.core.ui.BaseActivity @@ -95,9 +96,9 @@ class AlternativesActivity : BaseActivity(), getString( R.string.migrate_confirmation, viewModel.manga.title, - viewModel.manga.source.title, + viewModel.manga.source.getTitle(this), target.title, - target.source.title, + target.source.getTitle(this), ), ) .setNegativeButton(android.R.string.cancel, null) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/browser/BrowserActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/browser/BrowserActivity.kt index 04f71e3bd..56c6bd76a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/browser/BrowserActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/browser/BrowserActivity.kt @@ -12,13 +12,14 @@ import androidx.core.graphics.Insets import androidx.core.view.isVisible import androidx.core.view.updatePadding import dagger.hilt.android.AndroidEntryPoint +import okhttp3.internal.userAgent import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.util.ext.configureForParser -import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat import org.koitharu.kotatsu.core.util.ext.toUriOrNull import org.koitharu.kotatsu.databinding.ActivityBrowserBinding import org.koitharu.kotatsu.parsers.model.MangaSource @@ -42,10 +43,9 @@ class BrowserActivity : BaseActivity(), BrowserCallback setDisplayHomeAsUpEnabled(true) setHomeAsUpIndicator(materialR.drawable.abc_ic_clear_material) } - val userAgent = intent?.getSerializableExtraCompat(EXTRA_SOURCE)?.let { source -> - val repository = mangaRepositoryFactory.create(source) as? RemoteMangaRepository - repository?.headers?.get(CommonHeaders.USER_AGENT) - } + val mangaSource = MangaSource(intent?.getStringExtra(EXTRA_SOURCE)) + val repository = mangaRepositoryFactory.create(mangaSource) as? RemoteMangaRepository + repository?.headers?.get(CommonHeaders.USER_AGENT) viewBinding.webView.configureForParser(userAgent) CookieManager.getInstance().setAcceptThirdPartyCookies(viewBinding.webView, true) viewBinding.webView.webViewClient = BrowserClient(this) @@ -147,7 +147,7 @@ class BrowserActivity : BaseActivity(), BrowserCallback return Intent(context, BrowserActivity::class.java) .setData(Uri.parse(url)) .putExtra(EXTRA_TITLE, title) - .putExtra(EXTRA_SOURCE, source) + .putExtra(EXTRA_SOURCE, source?.name) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt index ea6d2db10..83318f8ea 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/browser/cloudflare/CaptchaNotifier.kt @@ -14,8 +14,9 @@ import coil.request.ErrorResult import coil.request.ImageRequest import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException +import org.koitharu.kotatsu.core.model.getTitle +import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission -import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaSource class CaptchaNotifier( @@ -46,7 +47,7 @@ class CaptchaNotifier( .setGroup(GROUP_CAPTCHA) .setAutoCancel(true) .setVisibility( - if (exception.source?.contentType == ContentType.HENTAI) { + if (exception.source?.isNsfw() == true) { NotificationCompat.VISIBILITY_SECRET } else { NotificationCompat.VISIBILITY_PUBLIC @@ -55,7 +56,7 @@ class CaptchaNotifier( .setContentText( context.getString( R.string.captcha_required_summary, - exception.source?.title ?: context.getString(R.string.app_name), + exception.source?.getTitle(context) ?: context.getString(R.string.app_name), ), ) .setContentIntent(PendingIntentCompat.getActivity(context, 0, intent, 0, false)) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/migrations/Migration16To17.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/migrations/Migration16To17.kt index e49a9b1ee..7e58a5bc5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/db/migrations/Migration16To17.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/db/migrations/Migration16To17.kt @@ -4,7 +4,7 @@ import android.content.Context import androidx.preference.PreferenceManager import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase -import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaParserSource class Migration16To17(context: Context) : Migration(16, 17) { @@ -15,11 +15,8 @@ class Migration16To17(context: Context) : Migration(16, 17) { db.execSQL("CREATE INDEX `index_sources_sort_key` ON `sources` (`sort_key`)") val hiddenSources = prefs.getStringSet("sources_hidden", null).orEmpty() val order = prefs.getString("sources_order_2", null)?.split('|').orEmpty() - val sources = MangaSource.entries + val sources = MangaParserSource.entries for (source in sources) { - if (source == MangaSource.LOCAL) { - continue - } val name = source.name val isHidden = name in hiddenSources var sortKey = order.indexOf(name) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt index 8ca03088a..8a3674fa8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt @@ -11,7 +11,6 @@ import org.koitharu.kotatsu.details.ui.model.ChapterListItem import org.koitharu.kotatsu.parsers.model.ContentRating import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.util.formatSimple import org.koitharu.kotatsu.parsers.util.mapToSet @@ -109,7 +108,7 @@ fun Manga.getPreferredBranch(history: MangaHistory?): String? { } val Manga.isLocal: Boolean - get() = source == MangaSource.LOCAL + get() = source == LocalMangaSource val Manga.appUrl: Uri get() = Uri.parse("https://kotatsu.app/manga").buildUpon() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt index 1de70d7c2..6c101ab82 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt @@ -7,24 +7,41 @@ import android.text.style.ForegroundColorSpan import android.text.style.RelativeSizeSpan import android.text.style.SuperscriptSpan import androidx.annotation.StringRes -import androidx.core.text.buildSpannedString import androidx.core.text.inSpans import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.util.ext.getDisplayName import org.koitharu.kotatsu.core.util.ext.getThemeColor import org.koitharu.kotatsu.core.util.ext.toLocale import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaSource import com.google.android.material.R as materialR -fun MangaSource(name: String): MangaSource { - MangaSource.entries.forEach { - if (it.name == name) return it - } - return MangaSource.UNKNOWN +data object LocalMangaSource : MangaSource { + override val name = "LOCAL" } -fun MangaSource.isNsfw() = contentType == ContentType.HENTAI +data object UnknownMangaSource : MangaSource { + override val name = "UNKNOWN" +} + +fun MangaSource(name: String?): MangaSource { + when (name) { + null, + UnknownMangaSource.name -> UnknownMangaSource + + LocalMangaSource.name -> LocalMangaSource + } + MangaParserSource.entries.forEach { + if (it.name == name) return it + } + return UnknownMangaSource +} + +fun MangaSource.isNsfw() = when (this) { + is MangaParserSource -> contentType == ContentType.HENTAI + else -> false +} @get:StringRes val ContentType.titleResId @@ -35,23 +52,23 @@ val ContentType.titleResId ContentType.OTHER -> R.string.content_type_other } -fun MangaSource.getSummary(context: Context): String { - val type = context.getString(contentType.titleResId) - val locale = locale.toLocale().getDisplayName(context) - return context.getString(R.string.source_summary_pattern, type, locale) -} - -fun MangaSource.getTitle(context: Context): CharSequence = if (isNsfw()) { - buildSpannedString { - append(title) - append(' ') - appendNsfwLabel(context) +fun MangaSource.getSummary(context: Context): String? = when (this) { + is MangaParserSource -> { + val type = context.getString(contentType.titleResId) + val locale = locale.toLocale().getDisplayName(context) + context.getString(R.string.source_summary_pattern, type, locale) } -} else { - title + + else -> null } -private fun SpannableStringBuilder.appendNsfwLabel(context: Context) = inSpans( +fun MangaSource.getTitle(context: Context): String = when (this) { + is MangaParserSource -> title + LocalMangaSource -> context.getString(R.string.local_storage) + else -> context.getString(R.string.unknown) +} + +fun SpannableStringBuilder.appendNsfwLabel(context: Context) = inSpans( ForegroundColorSpan(context.getThemeColor(materialR.attr.colorError, Color.RED)), RelativeSizeSpan(0.74f), SuperscriptSpan(), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/MangaSourceParceler.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/MangaSourceParceler.kt new file mode 100644 index 000000000..f18fca67d --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/MangaSourceParceler.kt @@ -0,0 +1,15 @@ +package org.koitharu.kotatsu.core.model.parcelable + +import android.os.Parcel +import kotlinx.parcelize.Parceler +import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaSource + +class MangaSourceParceler : Parceler { + + override fun create(parcel: Parcel): MangaSource = MangaSource(parcel.readString()) + + override fun MangaSource.write(parcel: Parcel, flags: Int) { + parcel.writeString(name) + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableChapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableChapter.kt index 8d47965db..148060125 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableChapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableChapter.kt @@ -4,9 +4,8 @@ import android.os.Parcel import android.os.Parcelable import kotlinx.parcelize.Parceler import kotlinx.parcelize.Parcelize -import org.koitharu.kotatsu.core.util.ext.readSerializableCompat +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaChapter -import org.koitharu.kotatsu.parsers.model.MangaSource @Parcelize data class ParcelableChapter( @@ -25,8 +24,8 @@ data class ParcelableChapter( scanlator = parcel.readString(), uploadDate = parcel.readLong(), branch = parcel.readString(), - source = parcel.readSerializableCompat() ?: MangaSource.UNKNOWN, - ) + source = MangaSource(parcel.readString()), + ), ) override fun ParcelableChapter.write(parcel: Parcel, flags: Int) = with(chapter) { @@ -38,7 +37,7 @@ data class ParcelableChapter( parcel.writeString(scanlator) parcel.writeLong(uploadDate) parcel.writeString(branch) - parcel.writeSerializable(source) + parcel.writeString(source.name) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableManga.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableManga.kt index c4d19a593..fb91de501 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableManga.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableManga.kt @@ -5,6 +5,7 @@ import android.os.Parcelable import androidx.core.os.ParcelCompat import kotlinx.parcelize.Parceler import kotlinx.parcelize.Parcelize +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.util.ext.readParcelableCompat import org.koitharu.kotatsu.core.util.ext.readSerializableCompat import org.koitharu.kotatsu.parsers.model.Manga @@ -30,7 +31,7 @@ data class ParcelableManga( parcel.writeParcelable(ParcelableMangaTags(tags), flags) parcel.writeSerializable(state) parcel.writeString(author) - parcel.writeSerializable(source) + parcel.writeString(source.name) } override fun create(parcel: Parcel) = ParcelableManga( @@ -49,8 +50,8 @@ data class ParcelableManga( state = parcel.readSerializableCompat(), author = parcel.readString(), chapters = null, - source = requireNotNull(parcel.readSerializableCompat()), - ) + source = MangaSource(parcel.readString()), + ), ) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableMangaPage.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableMangaPage.kt index eee936e84..94b3ce5fc 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableMangaPage.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableMangaPage.kt @@ -5,7 +5,7 @@ import android.os.Parcelable import kotlinx.parcelize.Parceler import kotlinx.parcelize.Parcelize import kotlinx.parcelize.TypeParceler -import org.koitharu.kotatsu.core.util.ext.readSerializableCompat +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaPage object MangaPageParceler : Parceler { @@ -13,14 +13,14 @@ object MangaPageParceler : Parceler { id = parcel.readLong(), url = requireNotNull(parcel.readString()), preview = parcel.readString(), - source = requireNotNull(parcel.readSerializableCompat()), + source = MangaSource(parcel.readString()), ) override fun MangaPage.write(parcel: Parcel, flags: Int) { parcel.writeLong(id) parcel.writeString(url) parcel.writeString(preview) - parcel.writeSerializable(source) + parcel.writeString(source.name) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableMangaTags.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableMangaTags.kt index 75640156a..b8fa8208f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableMangaTags.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/parcelable/ParcelableMangaTags.kt @@ -5,20 +5,20 @@ import android.os.Parcelable import kotlinx.parcelize.Parceler import kotlinx.parcelize.Parcelize import kotlinx.parcelize.TypeParceler -import org.koitharu.kotatsu.core.util.ext.readSerializableCompat +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag object MangaTagParceler : Parceler { override fun create(parcel: Parcel) = MangaTag( title = requireNotNull(parcel.readString()), key = requireNotNull(parcel.readString()), - source = requireNotNull(parcel.readSerializableCompat()), + source = MangaSource(parcel.readString()), ) override fun MangaTag.write(parcel: Parcel, flags: Int) { parcel.writeString(title) parcel.writeString(key) - parcel.writeSerializable(source) + parcel.writeString(source.name) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/MirrorSwitchInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/MirrorSwitchInterceptor.kt index efdc9564c..41beb11d7 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/MirrorSwitchInterceptor.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/MirrorSwitchInterceptor.kt @@ -15,6 +15,7 @@ import okhttp3.internal.publicsuffix.PublicSuffixDatabase import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaSource import java.util.EnumMap import javax.inject.Inject @@ -26,8 +27,8 @@ class MirrorSwitchInterceptor @Inject constructor( private val settings: AppSettings, ) : Interceptor { - private val locks = EnumMap(MangaSource::class.java) - private val blacklist = EnumMap>(MangaSource::class.java) + private val locks = EnumMap(MangaParserSource::class.java) + private val blacklist = EnumMap>(MangaParserSource::class.java) val isEnabled: Boolean get() = settings.isMirrorSwitchingAvailable @@ -145,15 +146,15 @@ class MirrorSwitchInterceptor @Inject constructor( return source().readByteArray().toResponseBody(contentType()) } - private fun obtainLock(source: MangaSource): Any = locks.getOrPut(source) { + private fun obtainLock(source: MangaParserSource): Any = locks.getOrPut(source) { Any() } - private fun isBlacklisted(source: MangaSource, domain: String): Boolean { + private fun isBlacklisted(source: MangaParserSource, domain: String): Boolean { return blacklist[source]?.contains(domain) == true } - private fun addToBlacklist(source: MangaSource, domain: String) { + private fun addToBlacklist(source: MangaParserSource, domain: String) { blacklist.getOrPut(source) { ArraySet(2) }.add(domain) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt index 986855095..71aad8f3f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.db.TABLE_HISTORY +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.parser.MangaDataRepository import org.koitharu.kotatsu.core.parser.favicon.faviconUri import org.koitharu.kotatsu.core.prefs.AppSettings @@ -173,9 +174,10 @@ class AppShortcutManager @Inject constructor( onSuccess = { IconCompat.createWithAdaptiveBitmap(it) }, onFailure = { IconCompat.createWithResource(context, R.drawable.ic_shortcut_default) }, ) + val title = source.getTitle(context) ShortcutInfoCompat.Builder(context, source.name) - .setShortLabel(source.title) - .setLongLabel(source.title) + .setShortLabel(title) + .setLongLabel(title) .setIcon(icon) .setLongLived(true) .setIntent(MangaListActivity.newIntent(context, source)) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/DummyParser.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/DummyParser.kt index 5d05b9c0a..8bad91af4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/DummyParser.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/DummyParser.kt @@ -8,7 +8,7 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder import java.util.EnumSet @@ -16,7 +16,7 @@ import java.util.EnumSet /** * This parser is just for parser development, it should not be used in releases */ -class DummyParser(context: MangaLoaderContext) : MangaParser(context, MangaSource.DUMMY) { +class DummyParser(context: MangaLoaderContext) : MangaParser(context, MangaParserSource.DUMMY) { override val configKeyDomain: ConfigKey.Domain get() = ConfigKey.Domain("localhost") diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/EmptyParser.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/EmptyMangaRepository.kt similarity index 52% rename from app/src/main/kotlin/org/koitharu/kotatsu/core/parser/EmptyParser.kt rename to app/src/main/kotlin/org/koitharu/kotatsu/core/parser/EmptyMangaRepository.kt index cfea2d7ff..f9ff0d1fb 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/EmptyParser.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/EmptyMangaRepository.kt @@ -1,38 +1,52 @@ package org.koitharu.kotatsu.core.parser import org.koitharu.kotatsu.core.exceptions.UnsupportedSourceException -import org.koitharu.kotatsu.parsers.MangaLoaderContext -import org.koitharu.kotatsu.parsers.MangaParser -import org.koitharu.kotatsu.parsers.config.ConfigKey +import org.koitharu.kotatsu.parsers.model.ContentRating import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder import java.util.EnumSet +import java.util.Locale /** * This parser is just for parser development, it should not be used in releases */ -class EmptyParser(context: MangaLoaderContext) : MangaParser(context, MangaSource.DUMMY) { +class EmptyMangaRepository(override val source: MangaSource) : MangaRepository { - override val configKeyDomain: ConfigKey.Domain - get() = ConfigKey.Domain("localhost") - - override val availableSortOrders: Set + override val sortOrders: Set get() = EnumSet.allOf(SortOrder::class.java) - - override suspend fun getDetails(manga: Manga): Manga = stub(manga) + override val states: Set + get() = emptySet() + override val contentRatings: Set + get() = emptySet() + override var defaultSortOrder: SortOrder + get() = SortOrder.NEWEST + set(value) = Unit + override val isMultipleTagsSupported: Boolean + get() = false + override val isTagsExclusionSupported: Boolean + get() = false + override val isSearchSupported: Boolean + get() = false override suspend fun getList(offset: Int, filter: MangaListFilter?): List = stub(null) + override suspend fun getDetails(manga: Manga): Manga = stub(manga) + override suspend fun getPages(chapter: MangaChapter): List = stub(null) - override suspend fun getAvailableTags(): Set = stub(null) + override suspend fun getPageUrl(page: MangaPage): String = stub(null) - override suspend fun getRelatedManga(seed: Manga): List = stub(seed) + override suspend fun getTags(): Set = stub(null) + + override suspend fun getLocales(): Set = stub(null) + + override suspend fun getRelated(seed: Manga): List = stub(seed) private fun stub(manga: Manga?): Nothing { throw UnsupportedSourceException("This manga source is not supported", manga) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt index ba2b08223..ffec4f507 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaDataRepository.kt @@ -12,6 +12,7 @@ import org.koitharu.kotatsu.core.db.entity.toEntities import org.koitharu.kotatsu.core.db.entity.toEntity import org.koitharu.kotatsu.core.db.entity.toManga import org.koitharu.kotatsu.core.db.entity.toMangaTags +import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.util.ext.toFileOrNull @@ -101,7 +102,7 @@ class MangaDataRepository @Inject constructor( suspend fun cleanupLocalManga() { val dao = db.getMangaDao() - val broken = dao.findAllBySource(MangaSource.LOCAL.name) + val broken = dao.findAllBySource(LocalMangaSource.name) .filter { x -> x.manga.url.toUri().toFileOrNull()?.exists() == false } if (broken.isNotEmpty()) { dao.delete(broken.map { it.manga }) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLinkResolver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLinkResolver.kt index f1474aaa1..71dce02f3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLinkResolver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaLinkResolver.kt @@ -4,10 +4,11 @@ import android.net.Uri import coil.request.CachePolicy import dagger.Reusable import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.core.model.UnknownMangaSource +import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.util.ext.ifNullOrEmpty import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.parsers.exception.NotFoundException -import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaSource @@ -36,7 +37,7 @@ class MangaLinkResolver @Inject constructor( require(uri.pathSegments.singleOrNull() == "manga") { "Invalid url" } val sourceName = requireNotNull(uri.getQueryParameter("source")) { "Source is not specified" } val source = MangaSource(sourceName) - require(source != MangaSource.UNKNOWN) { "Manga source $sourceName is not supported" } + require(source != UnknownMangaSource) { "Manga source $sourceName is not supported" } val repo = repositoryFactory.create(source) return repo.findExact( url = uri.getQueryParameter("url"), @@ -108,7 +109,7 @@ class MangaLinkResolver @Inject constructor( url = url, publicUrl = "", rating = 0.0f, - isNsfw = source.contentType == ContentType.HENTAI, + isNsfw = source.isNsfw(), coverUrl = "", tags = emptySet(), state = null, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaParser.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaParser.kt index 09262377c..3d12f5ecd 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaParser.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaParser.kt @@ -2,12 +2,11 @@ package org.koitharu.kotatsu.core.parser import org.koitharu.kotatsu.parsers.MangaLoaderContext import org.koitharu.kotatsu.parsers.MangaParser -import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaParserSource -fun MangaParser(source: MangaSource, loaderContext: MangaLoaderContext): MangaParser { +fun MangaParser(source: MangaParserSource, loaderContext: MangaLoaderContext): MangaParser { return when (source) { - MangaSource.UNKNOWN -> EmptyParser(loaderContext) - MangaSource.DUMMY -> DummyParser(loaderContext) + MangaParserSource.DUMMY -> DummyParser(loaderContext) else -> loaderContext.newParserInstance(source) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaRepository.kt index 72210b1e5..03c6c22df 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/MangaRepository.kt @@ -1,7 +1,10 @@ package org.koitharu.kotatsu.core.parser import androidx.annotation.AnyThread +import androidx.collection.ArrayMap import org.koitharu.kotatsu.core.cache.MemoryContentCache +import org.koitharu.kotatsu.core.model.LocalMangaSource +import org.koitharu.kotatsu.core.model.UnknownMangaSource import org.koitharu.kotatsu.core.network.MirrorSwitchInterceptor import org.koitharu.kotatsu.local.data.LocalMangaRepository import org.koitharu.kotatsu.parsers.MangaLoaderContext @@ -10,12 +13,12 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaPage +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder import java.lang.ref.WeakReference -import java.util.EnumMap import java.util.Locale import javax.inject.Inject import javax.inject.Singleton @@ -61,24 +64,35 @@ interface MangaRepository { private val mirrorSwitchInterceptor: MirrorSwitchInterceptor, ) { - private val cache = EnumMap>(MangaSource::class.java) + private val cache = ArrayMap>() @AnyThread fun create(source: MangaSource): MangaRepository { - if (source == MangaSource.LOCAL) { - return localMangaRepository + when (source) { + LocalMangaSource -> return localMangaRepository + UnknownMangaSource -> return EmptyMangaRepository(source) } cache[source]?.get()?.let { return it } return synchronized(cache) { cache[source]?.get()?.let { return it } - val repository = RemoteMangaRepository( - parser = MangaParser(source, loaderContext), - cache = contentCache, - mirrorSwitchInterceptor = mirrorSwitchInterceptor, - ) - cache[source] = WeakReference(repository) - repository + val repository = createRepository(source) + if (repository != null) { + cache[source] = WeakReference(repository) + repository + } else { + EmptyMangaRepository(source) + } } } + + private fun createRepository(source: MangaSource): MangaRepository? = when (source) { + is MangaParserSource -> RemoteMangaRepository( + parser = MangaParser(source, loaderContext), + cache = contentCache, + mirrorSwitchInterceptor = mirrorSwitchInterceptor, + ) + + else -> null + } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt index 7d7e78fa6..88b397dd4 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/RemoteMangaRepository.kt @@ -28,7 +28,7 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder @@ -46,7 +46,7 @@ class RemoteMangaRepository( private val relatedMangaMutex = MultiMutex() private val pagesMutex = MultiMutex() - override val source: MangaSource + override val source: MangaParserSource get() = parser.source override val sortOrders: Set diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt index d024d94d1..3eb10f798 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/parser/favicon/FaviconFetcher.kt @@ -46,7 +46,7 @@ class FaviconFetcher( ) : Fetcher { private val diskCacheKey - get() = options.diskCacheKey ?: "${mangaSource.name}[${mangaSource.ordinal}]x${options.size.toCacheKey()}" + get() = options.diskCacheKey ?: "${mangaSource.name}x${options.size.toCacheKey()}" private val fileSystem get() = checkNotNull(diskCache.value).fileSystem diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/service/MangaPrefetchService.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/service/MangaPrefetchService.kt index 471f4aa32..1caca8498 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/service/MangaPrefetchService.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/service/MangaPrefetchService.kt @@ -5,7 +5,9 @@ import android.content.Intent import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.EntryPointAccessors import org.koitharu.kotatsu.core.cache.MemoryContentCache +import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.model.findById +import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.model.parcelable.ParcelableChapter import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.parser.MangaRepository @@ -62,7 +64,7 @@ class MangaPrefetchService : CoroutineIntentService() { private suspend fun prefetchLast() { val last = historyRepository.getLastOrNull() ?: return - if (last.source == MangaSource.LOCAL) return + if (last.isLocal) return val repo = mangaRepositoryFactory.create(last.source) val details = runCatchingCancellable { repo.getDetails(last) }.getOrNull() ?: return val chapters = details.chapters @@ -110,7 +112,7 @@ class MangaPrefetchService : CoroutineIntentService() { } private fun isPrefetchAvailable(context: Context, source: MangaSource?): Boolean { - if (source == MangaSource.LOCAL || context.isPowerSaveMode()) { + if (source == LocalMangaSource || context.isPowerSaveMode()) { return false } val entryPoint = EntryPointAccessors.fromApplication( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt index c62e78451..af11b86e3 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsActivity.kt @@ -43,6 +43,9 @@ import kotlinx.coroutines.flow.map import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.domain.Bookmark import org.koitharu.kotatsu.core.model.FavouriteCategory +import org.koitharu.kotatsu.core.model.LocalMangaSource +import org.koitharu.kotatsu.core.model.UnknownMangaSource +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.model.iconResId import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.model.titleResId @@ -94,7 +97,6 @@ import org.koitharu.kotatsu.list.ui.model.MangaItemModel import org.koitharu.kotatsu.list.ui.size.StaticItemSizeResolver import org.koitharu.kotatsu.local.ui.info.LocalInfoDialog import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.util.ellipsize import org.koitharu.kotatsu.reader.ui.ReaderActivity @@ -463,10 +465,10 @@ class DetailsActivity : imageViewState.isVisible = false } - if (manga.source == MangaSource.LOCAL || manga.source == MangaSource.UNKNOWN) { + if (manga.source == LocalMangaSource || manga.source == UnknownMangaSource) { infoLayout.chipSource.isVisible = false } else { - infoLayout.chipSource.text = manga.source.title + infoLayout.chipSource.text = manga.source.getTitle(this@DetailsActivity) infoLayout.chipSource.isVisible = true } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt index 87ef10aa0..c37f5813e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/DetailsMenuProvider.kt @@ -16,11 +16,12 @@ import kotlinx.coroutines.launch import org.koitharu.kotatsu.R import org.koitharu.kotatsu.alternatives.ui.AlternativesActivity import org.koitharu.kotatsu.browser.BrowserActivity +import org.koitharu.kotatsu.core.model.LocalMangaSource +import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.os.AppShortcutManager import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.util.ShareHelper import org.koitharu.kotatsu.download.ui.dialog.DownloadOption -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet import org.koitharu.kotatsu.search.ui.multi.MultiSearchActivity import org.koitharu.kotatsu.stats.ui.sheet.MangaStatsSheet @@ -38,10 +39,10 @@ class DetailsMenuProvider( override fun onPrepareMenu(menu: Menu) { val manga = viewModel.manga.value - menu.findItem(R.id.action_save).isVisible = manga?.source != null && manga.source != MangaSource.LOCAL - menu.findItem(R.id.action_delete).isVisible = manga?.source == MangaSource.LOCAL - menu.findItem(R.id.action_browser).isVisible = manga?.source != MangaSource.LOCAL - menu.findItem(R.id.action_alternatives).isVisible = manga?.source != MangaSource.LOCAL + menu.findItem(R.id.action_save).isVisible = manga?.source != null && manga.source != LocalMangaSource + menu.findItem(R.id.action_delete).isVisible = manga?.source == LocalMangaSource + menu.findItem(R.id.action_browser).isVisible = manga?.source != LocalMangaSource + menu.findItem(R.id.action_alternatives).isVisible = manga?.source != LocalMangaSource menu.findItem(R.id.action_shortcut).isVisible = ShortcutManagerCompat.isRequestPinShortcutSupported(activity) menu.findItem(R.id.action_scrobbling).isVisible = viewModel.isScrobblingAvailable menu.findItem(R.id.action_online).isVisible = viewModel.remoteManga.value != null @@ -53,7 +54,7 @@ class DetailsMenuProvider( R.id.action_share -> { viewModel.manga.value?.let { val shareHelper = ShareHelper(activity) - if (it.source == MangaSource.LOCAL) { + if (it.isLocal) { shareHelper.shareCbz(listOf(it.url.toUri().toFile())) } else { shareHelper.shareMangaLink(it) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt index 764f87738..1e5b2da24 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/ui/pager/chapters/ChaptersFragment.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener @@ -40,7 +41,6 @@ import org.koitharu.kotatsu.details.ui.withVolumeHeaders import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.local.ui.LocalChaptersRemoveService -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder import org.koitharu.kotatsu.reader.ui.ReaderNavigationCallback import org.koitharu.kotatsu.reader.ui.ReaderState @@ -218,7 +218,7 @@ class ChaptersFragment : var canSave = true var canDelete = true items.forEach { (_, x) -> - val isLocal = x.isDownloaded || x.chapter.source == MangaSource.LOCAL + val isLocal = x.isDownloaded || x.chapter.source == LocalMangaSource if (isLocal) canSave = false else canDelete = false } menu.findItem(R.id.action_save).isVisible = canSave diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt index aa23dc794..cd6c5b72d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt @@ -21,13 +21,13 @@ import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.details.ui.DetailsActivity import org.koitharu.kotatsu.download.domain.DownloadState import org.koitharu.kotatsu.download.ui.list.DownloadsActivity import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.format import org.koitharu.kotatsu.parsers.util.runCatchingCancellable import org.koitharu.kotatsu.search.ui.MangaListActivity @@ -231,7 +231,7 @@ class DownloadNotificationFactory @AssistedInject constructor( if (manga != null) { DetailsActivity.newIntent(context, manga) } else { - MangaListActivity.newIntent(context, MangaSource.LOCAL) + MangaListActivity.newIntent(context, LocalMangaSource) }, PendingIntent.FLAG_CANCEL_CURRENT, false, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt index fb0fea709..f89ba563e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt @@ -43,6 +43,7 @@ import okio.sink import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.TooManyRequestExceptions import org.koitharu.kotatsu.core.model.ids +import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.network.MangaHttpClient import org.koitharu.kotatsu.core.parser.MangaDataRepository @@ -177,7 +178,7 @@ class DownloadWorker @AssistedInject constructor( checkNotNull(destination) { applicationContext.getString(R.string.cannot_find_available_storage) } var output: LocalMangaOutput? = null try { - if (manga.source == MangaSource.LOCAL) { + if (manga.isLocal) { manga = localMangaRepository.getRemoteManga(manga) ?: error("Cannot obtain remote manga instance") } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt index 485dbc1cd..b8c2732e0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/data/MangaSourcesRepository.kt @@ -17,6 +17,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.core.ui.util.ReversibleHandle import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.mapToSet import java.util.Collections @@ -34,15 +35,13 @@ class MangaSourcesRepository @Inject constructor( private val dao: MangaSourcesDao get() = db.getSourcesDao() - private val remoteSources = EnumSet.allOf(MangaSource::class.java).apply { - remove(MangaSource.LOCAL) - remove(MangaSource.UNKNOWN) + private val remoteSources = EnumSet.allOf(MangaParserSource::class.java).apply { if (!BuildConfig.DEBUG) { - remove(MangaSource.DUMMY) + remove(MangaParserSource.DUMMY) } } - val allMangaSources: Set + val allMangaSources: Set get() = Collections.unmodifiableSet(remoteSources) suspend fun getEnabledSources(): List { @@ -70,7 +69,7 @@ class MangaSourcesRepository @Inject constructor( query: String?, locale: String?, sortOrder: SourcesSortOrder?, - ): List { + ): List { assimilateNewSources() val entities = dao.findAll().toMutableList() if (isDisabledOnly) { @@ -236,7 +235,7 @@ class MangaSourcesRepository @Inject constructor( } } - private suspend fun getNewSources(): MutableSet { + private suspend fun getNewSources(): MutableSet { val entities = dao.findAll() val result = EnumSet.copyOf(remoteSources) for (e in entities) { @@ -248,8 +247,8 @@ class MangaSourcesRepository @Inject constructor( private fun List.toSources( skipNsfwSources: Boolean, sortOrder: SourcesSortOrder?, - ): MutableList { - val result = ArrayList(size) + ): MutableList { + val result = ArrayList(size) for (entity in this) { val source = entity.source.toMangaSourceOrNull() ?: continue if (skipNsfwSources && source.isNsfw()) { @@ -273,5 +272,5 @@ class MangaSourcesRepository @Inject constructor( sourcesSortOrder } - private fun String.toMangaSourceOrNull(): MangaSource? = MangaSource.entries.find { it.name == this } + private fun String.toMangaSourceOrNull(): MangaParserSource? = MangaParserSource.entries.find { it.name == this } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt index 9a473aac6..19446b1ee 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/domain/ExploreRepository.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.explore.domain +import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.almostEquals @@ -7,7 +8,6 @@ import org.koitharu.kotatsu.core.util.ext.asArrayList import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.history.data.HistoryRepository -import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaSource @@ -45,7 +45,7 @@ class ExploreRepository @Inject constructor( suspend fun findRandomManga(source: MangaSource, tagsLimit: Int): Manga { val tagsBlacklist = TagsBlacklist(settings.suggestionsTagsBlacklist, 0.4f) - val skipNsfw = settings.isSuggestionsExcludeNsfw && source.contentType != ContentType.HENTAI + val skipNsfw = settings.isSuggestionsExcludeNsfw && !source.isNsfw() val tags = historyRepository.getPopularTags(tagsLimit).mapNotNull { if (it in tagsBlacklist) null else it.title } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt index a9c34b87b..28f165740 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/ExploreFragment.kt @@ -20,6 +20,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.bookmarks.ui.AllBookmarksActivity import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.ui.BaseFragment import org.koitharu.kotatsu.core.ui.dialog.TwoButtonsAlertDialog import org.koitharu.kotatsu.core.ui.list.ListSelectionController @@ -40,7 +41,7 @@ import org.koitharu.kotatsu.explore.ui.model.MangaSourceItem import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration import org.koitharu.kotatsu.list.ui.model.ListHeader import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.util.mapNotNullToSet import org.koitharu.kotatsu.search.ui.MangaListActivity import org.koitharu.kotatsu.settings.SettingsActivity @@ -123,7 +124,7 @@ class ExploreFragment : override fun onClick(v: View) { val intent = when (v.id) { - R.id.button_local -> MangaListActivity.newIntent(v.context, MangaSource.LOCAL) + R.id.button_local -> MangaListActivity.newIntent(v.context, LocalMangaSource) R.id.button_bookmarks -> AllBookmarksActivity.newIntent(v.context) R.id.button_more -> SuggestionsActivity.newIntent(v.context) R.id.button_downloads -> DownloadsActivity.newIntent(v.context) @@ -173,7 +174,7 @@ class ExploreFragment : override fun onActionItemClicked(controller: ListSelectionController, mode: ActionMode, item: MenuItem): Boolean { val selectedSources = controller.peekCheckedIds().mapNotNullToSet { id -> - MangaSource.entries.getOrNull(id.toInt()) + MangaParserSource.entries.getOrNull(id.toInt()) // TODO } if (selectedSources.isEmpty()) { return false diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/model/MangaSourceItem.kt b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/model/MangaSourceItem.kt index 4866dc2d1..891535fc9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/model/MangaSourceItem.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/explore/ui/model/MangaSourceItem.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.explore.ui.model +import org.koitharu.kotatsu.core.util.ext.longHashCode import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.parsers.model.MangaSource @@ -8,8 +9,7 @@ data class MangaSourceItem( val isGrid: Boolean, ) : ListModel { - val id: Long - get() = source.ordinal.toLong() + val id: Long = source.name.longHashCode() override fun areItemsTheSame(other: ListModel): Boolean { return other is MangaSourceItem && other.source == source diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/model/Cover.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/model/Cover.kt index e76c18724..32c0d4c0f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/model/Cover.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/domain/model/Cover.kt @@ -1,12 +1,10 @@ package org.koitharu.kotatsu.favourites.domain.model -import org.koitharu.kotatsu.parsers.model.MangaSource -import org.koitharu.kotatsu.parsers.util.find +import org.koitharu.kotatsu.core.model.MangaSource data class Cover( val url: String, val source: String, ) { - val mangaSource: MangaSource? - get() = if (source.isEmpty()) null else MangaSource.entries.find(source) + val mangaSource by lazy { MangaSource(source) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt index a49bdf217..0d2f78118 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/list/FavouritesListFragment.kt @@ -10,13 +10,13 @@ import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.util.ext.sortedByOrdinal import org.koitharu.kotatsu.core.util.ext.withArgs import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.list.domain.ListSortOrder import org.koitharu.kotatsu.list.ui.MangaListFragment -import org.koitharu.kotatsu.parsers.model.MangaSource @AndroidEntryPoint class FavouritesListFragment : MangaListFragment(), PopupMenu.OnMenuItemClickListener { @@ -57,9 +57,7 @@ class FavouritesListFragment : MangaListFragment(), PopupMenu.OnMenuItemClickLis } override fun onPrepareActionMode(controller: ListSelectionController, mode: ActionMode, menu: Menu): Boolean { - menu.findItem(R.id.action_save)?.isVisible = selectedItems.none { - it.source == MangaSource.LOCAL - } + menu.findItem(R.id.action_save)?.isVisible = selectedItems.none { it.isLocal } return super.onPrepareActionMode(controller, mode, menu) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterCoordinator.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterCoordinator.kt index e664b7785..c5a75289d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterCoordinator.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterCoordinator.kt @@ -43,6 +43,7 @@ import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.list.ui.model.toErrorFooter import org.koitharu.kotatsu.parsers.model.ContentRating import org.koitharu.kotatsu.parsers.model.MangaListFilter +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder @@ -451,7 +452,7 @@ class FilterCoordinator @Inject constructor( } private fun mergeTags(primary: Set, secondary: Set): Set { - val result = TreeSet(TagTitleComparator(repository.source.locale)) + val result = TreeSet(TagTitleComparator((repository.source as? MangaParserSource)?.locale)) result.addAll(secondary) result.addAll(primary) return result diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt index 4d5a1cac3..03c2ee975 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/history/ui/HistoryListFragment.kt @@ -8,6 +8,7 @@ import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.os.NetworkManageIntent import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.list.RecyclerScrollKeeper @@ -17,7 +18,6 @@ import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.databinding.FragmentListBinding import org.koitharu.kotatsu.list.ui.MangaListFragment import org.koitharu.kotatsu.list.ui.size.DynamicItemSizeResolver -import org.koitharu.kotatsu.parsers.model.MangaSource @AndroidEntryPoint class HistoryListFragment : MangaListFragment() { @@ -44,9 +44,7 @@ class HistoryListFragment : MangaListFragment() { } override fun onPrepareActionMode(controller: ListSelectionController, mode: ActionMode, menu: Menu): Boolean { - menu.findItem(R.id.action_save)?.isVisible = selectedItems.none { - it.source == MangaSource.LOCAL - } + menu.findItem(R.id.action_save)?.isVisible = selectedItems.none { it.isLocal } return super.onPrepareActionMode(controller, mode, menu) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt index d3ea527bf..a8e7440e1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/image/ui/ImageActivity.kt @@ -25,13 +25,13 @@ import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.util.PopupMenuMediator import org.koitharu.kotatsu.core.util.ShareHelper import org.koitharu.kotatsu.core.util.ext.enqueueWith import org.koitharu.kotatsu.core.util.ext.getDisplayIcon import org.koitharu.kotatsu.core.util.ext.getDisplayMessage -import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat import org.koitharu.kotatsu.core.util.ext.getThemeColor import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.observeEvent @@ -120,7 +120,7 @@ class ImageActivity : BaseActivity(), ImageRequest.Listene .memoryCachePolicy(CachePolicy.DISABLED) .lifecycle(this) .listener(this) - .source(intent.getSerializableExtraCompat(EXTRA_SOURCE)) + .source(MangaSource(intent.getStringExtra(EXTRA_SOURCE))) .target(SsivTarget(viewBinding.ssiv)) .enqueueWith(coil) } @@ -180,7 +180,7 @@ class ImageActivity : BaseActivity(), ImageRequest.Listene fun newIntent(context: Context, url: String, source: MangaSource?): Intent { return Intent(context, ImageActivity::class.java) .setData(Uri.parse(url)) - .putExtra(EXTRA_SOURCE, source) + .putExtra(EXTRA_SOURCE, source?.name) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt index b0eba4608..bafffd7fa 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/LocalMangaRepository.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import kotlinx.coroutines.runInterruptible +import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings @@ -29,7 +30,6 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.parsers.model.SortOrder @@ -49,7 +49,7 @@ class LocalMangaRepository @Inject constructor( private val settings: AppSettings, ) : MangaRepository { - override val source = MangaSource.LOCAL + override val source = LocalMangaSource private val locks = MultiMutex() private val localMappingCache = LocalMangaMappingCache() @@ -100,7 +100,7 @@ class LocalMangaRepository @Inject constructor( } override suspend fun getDetails(manga: Manga): Manga = when { - manga.source != MangaSource.LOCAL -> requireNotNull(findSavedManga(manga)?.manga) { + !manga.isLocal -> requireNotNull(findSavedManga(manga)?.manga) { "Manga is not local or saved" } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/MangaIndex.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/MangaIndex.kt index c859ffe66..394746604 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/MangaIndex.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/MangaIndex.kt @@ -4,6 +4,7 @@ import androidx.annotation.WorkerThread import org.json.JSONArray import org.json.JSONObject import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter @@ -58,7 +59,7 @@ class MangaIndex(source: String?) { } fun getMangaInfo(): Manga? = if (json.length() == 0) null else runCatching { - val source = MangaSource.valueOf(json.getString("source")) + val source = MangaSource(json.getString("source")) Manga( id = json.getLong("id"), title = json.getString("title"), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaDirInput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaDirInput.kt index 3781a7c4e..3667f1e31 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaDirInput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaDirInput.kt @@ -4,6 +4,7 @@ import androidx.core.net.toFile import androidx.core.net.toUri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible +import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.util.AlphanumComparator import org.koitharu.kotatsu.core.util.ext.children import org.koitharu.kotatsu.core.util.ext.creationTime @@ -18,7 +19,6 @@ import org.koitharu.kotatsu.local.domain.model.LocalManga import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.toCamelCase import java.io.File import java.util.TreeMap @@ -47,7 +47,7 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) { index?.getCoverEntry() ?: findFirstImageEntry().orEmpty(), ) val manga = info?.copy2( - source = MangaSource.LOCAL, + source = LocalMangaSource, url = mangaUri, coverUrl = cover, largeCoverUrl = cover, @@ -59,14 +59,14 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) { // old downloads chapterFiles.values.elementAtOrNull(i) } ?: return@mapIndexedNotNull null - c.copy(url = file.toUri().toString(), source = MangaSource.LOCAL) + c.copy(url = file.toUri().toString(), source = LocalMangaSource) }, ) ?: Manga( id = root.absolutePath.longHashCode(), title = root.name.toHumanReadable(), url = mangaUri, publicUrl = mangaUri, - source = MangaSource.LOCAL, + source = LocalMangaSource, coverUrl = findFirstImageEntry().orEmpty(), chapters = chapterFiles.values.mapIndexed { i, f -> MangaChapter( @@ -74,7 +74,7 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) { name = f.nameWithoutExtension.toHumanReadable(), number = 0f, volume = 0, - source = MangaSource.LOCAL, + source = LocalMangaSource, uploadDate = f.creationTime, url = f.toUri().toString(), scanlator = null, @@ -106,7 +106,7 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) { .toListSorted(compareBy(AlphanumComparator()) { x -> x.name }) .map { val pageUri = it.toUri().toString() - MangaPage(pageUri.longHashCode(), pageUri, null, MangaSource.LOCAL) + MangaPage(pageUri.longHashCode(), pageUri, null, LocalMangaSource) } } else { ZipFile(file).use { zip -> @@ -121,7 +121,7 @@ class LocalMangaDirInput(root: File) : LocalMangaInput(root) { id = pageUri.longHashCode(), url = pageUri, preview = null, - source = MangaSource.LOCAL, + source = LocalMangaSource, ) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt index d2aa8f71d..36f839979 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/input/LocalMangaZipInput.kt @@ -7,6 +7,7 @@ import androidx.core.net.toFile import androidx.core.net.toUri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible +import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.util.AlphanumComparator import org.koitharu.kotatsu.core.util.ext.longHashCode import org.koitharu.kotatsu.core.util.ext.readText @@ -17,7 +18,6 @@ import org.koitharu.kotatsu.local.domain.model.LocalManga import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaChapter import org.koitharu.kotatsu.parsers.model.MangaPage -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.util.toCamelCase import java.io.File import java.util.Enumeration @@ -47,12 +47,12 @@ class LocalMangaZipInput(root: File) : LocalMangaInput(root) { entryName = index.getCoverEntry() ?: findFirstImageEntry(zip.entries())?.name.orEmpty(), ) return@use info.copy2( - source = MangaSource.LOCAL, + source = LocalMangaSource, url = fileUri, coverUrl = cover, largeCoverUrl = cover, chapters = info.chapters?.map { c -> - c.copy(url = fileUri, source = MangaSource.LOCAL) + c.copy(url = fileUri, source = LocalMangaSource) }, ) } @@ -70,7 +70,7 @@ class LocalMangaZipInput(root: File) : LocalMangaInput(root) { title = title, url = fileUri, publicUrl = fileUri, - source = MangaSource.LOCAL, + source = LocalMangaSource, coverUrl = zipUri(root, findFirstImageEntry(zip.entries())?.name.orEmpty()), chapters = chapters.sortedWith(AlphanumComparator()) .mapIndexed { i, s -> @@ -79,7 +79,7 @@ class LocalMangaZipInput(root: File) : LocalMangaInput(root) { name = s.ifEmpty { title }, number = 0f, volume = 0, - source = MangaSource.LOCAL, + source = LocalMangaSource, uploadDate = 0L, url = uriBuilder.fragment(s).build().toString(), scanlator = null, @@ -135,7 +135,7 @@ class LocalMangaZipInput(root: File) : LocalMangaInput(root) { id = entryUri.longHashCode(), url = entryUri, preview = null, - source = MangaSource.LOCAL, + source = LocalMangaSource, ) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaUtil.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaUtil.kt index d2fb72122..08df5832a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaUtil.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/data/output/LocalMangaUtil.kt @@ -4,17 +4,15 @@ import androidx.core.net.toFile import androidx.core.net.toUri import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runInterruptible +import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.parsers.model.Manga -import org.koitharu.kotatsu.parsers.model.MangaSource class LocalMangaUtil( private val manga: Manga, ) { init { - require(manga.source == MangaSource.LOCAL) { - "Expected LOCAL source but ${manga.source} found" - } + require(manga.isLocal) { "Expected LOCAL source but ${manga.source} found" } } suspend fun deleteChapters(ids: Set) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListFragment.kt index f82d06de8..069240354 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/local/ui/LocalListFragment.kt @@ -14,6 +14,7 @@ import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.widgets.TipView import org.koitharu.kotatsu.core.util.ShareHelper @@ -26,7 +27,6 @@ import org.koitharu.kotatsu.filter.ui.FilterOwner import org.koitharu.kotatsu.filter.ui.MangaFilter import org.koitharu.kotatsu.filter.ui.sheet.FilterSheetFragment import org.koitharu.kotatsu.list.ui.MangaListFragment -import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment import org.koitharu.kotatsu.settings.storage.RequestStorageManagerPermissionContract import org.koitharu.kotatsu.settings.storage.directories.MangaDirectoriesActivity @@ -47,9 +47,9 @@ class LocalListFragment : MangaListFragment(), FilterOwner { init { withArgs(1) { - putSerializable( + putString( RemoteListFragment.ARG_SOURCE, - MangaSource.LOCAL, + LocalMangaSource.name, ) // required by FilterCoordinator } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/ReadingResumeEnabledUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/ReadingResumeEnabledUseCase.kt index 26e9d26c3..8828edcfd 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/ReadingResumeEnabledUseCase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/domain/ReadingResumeEnabledUseCase.kt @@ -4,11 +4,11 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf +import org.koitharu.kotatsu.core.model.isLocal import org.koitharu.kotatsu.core.os.NetworkState import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.observeAsFlow import org.koitharu.kotatsu.history.data.HistoryRepository -import org.koitharu.kotatsu.parsers.model.MangaSource import javax.inject.Inject class ReadingResumeEnabledUseCase @Inject constructor( @@ -24,7 +24,7 @@ class ReadingResumeEnabledUseCase @Inject constructor( flowOf(false) } else { combine(networkState, historyRepository.observeLast()) { isOnline, last -> - last != null && (isOnline || last.source == MangaSource.LOCAL) + last != null && (isOnline || last.isLocal) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeViewModel.kt index e97d7950a..350fd8c67 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/main/ui/welcome/WelcomeViewModel.kt @@ -15,7 +15,7 @@ import org.koitharu.kotatsu.core.util.ext.toLocale import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.filter.ui.model.FilterProperty import org.koitharu.kotatsu.parsers.model.ContentType -import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.util.mapToSet import java.util.EnumSet import java.util.Locale @@ -103,7 +103,7 @@ class WelcomeViewModel @Inject constructor( private suspend fun commit() { val languages = locales.value.selectedItems.mapToSet { it.language } val types = types.value.selectedItems - val enabledSources = allSources.filterTo(EnumSet.noneOf(MangaSource::class.java)) { x -> + val enabledSources = allSources.filterTo(EnumSet.noneOf(MangaParserSource::class.java)) { x -> x.contentType in types && x.locale in languages } repository.setSourcesEnabledExclusive(enabledSources) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/ReaderPage.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/ReaderPage.kt index a9c97cf96..f9b4a45dd 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/ReaderPage.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/ReaderPage.kt @@ -2,10 +2,13 @@ package org.koitharu.kotatsu.reader.ui.pager import android.os.Parcelable import kotlinx.parcelize.Parcelize +import kotlinx.parcelize.TypeParceler +import org.koitharu.kotatsu.core.model.parcelable.MangaSourceParceler import org.koitharu.kotatsu.parsers.model.MangaPage import org.koitharu.kotatsu.parsers.model.MangaSource @Parcelize +@TypeParceler data class ReaderPage( val id: Long, val url: String, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt index ce7673c1e..28795aac5 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.drop import org.koitharu.kotatsu.R import org.koitharu.kotatsu.browser.BrowserActivity +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.ui.list.ListSelectionController import org.koitharu.kotatsu.core.ui.util.MenuInvalidator import org.koitharu.kotatsu.core.util.ext.addMenuProvider @@ -75,7 +76,12 @@ class RemoteListFragment : MangaListFragment(), FilterOwner { override fun onSecondaryErrorActionClick(error: Throwable) { viewModel.browserUrl?.also { url -> startActivity( - BrowserActivity.newIntent(requireContext(), url, viewModel.source, viewModel.source.title), + BrowserActivity.newIntent( + requireContext(), + url, + viewModel.source, + viewModel.source.getTitle(requireContext()), + ), ) } ?: Snackbar.make(requireViewBinding().recyclerView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT) .show() @@ -165,8 +171,8 @@ class RemoteListFragment : MangaListFragment(), FilterOwner { const val ARG_SOURCE = "provider" - fun newInstance(provider: MangaSource) = RemoteListFragment().withArgs(1) { - putSerializable(ARG_SOURCE, provider) + fun newInstance(source: MangaSource) = RemoteListFragment().withArgs(1) { + putString(ARG_SOURCE, source.name) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt index 75f472d85..3819bb9ce 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/MangaListActivity.kt @@ -21,7 +21,9 @@ import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.LocalMangaSource import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaTags @@ -77,7 +79,7 @@ class MangaListActivity : finishAfterTransition() } else { viewBinding.buttonOrder?.setOnClickListener(this) - title = if (src == MangaSource.LOCAL) getString(R.string.local_storage) else src.title + title = src.getTitle(this) initList(src, tags) } } @@ -125,7 +127,7 @@ class MangaListActivity : } else { fm.commit { setReorderingAllowed(true) - val fragment = if (source == MangaSource.LOCAL) { + val fragment = if (source == LocalMangaSource) { LocalListFragment() } else { RemoteListFragment.newInstance(source) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchActivity.kt index 02860a3e8..e88057a82 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchActivity.kt @@ -11,8 +11,9 @@ import androidx.core.view.updatePadding import androidx.fragment.app.commit import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.MangaSource +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.ui.BaseActivity -import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat import org.koitharu.kotatsu.core.util.ext.observe import org.koitharu.kotatsu.core.util.ext.showKeyboard import org.koitharu.kotatsu.databinding.ActivitySearchBinding @@ -28,15 +29,12 @@ class SearchActivity : BaseActivity(), SearchView.OnQuery override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(ActivitySearchBinding.inflate(layoutInflater)) - source = intent.getSerializableExtraCompat(EXTRA_SOURCE) ?: run { - finishAfterTransition() - return - } + source = MangaSource(intent.getStringExtra(EXTRA_SOURCE)) val query = intent.getStringExtra(EXTRA_QUERY) supportActionBar?.setDisplayHomeAsUpEnabled(true) searchSuggestionViewModel.isIncognitoModeEnabled.observe(this, this::onIncognitoModeChanged) with(viewBinding.searchView) { - queryHint = getString(R.string.search_on_s, source.title) + queryHint = getString(R.string.search_on_s, source.getTitle(context)) setOnQueryTextListener(this@SearchActivity) if (query.isNullOrBlank()) { @@ -52,7 +50,7 @@ class SearchActivity : BaseActivity(), SearchView.OnQuery viewBinding.toolbar.updatePadding( left = insets.left, right = insets.right, - top = insets.top + top = insets.top, ) viewBinding.container.updatePadding( bottom = insets.bottom, @@ -93,7 +91,7 @@ class SearchActivity : BaseActivity(), SearchView.OnQuery fun newIntent(context: Context, source: MangaSource, query: String?) = Intent(context, SearchActivity::class.java) - .putExtra(EXTRA_SOURCE, source) + .putExtra(EXTRA_SOURCE, source.name) .putExtra(EXTRA_QUERY, query) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchFragment.kt index 6e8b9683a..7662f9eb9 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchFragment.kt @@ -30,7 +30,7 @@ class SearchFragment : MangaListFragment() { const val ARG_SOURCE = "source" fun newInstance(source: MangaSource, query: String) = SearchFragment().withArgs(2) { - putSerializable(ARG_SOURCE, source) + putString(ARG_SOURCE, source.name) putString(ARG_QUERY, query) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchViewModel.kt index 57eb09333..a1a6a0f1d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/SearchViewModel.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.plus import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.distinctById import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.prefs.AppSettings @@ -42,7 +43,7 @@ class SearchViewModel @Inject constructor( ) : MangaListViewModel(settings, downloadScheduler) { private val query = savedStateHandle.require(SearchFragment.ARG_QUERY) - private val repository = repositoryFactory.create(savedStateHandle.require(SearchFragment.ARG_SOURCE)) + private val repository = repositoryFactory.create(MangaSource(savedStateHandle.get(SearchFragment.ARG_SOURCE))) private val mangaList = MutableStateFlow?>(null) private val hasNextPage = MutableStateFlow(false) private val listError = MutableStateFlow(null) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchResultsAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchResultsAD.kt index 6f945677f..9363051e8 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchResultsAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/adapter/SearchResultsAD.kt @@ -8,6 +8,7 @@ import coil.ImageLoader import com.hannesdorfmann.adapterdelegates4.ListDelegationAdapter import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.list.decor.SpacingItemDecoration @@ -45,7 +46,7 @@ fun searchResultsAD( binding.buttonMore.setOnClickListener(eventListener) bind { - binding.textViewTitle.text = item.source.title + binding.textViewTitle.text = item.source.getTitle(context) binding.buttonMore.isVisible = item.hasMore adapter.items = item.list adapter.notifyDataSetChanged() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt index 315cb8ec8..9e8e7ccea 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/SearchSuggestionViewModel.kt @@ -22,7 +22,6 @@ import org.koitharu.kotatsu.core.prefs.observeAsStateFlow import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.core.util.ext.sizeOrZero -import org.koitharu.kotatsu.core.util.ext.toEnumSet import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag @@ -103,7 +102,7 @@ class SearchSuggestionViewModel @Inject constructor( suggestionJob?.cancel() suggestionJob = combine( query.debounce(DEBOUNCE_TIMEOUT), - sourcesRepository.observeEnabledSources().map { it.toEnumSet() }, + sourcesRepository.observeEnabledSources().map { it.toSet() }, settings.observeAsFlow(AppSettings.KEY_SEARCH_SUGGESTION_TYPES) { searchSuggestionTypes }, ::Triple, ).mapLatest { (searchQuery, enabledSources, types) -> diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/model/SearchSuggestionItem.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/model/SearchSuggestionItem.kt index 5493fb920..263153f30 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/model/SearchSuggestionItem.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/suggestion/model/SearchSuggestionItem.kt @@ -1,9 +1,9 @@ package org.koitharu.kotatsu.search.ui.suggestion.model +import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.list.ui.ListModelDiffCallback import org.koitharu.kotatsu.list.ui.model.ListModel -import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaSource @@ -51,7 +51,7 @@ sealed interface SearchSuggestionItem : ListModel { ) : SearchSuggestionItem { val isNsfw: Boolean - get() = source.contentType == ContentType.HENTAI + get() = source.isNsfw() override fun areItemsTheSame(other: ListModel): Boolean { return other is Source && other.source == source diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt index f9ae0f40d..d6d1ad5d0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/SettingsActivity.kt @@ -16,8 +16,8 @@ import com.google.android.material.appbar.AppBarLayout import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.ui.BaseActivity -import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.databinding.ActivitySettingsBinding import org.koitharu.kotatsu.main.ui.owners.AppBarOwner @@ -110,7 +110,7 @@ class SettingsActivity : ACTION_SOURCES -> SourcesSettingsFragment() ACTION_MANAGE_DOWNLOADS -> DownloadsSettingsFragment() ACTION_SOURCE -> SourceSettingsFragment.newInstance( - intent.getSerializableExtraCompat(EXTRA_SOURCE) ?: MangaSource.LOCAL, + MangaSource(intent.getStringExtra(EXTRA_SOURCE)), ) ACTION_MANAGE_SOURCES -> SourcesManageFragment() @@ -177,6 +177,6 @@ class SettingsActivity : fun newSourceSettingsIntent(context: Context, source: MangaSource) = Intent(context, SettingsActivity::class.java) .setAction(ACTION_SOURCE) - .putExtra(EXTRA_SOURCE, source) + .putExtra(EXTRA_SOURCE, source.name) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsFragment.kt index 460bdabcc..e6f1d0252 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsFragment.kt @@ -9,6 +9,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.exceptions.resolve.ExceptionResolver import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BasePreferenceFragment import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver @@ -26,7 +27,9 @@ class SourceSettingsFragment : BasePreferenceFragment(0), Preference.OnPreferenc override fun onResume() { super.onResume() - setTitle(viewModel.source.title) + context?.let { ctx -> + setTitle(viewModel.source.getTitle(ctx)) + } viewModel.onResume() } @@ -36,7 +39,7 @@ class SourceSettingsFragment : BasePreferenceFragment(0), Preference.OnPreferenc addPreferencesFromRepository(viewModel.repository) findPreference(KEY_ENABLE)?.run { - setOnPreferenceChangeListener(this@SourceSettingsFragment) + onPreferenceChangeListener = this@SourceSettingsFragment } findPreference(KEY_AUTH)?.run { val authProvider = viewModel.repository.getAuthProvider() @@ -101,7 +104,7 @@ class SourceSettingsFragment : BasePreferenceFragment(0), Preference.OnPreferenc const val EXTRA_SOURCE = "source" fun newInstance(source: MangaSource) = SourceSettingsFragment().withArgs(1) { - putSerializable(EXTRA_SOURCE, source) + putString(EXTRA_SOURCE, source.name) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsViewModel.kt index bd55af447..4f6c2f72f 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/SourceSettingsViewModel.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import okhttp3.HttpUrl import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository @@ -16,10 +17,8 @@ import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.ui.util.ReversibleAction import org.koitharu.kotatsu.core.util.ext.MutableEventFlow import org.koitharu.kotatsu.core.util.ext.call -import org.koitharu.kotatsu.core.util.ext.require import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.parsers.exception.AuthRequiredException -import org.koitharu.kotatsu.parsers.model.MangaSource import javax.inject.Inject @HiltViewModel @@ -30,7 +29,7 @@ class SourceSettingsViewModel @Inject constructor( private val mangaSourcesRepository: MangaSourcesRepository, ) : BaseViewModel(), SharedPreferences.OnSharedPreferenceChangeListener { - val source = savedStateHandle.require(SourceSettingsFragment.EXTRA_SOURCE) + val source = MangaSource(savedStateHandle.get(SourceSettingsFragment.EXTRA_SOURCE)) val repository = mangaRepositoryFactory.create(source) as RemoteMangaRepository val onActionDone = MutableEventFlow() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt index 11c33dd17..fca77d51d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/auth/SourceAuthActivity.kt @@ -18,15 +18,16 @@ import org.koitharu.kotatsu.browser.BrowserCallback import org.koitharu.kotatsu.browser.BrowserClient import org.koitharu.kotatsu.browser.ProgressChromeClient import org.koitharu.kotatsu.browser.WebViewBackPressedCallback +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.network.CommonHeaders import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.util.TaggedActivityResult import org.koitharu.kotatsu.core.util.ext.configureForParser -import org.koitharu.kotatsu.core.util.ext.getSerializableExtraCompat import org.koitharu.kotatsu.databinding.ActivityBrowserBinding import org.koitharu.kotatsu.parsers.MangaParserAuthProvider +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaSource import javax.inject.Inject import com.google.android.material.R as materialR @@ -46,8 +47,8 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba if (!setContentViewWebViewSafe { ActivityBrowserBinding.inflate(layoutInflater) }) { return } - val source = intent?.getSerializableExtraCompat(EXTRA_SOURCE) - if (source == null) { + val source = MangaSource(intent?.getStringExtra(EXTRA_SOURCE)) + if (source !is MangaParserSource) { finishAfterTransition() return } @@ -148,7 +149,7 @@ class SourceAuthActivity : BaseActivity(), BrowserCallba fun newIntent(context: Context, source: MangaSource): Intent { return Intent(context, SourceAuthActivity::class.java) - .putExtra(EXTRA_SOURCE, source) + .putExtra(EXTRA_SOURCE, source.name) } } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItem.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItem.kt index d5ce18d8e..350c56b57 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItem.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourceCatalogItem.kt @@ -3,12 +3,12 @@ package org.koitharu.kotatsu.settings.sources.catalog import androidx.annotation.DrawableRes import androidx.annotation.StringRes import org.koitharu.kotatsu.list.ui.model.ListModel -import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaParserSource sealed interface SourceCatalogItem : ListModel { data class Source( - val source: MangaSource, + val source: MangaParserSource, ) : SourceCatalogItem { override fun areItemsTheSame(other: ListModel): Boolean { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt index 030a0f661..0e2ebbf01 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogAdapter.kt @@ -3,6 +3,7 @@ package org.koitharu.kotatsu.settings.sources.catalog import android.content.Context import androidx.lifecycle.LifecycleOwner import coil.ImageLoader +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.ui.BaseListAdapter import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener import org.koitharu.kotatsu.core.ui.list.fastscroll.FastScroller @@ -23,6 +24,6 @@ class SourcesCatalogAdapter( } override fun getSectionText(context: Context, position: Int): CharSequence? { - return (items.getOrNull(position) as? SourceCatalogItem.Source)?.source?.title?.take(1) + return (items.getOrNull(position) as? SourceCatalogItem.Source)?.source?.getTitle(context)?.take(1) } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt index c519807b4..411d68f06 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/manage/SourcesListProducer.kt @@ -1,7 +1,9 @@ package org.koitharu.kotatsu.settings.sources.manage +import android.content.Context import androidx.room.InvalidationTracker import dagger.hilt.android.ViewModelLifecycle +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.scopes.ViewModelScoped import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.cancelAndJoin @@ -14,10 +16,10 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.db.TABLE_SOURCES +import org.koitharu.kotatsu.core.model.getTitle import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.util.ext.lifecycleScope -import org.koitharu.kotatsu.core.util.ext.toEnumSet import org.koitharu.kotatsu.explore.data.MangaSourcesRepository import org.koitharu.kotatsu.explore.data.SourcesSortOrder import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem @@ -26,6 +28,7 @@ import javax.inject.Inject @ViewModelScoped class SourcesListProducer @Inject constructor( lifecycle: ViewModelLifecycle, + @ApplicationContext private val context: Context, private val repository: MangaSourcesRepository, private val settings: AppSettings, ) : InvalidationTracker.Observer(TABLE_SOURCES) { @@ -64,10 +67,10 @@ class SourcesListProducer @Inject constructor( val isNsfwDisabled = settings.isNsfwContentDisabled val isReorderAvailable = settings.sourcesSortOrder == SourcesSortOrder.MANUAL val withTip = isReorderAvailable && settings.isTipEnabled(TIP_REORDER) - val enabledSet = enabledSources.toEnumSet() + val enabledSet = enabledSources.toSet() if (query.isNotEmpty()) { return enabledSources.mapNotNull { - if (!it.title.contains(query, ignoreCase = true)) { + if (!it.getTitle(context).contains(query, ignoreCase = true)) { return@mapNotNull null } SourceConfigItem.SourceItem( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/model/SourceConfigItem.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/model/SourceConfigItem.kt index a68b6581b..fcc1f4410 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/model/SourceConfigItem.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/model/SourceConfigItem.kt @@ -2,8 +2,8 @@ package org.koitharu.kotatsu.settings.sources.model import androidx.annotation.DrawableRes import androidx.annotation.StringRes +import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.list.ui.model.ListModel -import org.koitharu.kotatsu.parsers.model.ContentType import org.koitharu.kotatsu.parsers.model.MangaSource sealed interface SourceConfigItem : ListModel { @@ -16,7 +16,7 @@ sealed interface SourceConfigItem : ListModel { ) : SourceConfigItem { val isNsfw: Boolean - get() = source.contentType == ContentType.HENTAI + get() = source.isNsfw() override fun areItemsTheSame(other: ListModel): Boolean { return other is SourceItem && other.source == source diff --git a/app/src/test/kotlin/org/koitharu/kotatsu/core/backup/JsonSerializerTest.kt b/app/src/test/kotlin/org/koitharu/kotatsu/core/backup/JsonSerializerTest.kt index 2ce335749..b4d2667ca 100644 --- a/app/src/test/kotlin/org/koitharu/kotatsu/core/backup/JsonSerializerTest.kt +++ b/app/src/test/kotlin/org/koitharu/kotatsu/core/backup/JsonSerializerTest.kt @@ -7,7 +7,7 @@ import org.koitharu.kotatsu.core.db.entity.TagEntity import org.koitharu.kotatsu.favourites.data.FavouriteCategoryEntity import org.koitharu.kotatsu.favourites.data.FavouriteEntity import org.koitharu.kotatsu.history.data.HistoryEntity -import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaState import org.koitharu.kotatsu.parsers.model.SortOrder import java.util.concurrent.TimeUnit @@ -42,7 +42,7 @@ class JsonSerializerTest { largeCoverUrl = null, state = MangaState.FINISHED.name, author = "RERE", - source = MangaSource.DUMMY.name, + source = MangaParserSource.DUMMY.name, ) val json = JsonSerializer(entity).toJson() val result = JsonDeserializer(json).toMangaEntity() @@ -55,7 +55,7 @@ class JsonSerializerTest { id = 934023534, title = "Adventure", key = "adventure", - source = MangaSource.DUMMY.name, + source = MangaParserSource.DUMMY.name, ) val json = JsonSerializer(entity).toJson() val result = JsonDeserializer(json).toTagEntity() diff --git a/app/src/test/kotlin/org/koitharu/kotatsu/reader/domain/ChapterPagesTest.kt b/app/src/test/kotlin/org/koitharu/kotatsu/reader/domain/ChapterPagesTest.kt index 039115a00..6829c7b4c 100644 --- a/app/src/test/kotlin/org/koitharu/kotatsu/reader/domain/ChapterPagesTest.kt +++ b/app/src/test/kotlin/org/koitharu/kotatsu/reader/domain/ChapterPagesTest.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.reader.domain import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test -import org.koitharu.kotatsu.parsers.model.MangaSource +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.reader.ui.pager.ReaderPage import kotlin.random.Random @@ -73,6 +73,6 @@ class ChapterPagesTest { preview = null, chapterId = chapterId, index = Random.nextInt(), - source = MangaSource.DUMMY, + source = MangaParserSource.DUMMY, ) }