diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 70ec0d3b7..044602cf4 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -242,6 +242,9 @@
+
{
+ val sources = sourcesRepository.getEnabledSources()
+ if (sources.isEmpty()) {
+ return emptyFlow()
+ }
+ val semaphore = Semaphore(MAX_PARALLELISM)
+ return channelFlow {
+ for (source in sources) {
+ val repository = mangaRepositoryFactory.create(source)
+ if (!repository.isSearchSupported) {
+ continue
+ }
+ launch {
+ val list = runCatchingCancellable {
+ semaphore.withPermit {
+ repository.getList(offset = 0, filter = MangaListFilter.Search(manga.title))
+ }
+ }.getOrDefault(emptyList())
+ for (item in list) {
+ if (item != manga && item.matches(manga)) {
+ send(item)
+ }
+ }
+ }
+ }
+ }.map {
+ runCatchingCancellable {
+ mangaRepositoryFactory.create(it.source).getDetails(it)
+ }.getOrDefault(it)
+ }
+ }
+
+ private fun Manga.matches(ref: Manga): Boolean {
+ return matchesTitles(title, ref.title) ||
+ matchesTitles(title, ref.altTitle) ||
+ matchesTitles(altTitle, ref.title) ||
+ matchesTitles(altTitle, ref.altTitle)
+
+ }
+
+ private fun matchesTitles(a: String?, b: String?): Boolean {
+ return !a.isNullOrEmpty() && !b.isNullOrEmpty() && a.almostEquals(b, MATCH_THRESHOLD)
+ }
+}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/MigrateUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/MigrateUseCase.kt
new file mode 100644
index 000000000..4a061af87
--- /dev/null
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/domain/MigrateUseCase.kt
@@ -0,0 +1,127 @@
+package org.koitharu.kotatsu.alternatives.domain
+
+import androidx.room.withTransaction
+import org.koitharu.kotatsu.core.db.MangaDatabase
+import org.koitharu.kotatsu.core.model.getPreferredBranch
+import org.koitharu.kotatsu.core.parser.MangaDataRepository
+import org.koitharu.kotatsu.core.parser.MangaRepository
+import org.koitharu.kotatsu.details.domain.DetailsLoadUseCase
+import org.koitharu.kotatsu.details.domain.ProgressUpdateUseCase
+import org.koitharu.kotatsu.favourites.data.FavouriteEntity
+import org.koitharu.kotatsu.history.data.HistoryEntity
+import org.koitharu.kotatsu.history.data.PROGRESS_NONE
+import org.koitharu.kotatsu.history.data.toMangaHistory
+import org.koitharu.kotatsu.parsers.model.Manga
+import org.koitharu.kotatsu.parsers.model.MangaChapter
+import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
+import javax.inject.Inject
+
+class MigrateUseCase @Inject constructor(
+ private val mangaRepositoryFactory: MangaRepository.Factory,
+ private val mangaDataRepository: MangaDataRepository,
+ private val database: MangaDatabase,
+ private val progressUpdateUseCase: ProgressUpdateUseCase,
+ private val useCase: DetailsLoadUseCase
+) {
+
+ suspend operator fun invoke(oldManga: Manga, newManga: Manga) {
+ val oldDetails = if (oldManga.chapters.isNullOrEmpty()) {
+ runCatchingCancellable {
+ mangaRepositoryFactory.create(oldManga.source).getDetails(oldManga)
+ }.getOrDefault(oldManga)
+ } else {
+ oldManga
+ }
+ val newDetails = if (newManga.chapters.isNullOrEmpty()) {
+ mangaRepositoryFactory.create(newManga.source).getDetails(newManga)
+ } else {
+ newManga
+ }
+ mangaDataRepository.storeManga(newDetails)
+ database.withTransaction {
+ // replace favorites
+ val favoritesDao = database.getFavouritesDao()
+ val oldFavourite = favoritesDao.find(oldDetails.id)
+ if (oldFavourite != null) {
+ favoritesDao.delete(oldManga.id)
+ for (f in oldFavourite.categories) {
+ val e = FavouriteEntity(
+ mangaId = newManga.id,
+ categoryId = f.categoryId.toLong(),
+ sortKey = f.sortKey,
+ createdAt = f.createdAt,
+ deletedAt = 0,
+ )
+ favoritesDao.upsert(e)
+ }
+ }
+ // replace history
+ val historyDao = database.getHistoryDao()
+ val oldHistory = historyDao.find(oldDetails.id)
+ if (oldHistory != null) {
+ val newHistory = makeNewHistory(oldDetails, newDetails, oldHistory)
+ historyDao.delete(oldDetails.id)
+ historyDao.upsert(newHistory)
+ }
+ }
+ progressUpdateUseCase(newManga)
+ }
+
+ private fun makeNewHistory(
+ oldManga: Manga,
+ newManga: Manga,
+ history: HistoryEntity,
+ ): HistoryEntity {
+ if (oldManga.chapters.isNullOrEmpty()) { // probably broken manga/source
+ val branch = newManga.getPreferredBranch(null)
+ val chapters = checkNotNull(newManga.getChapters(branch))
+ return HistoryEntity(
+ mangaId = newManga.id,
+ createdAt = history.createdAt,
+ updatedAt = System.currentTimeMillis(),
+ chapterId = chapters[(chapters.lastIndex * history.percent).toInt()].id,
+ page = history.page,
+ scroll = history.scroll,
+ percent = history.percent,
+ deletedAt = 0,
+ chaptersCount = chapters.size,
+ )
+ }
+ val branch = oldManga.getPreferredBranch(history.toMangaHistory())
+ val oldChapters = checkNotNull(oldManga.getChapters(branch))
+ var index = oldChapters.indexOfFirst { it.id == history.chapterId }
+ if (index < 0) {
+ index = (oldChapters.size * history.percent).toInt()
+ }
+ val newChapters = checkNotNull(newManga.chapters).groupBy { it.branch }
+ val newBranch = if (newChapters.containsKey(branch)) {
+ branch
+ } else {
+ newManga.getPreferredBranch(null)
+ }
+ val newChapterId = checkNotNull(newChapters[newBranch]).let {
+ val oldChapter = oldChapters[index]
+ it.findByNumber(oldChapter.volume, oldChapter.number) ?: it.getOrNull(index) ?: it.last()
+ }.id
+
+ return HistoryEntity(
+ mangaId = newManga.id,
+ createdAt = history.createdAt,
+ updatedAt = System.currentTimeMillis(),
+ chapterId = newChapterId,
+ page = history.page,
+ scroll = history.scroll,
+ percent = PROGRESS_NONE,
+ deletedAt = 0,
+ chaptersCount = checkNotNull(newChapters[newBranch]).size,
+ )
+ }
+
+ private fun List.findByNumber(volume: Int, number: Float): MangaChapter? {
+ return if (number <= 0f) {
+ null
+ } else {
+ firstOrNull { it.volume == volume && it.number == number }
+ }
+ }
+}
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
new file mode 100644
index 000000000..9c2f4f7a0
--- /dev/null
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativeAD.kt
@@ -0,0 +1,88 @@
+package org.koitharu.kotatsu.alternatives.ui
+
+import android.text.style.ForegroundColorSpan
+import androidx.core.content.ContextCompat
+import androidx.core.text.buildSpannedString
+import androidx.core.text.inSpans
+import androidx.lifecycle.LifecycleOwner
+import coil.ImageLoader
+import coil.request.ImageRequest
+import coil.transform.CircleCropTransformation
+import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
+import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.core.parser.favicon.faviconUri
+import org.koitharu.kotatsu.core.ui.image.ChipIconTarget
+import org.koitharu.kotatsu.core.ui.image.CoverSizeResolver
+import org.koitharu.kotatsu.core.ui.image.TrimTransformation
+import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter
+import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
+import org.koitharu.kotatsu.core.util.ext.enqueueWith
+import org.koitharu.kotatsu.core.util.ext.newImageRequest
+import org.koitharu.kotatsu.core.util.ext.source
+import org.koitharu.kotatsu.databinding.ItemMangaAlternativeBinding
+import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
+import org.koitharu.kotatsu.list.ui.model.ListModel
+import kotlin.math.sign
+import com.google.android.material.R as materialR
+
+fun alternativeAD(
+ coil: ImageLoader,
+ lifecycleOwner: LifecycleOwner,
+ listener: OnListItemClickListener,
+) = adapterDelegateViewBinding(
+ { inflater, parent -> ItemMangaAlternativeBinding.inflate(inflater, parent, false) },
+) {
+
+ val colorGreen = ContextCompat.getColor(context, R.color.common_green)
+ val colorRed = ContextCompat.getColor(context, R.color.common_red)
+ val clickListener = AdapterDelegateClickListenerAdapter(this, listener)
+ itemView.setOnClickListener(clickListener)
+ binding.buttonMigrate.setOnClickListener(clickListener)
+ binding.chipSource.setOnClickListener(clickListener)
+
+ bind { payloads ->
+ binding.textViewTitle.text = item.manga.title
+ binding.textViewSubtitle.text = buildSpannedString {
+ append(context.resources.getQuantityString(R.plurals.chapters, item.chaptersCount, item.chaptersCount))
+ when (item.chaptersDiff.sign) {
+ -1 -> inSpans(ForegroundColorSpan(colorRed)) {
+ append(" ▼ ")
+ append(item.chaptersDiff.toString())
+ }
+
+ 1 -> inSpans(ForegroundColorSpan(colorGreen)) {
+ append(" ▲ +")
+ append(item.chaptersDiff.toString())
+ }
+ }
+ }
+ binding.progressView.setPercent(item.progress, ListModelDiffCallback.PAYLOAD_PROGRESS_CHANGED in payloads)
+ binding.chipSource.also { chip ->
+ chip.text = item.manga.source.title
+ ImageRequest.Builder(context)
+ .data(item.manga.source.faviconUri())
+ .lifecycle(lifecycleOwner)
+ .crossfade(false)
+ .size(context.resources.getDimensionPixelSize(materialR.dimen.m3_chip_icon_size))
+ .target(ChipIconTarget(chip))
+ .placeholder(R.drawable.ic_web)
+ .fallback(R.drawable.ic_web)
+ .error(R.drawable.ic_web)
+ .source(item.manga.source)
+ .transformations(CircleCropTransformation())
+ .allowRgb565(true)
+ .enqueueWith(coil)
+ }
+ binding.imageViewCover.newImageRequest(lifecycleOwner, item.manga.coverUrl)?.run {
+ size(CoverSizeResolver(binding.imageViewCover))
+ placeholder(R.drawable.ic_placeholder)
+ fallback(R.drawable.ic_placeholder)
+ error(R.drawable.ic_error_placeholder)
+ transformations(TrimTransformation())
+ allowRgb565(true)
+ tag(item.manga)
+ source(item.manga.source)
+ enqueueWith(coil)
+ }
+ }
+}
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
new file mode 100644
index 000000000..03d099a54
--- /dev/null
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesActivity.kt
@@ -0,0 +1,114 @@
+package org.koitharu.kotatsu.alternatives.ui
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.View
+import android.widget.Toast
+import androidx.activity.viewModels
+import androidx.core.graphics.Insets
+import androidx.core.view.updatePadding
+import coil.ImageLoader
+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.parcelable.ParcelableManga
+import org.koitharu.kotatsu.core.parser.MangaIntent
+import org.koitharu.kotatsu.core.ui.BaseActivity
+import org.koitharu.kotatsu.core.ui.BaseListAdapter
+import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
+import org.koitharu.kotatsu.core.util.ext.DIALOG_THEME_CENTERED
+import org.koitharu.kotatsu.core.util.ext.observe
+import org.koitharu.kotatsu.core.util.ext.observeEvent
+import org.koitharu.kotatsu.databinding.ActivityAlternativesBinding
+import org.koitharu.kotatsu.details.ui.DetailsActivity
+import org.koitharu.kotatsu.list.ui.adapter.ListItemType
+import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
+import org.koitharu.kotatsu.list.ui.adapter.emptyStateListAD
+import org.koitharu.kotatsu.list.ui.adapter.loadingFooterAD
+import org.koitharu.kotatsu.list.ui.adapter.loadingStateAD
+import org.koitharu.kotatsu.list.ui.model.ListModel
+import org.koitharu.kotatsu.parsers.model.Manga
+import org.koitharu.kotatsu.search.ui.SearchActivity
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class AlternativesActivity : BaseActivity(),
+ OnListItemClickListener {
+
+ @Inject
+ lateinit var coil: ImageLoader
+
+ private val viewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(ActivityAlternativesBinding.inflate(layoutInflater))
+ supportActionBar?.run {
+ setDisplayHomeAsUpEnabled(true)
+ subtitle = viewModel.manga.title
+ }
+ val listAdapter = BaseListAdapter()
+ .addDelegate(ListItemType.MANGA_LIST_DETAILED, alternativeAD(coil, this, this))
+ .addDelegate(ListItemType.STATE_EMPTY, emptyStateListAD(coil, this, null))
+ .addDelegate(ListItemType.FOOTER_LOADING, loadingFooterAD())
+ .addDelegate(ListItemType.STATE_LOADING, loadingStateAD())
+ with(viewBinding.recyclerView) {
+ setHasFixedSize(true)
+ addItemDecoration(TypedListSpacingDecoration(context, addHorizontalPadding = false))
+ adapter = listAdapter
+ }
+
+ viewModel.onError.observeEvent(this, SnackbarErrorObserver(viewBinding.recyclerView, null))
+ viewModel.content.observe(this, listAdapter)
+ viewModel.onMigrated.observeEvent(this) {
+ Toast.makeText(this, R.string.migration_completed, Toast.LENGTH_SHORT).show()
+ startActivity(DetailsActivity.newIntent(this, it))
+ finishAfterTransition()
+ }
+ }
+
+ override fun onWindowInsetsChanged(insets: Insets) {
+ viewBinding.root.updatePadding(
+ left = insets.left,
+ right = insets.right,
+ )
+ viewBinding.recyclerView.updatePadding(
+ bottom = insets.bottom + viewBinding.recyclerView.paddingTop,
+ )
+ }
+
+ override fun onItemClick(item: MangaAlternativeModel, view: View) {
+ when (view.id) {
+ R.id.chip_source -> startActivity(SearchActivity.newIntent(this, item.manga.source, viewModel.manga.title))
+ R.id.button_migrate -> confirmMigration(item.manga)
+ else -> startActivity(DetailsActivity.newIntent(this, item.manga))
+ }
+ }
+
+ private fun confirmMigration(target: Manga) {
+ MaterialAlertDialogBuilder(this, DIALOG_THEME_CENTERED)
+ .setIcon(R.drawable.ic_replace)
+ .setTitle(R.string.manga_migration)
+ .setMessage(
+ getString(
+ R.string.migrate_confirmation,
+ viewModel.manga.title,
+ viewModel.manga.source.title,
+ target.title,
+ target.source.title,
+ ),
+ )
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(R.string.migrate) { _, _ ->
+ viewModel.migrate(target)
+ }.show()
+ }
+
+ companion object {
+
+ fun newIntent(context: Context, manga: Manga) = Intent(context, AlternativesActivity::class.java)
+ .putExtra(MangaIntent.KEY_MANGA, ParcelableManga(manga))
+ }
+}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt
new file mode 100644
index 000000000..b8111042d
--- /dev/null
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/AlternativesViewModel.kt
@@ -0,0 +1,94 @@
+package org.koitharu.kotatsu.alternatives.ui
+
+import androidx.lifecycle.SavedStateHandle
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEmpty
+import kotlinx.coroutines.flow.runningFold
+import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.alternatives.domain.AlternativesUseCase
+import org.koitharu.kotatsu.alternatives.domain.MigrateUseCase
+import org.koitharu.kotatsu.core.model.chaptersCount
+import org.koitharu.kotatsu.core.model.parcelable.ParcelableManga
+import org.koitharu.kotatsu.core.parser.MangaIntent
+import org.koitharu.kotatsu.core.parser.MangaRepository
+import org.koitharu.kotatsu.core.ui.BaseViewModel
+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.list.domain.ListExtraProvider
+import org.koitharu.kotatsu.list.ui.model.EmptyState
+import org.koitharu.kotatsu.list.ui.model.ListModel
+import org.koitharu.kotatsu.list.ui.model.LoadingFooter
+import org.koitharu.kotatsu.list.ui.model.LoadingState
+import org.koitharu.kotatsu.parsers.model.Manga
+import javax.inject.Inject
+
+@HiltViewModel
+class AlternativesViewModel @Inject constructor(
+ savedStateHandle: SavedStateHandle,
+ private val mangaRepositoryFactory: MangaRepository.Factory,
+ private val alternativesUseCase: AlternativesUseCase,
+ private val migrateUseCase: MigrateUseCase,
+ private val extraProvider: ListExtraProvider,
+) : BaseViewModel() {
+
+ val manga = savedStateHandle.require(MangaIntent.KEY_MANGA).manga
+
+ val onMigrated = MutableEventFlow()
+ val content = MutableStateFlow>(listOf(LoadingState))
+ private var migrationJob: Job? = null
+
+ init {
+ launchJob(Dispatchers.Default) {
+ val ref = mangaRepositoryFactory.create(manga.source).getDetails(manga)
+ val refCount = ref.chaptersCount()
+ alternativesUseCase(manga)
+ .map {
+ MangaAlternativeModel(
+ manga = it,
+ progress = extraProvider.getProgress(it.id),
+ referenceChapters = refCount,
+ )
+ }.runningFold>(listOf(LoadingState)) { acc, item ->
+ acc.filterIsInstance() + item + LoadingFooter()
+ }.onEmpty {
+ emit(
+ listOf(
+ EmptyState(
+ icon = R.drawable.ic_empty_common,
+ textPrimary = R.string.nothing_found,
+ textSecondary = R.string.text_search_holder_secondary,
+ actionStringRes = 0,
+ ),
+ ),
+ )
+ }.collect {
+ content.value = it
+ }
+ }
+ }
+
+ fun migrate(target: Manga) {
+ if (migrationJob?.isActive == true) {
+ return
+ }
+ migrationJob = launchLoadingJob(Dispatchers.Default) {
+ migrateUseCase(manga, target)
+ onMigrated.call(target)
+ }
+ }
+
+ private suspend fun mapList(list: List, refCount: Int): List {
+ return list.map {
+ MangaAlternativeModel(
+ manga = it,
+ progress = extraProvider.getProgress(it.id),
+ referenceChapters = refCount,
+ )
+ }
+ }
+}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/MangaAlternativeModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/MangaAlternativeModel.kt
new file mode 100644
index 000000000..18571c17a
--- /dev/null
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/alternatives/ui/MangaAlternativeModel.kt
@@ -0,0 +1,21 @@
+package org.koitharu.kotatsu.alternatives.ui
+
+import org.koitharu.kotatsu.core.model.chaptersCount
+import org.koitharu.kotatsu.list.ui.model.ListModel
+import org.koitharu.kotatsu.parsers.model.Manga
+
+data class MangaAlternativeModel(
+ val manga: Manga,
+ val progress: Float,
+ private val referenceChapters: Int,
+) : ListModel {
+
+ val chaptersCount = manga.chaptersCount()
+
+ val chaptersDiff: Int
+ get() = if (referenceChapters == 0 || chaptersCount == 0) 0 else chaptersCount - referenceChapters
+
+ override fun areItemsTheSame(other: ListModel): Boolean {
+ return other is MangaAlternativeModel && other.manga.id == manga.id
+ }
+}
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 8cf08b944..b3b591f7f 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
@@ -131,3 +131,19 @@ fun MangaChapter.formatNumber(): String? {
}
return chaptersNumberFormat.format(number.toDouble())
}
+
+fun Manga.chaptersCount(): Int {
+ if (chapters.isNullOrEmpty()) {
+ return 0
+ }
+ val counters = MutableObjectIntMap()
+ var max = 0
+ chapters?.forEach { x ->
+ val c = counters.getOrDefault(x.branch, 0) + 1
+ counters[x.branch] = c
+ if (max < c) {
+ max = c
+ }
+ }
+ return max
+}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ChipIconTarget.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ChipIconTarget.kt
new file mode 100644
index 000000000..82b002b86
--- /dev/null
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/ui/image/ChipIconTarget.kt
@@ -0,0 +1,14 @@
+package org.koitharu.kotatsu.core.ui.image
+
+import android.graphics.drawable.Drawable
+import coil.target.GenericViewTarget
+import com.google.android.material.chip.Chip
+
+class ChipIconTarget(override val view: Chip) : GenericViewTarget() {
+
+ override var drawable: Drawable?
+ get() = view.chipIcon
+ set(value) {
+ view.chipIcon = value
+ }
+}
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 0c9be69b9..e544accd9 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
@@ -14,6 +14,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar
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.os.AppShortcutManager
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
@@ -41,6 +42,7 @@ class DetailsMenuProvider(
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_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
@@ -103,6 +105,12 @@ class DetailsMenuProvider(
}
}
+ R.id.action_alternatives -> {
+ viewModel.manga.value?.let {
+ activity.startActivity(AlternativesActivity.newIntent(activity, it))
+ }
+ }
+
R.id.action_stats -> {
viewModel.manga.value?.let {
MangaStatsSheet.show(activity.supportFragmentManager, it)
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt
index 534092ebd..fc5a482c4 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/favourites/ui/categories/FavouriteCategoriesActivity.kt
@@ -178,6 +178,7 @@ class FavouriteCategoriesActivity :
}
}
+ @Deprecated("")
companion object {
fun newIntent(context: Context) = Intent(context, FavouriteCategoriesActivity::class.java)
diff --git a/app/src/main/res/drawable/ic_replace.xml b/app/src/main/res/drawable/ic_replace.xml
new file mode 100644
index 000000000..c7ee3eddd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_replace.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/app/src/main/res/layout/activity_alternatives.xml b/app/src/main/res/layout/activity_alternatives.xml
new file mode 100644
index 000000000..a9a1b8e4b
--- /dev/null
+++ b/app/src/main/res/layout/activity_alternatives.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/item_manga_alternative.xml b/app/src/main/res/layout/item_manga_alternative.xml
new file mode 100644
index 000000000..7b7e8c622
--- /dev/null
+++ b/app/src/main/res/layout/item_manga_alternative.xml
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/opt_details.xml b/app/src/main/res/menu/opt_details.xml
index c525f7c5c..36990cfb8 100644
--- a/app/src/main/res/menu/opt_details.xml
+++ b/app/src/main/res/menu/opt_details.xml
@@ -49,6 +49,12 @@
android:title="@string/find_similar"
app:showAsAction="never" />
+
+
- #66311B92
#FB8C00
#222222
+ #81C784
+ #E57373
#191C1C
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 22dd1c970..6ea2264d8 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -25,6 +25,8 @@
#99B39DDB
#E65100
#FFFFFF
+ #388E3C
+ #D32F2F
#E4FFFA
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 52f610b0f..0aa7ee55a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,603 +1,603 @@
- Kotatsu
- Local storage
- Favourites
- History
- An error occurred
- Network error
- Details
- Chapters
- List
- Detailed list
- Grid
- List mode
- Settings
- Manga sources
- Loading…
- Computing…
- Chapter %1$d of %2$d
- Close
- Try again
- Clear history
- Nothing found
- No history yet
- Read
- No favourites yet
- Favourite this
- New category
- Add
- Save
- Share
- Create shortcut…
- Share %s
- Search
- Search manga
- Downloading…
- Processing…
- Downloaded
- Downloads
- Name
- Popular
- Updated
- Newest
- Rating
- Sorting order
- Filter
- Theme
- Light
- Dark
-
+ Kotatsu
+ Local storage
+ Favourites
+ History
+ An error occurred
+ Network error
+ Details
+ Chapters
+ List
+ Detailed list
+ Grid
+ List mode
+ Settings
+ Manga sources
+ Loading…
+ Computing…
+ Chapter %1$d of %2$d
+ Close
+ Try again
+ Clear history
+ Nothing found
+ No history yet
+ Read
+ No favourites yet
+ Favourite this
+ New category
+ Add
+ Save
+ Share
+ Create shortcut…
+ Share %s
+ Search
+ Search manga
+ Downloading…
+ Processing…
+ Downloaded
+ Downloads
+ Name
+ Popular
+ Updated
+ Newest
+ Rating
+ Sorting order
+ Filter
+ Theme
+ Light
+ Dark
+
Follow system
- Pages
- Clear
- Clear all reading history permanently?
- Remove
- \"%s\" deleted from local storage
- Save page
- Saved
- Share image
- Import
- Delete
- This operation is not supported
- Either pick a ZIP or CBZ file.
- No description
- Clear page cache
- B|kB|MB|GB|TB
- Standard
- Webtoon
- Read mode
- Grid size
- Search on %s
- Delete manga
- Permanently delete \"%s\" from device?
- Reader settings
- Switch pages
- Edge taps
- Volume buttons
- Continue
- Error
- Clear thumbnails cache
- Clear search history
- Cleared
- Gestures only
- Internal storage
- External storage
- Domain
- A new version of the app is available
- Open in web browser
- This manga has %s. Save all of it?
- Save
- Notifications
- %1$d of %2$d on
- New chapters
- Download
- Notifications settings
- Notification sound
- LED indicator
- Vibration
- Favourite categories
- Remove
- It\'s kind of empty here…
- Try to reformulate the query.
- What you read will be displayed here
- Find what to read in the «Explore» section
- Your manga will be displayed here
- Find what to read in the «Explore» section
- Save something first
- Save something from an online catalog or import it from a file.
- Shelf
- Recent
- Page animation
- Downloads folder
- Not available
- No available storage
- Other storage
- Done
- All favourites
- Empty category
- Read later
- Updates
- New chapters of what you are reading are shown here
- Search results
- New version: %s
- Size: %s
- Clear updates feed
- Cleared
- Rotate screen
- Update
- Feed update will start soon
- Look for updates
- Don\'t check
- Enter password
- Wrong password
- Protect the app
- Ask for password when starting Kotatsu
- Repeat the password
- Mismatching passwords
- About
- Version %s
- Check for updates
- No updates available
- Right-to-left
- New category
- Scale mode
- Fit center
- Fit to height
- Fit to width
- Keep at start
- Black
- Uses less power on AMOLED screens
- Backup and restore
- Create data backup
- Restore from backup
- Restored
- Preparing…
- File not found
- All data was restored
- The data was restored, but there are errors
- You can create backup of your history and favourites and restore it
- Just now
- Yesterday
- Long ago
- Group
- Today
- Tap to try again
- The chosen configuration will be remembered for this manga
- Silent
- CAPTCHA required
- Solve
- Clear cookies
- All cookies were removed
- Clear feed
- Clear all update history permanently?
- Check for new chapters
- Reverse
- Sign in
- Sign in to view this content
- Default: %s
- Next
- Enter a password to start the app with
- Confirm
- The password must be 4 characters or more
- Remove all recent search queries permanently?
- Welcome
- Backup saved
- Some devices have different system behavior, which may break background tasks.
- Read more
- Queued
- The chapter is missing
- Translate this app
- Translation
- Authorized
- Logging in on %s is not supported
- You will be logged out from all sources
- Genres
- Finished
- Ongoing
- Default
- Exclude NSFW manga from history
- Numbered pages
- Used sources
- Available sources
- Screenshot policy
- Allow
- Block on NSFW
- Always block
- Suggestions
- Enable suggestions
- Suggest manga based on your preferences
- All data is only analyzed locally on this device and never sent anywhere.
- Start reading manga and you will get personalized suggestions
- Do not suggest NSFW manga
- Enabled
- Disabled
- Unable to load genres list
- Reset filter
- Select languages which you want to read manga. You can change it later in settings.
- Never
- Only on Wi-Fi
- Always
- Preload pages
- Logged in as %s
- 18+
- Various languages
- Find chapter
- No chapters in this manga
- %1$s%%
- Appearance
- Suggestions updating
- Exclude genres
- Specify genres that you do not want to see in the suggestions
- Delete selected items from device permanently?
- Removal completed
- Shikimori
- AniList
- Download slowdown
- Helps avoid blocking your IP address
- Saved manga processing
- Chapters will be removed in the background
- Canceled
- Account already exists
- Back
- Synchronization
- Sync your data
- Enter your email to continue
- Hide
- New manga sources are available
- Check for new chapters and notify about it
- You will receive notifications about updates of manga you are reading
- You will not receive notifications but new chapters will be highlighted in the lists
- Enable notifications
- Name
- Edit
- Edit category
- Tracking
- No favourite categories
- Log out
- Add bookmark
- Remove bookmark
- Bookmarks
- Bookmark removed
- Bookmark added
- Undo
- Removed from history
- DNS over HTTPS
- Default mode
- Autodetect reader mode
- Automatically detect if manga is webtoon
- Disable battery optimization
- Helps with background updates checks
- Something went wrong. Please submit a bug report to the developers to help us fix it.
- Send
- Planned
- Reading
- Re-reading
- Completed
- On hold
- Dropped
- Disable all
- Use fingerprint if available
- Manga from your favourites
- Your recently read manga
- Report
- Show reading progress indicators
- Data deletion
- Show percentage read in history and favourites
- Manga marked as NSFW will never be added to the history and your progress will not be saved
- Can help in case of some issues. All authorizations will be invalidated
- Show all
- Invalid domain
- Select range
- Clear all history
- Last 2 hours
- History cleared
- Manage
- No bookmarks yet
- You can create bookmark while reading manga
- Bookmarks removed
- No manga sources
- Enable manga sources to read manga online
- Random
- Are you sure you want to delete the selected favorite categories?\nAll manga in it will be lost and this cannot be undone.
- Reorder
- Empty
- Explore
- Press "Back" again to exit
- Press "Back" twice to exit the app
- Exit confirmation
- Saved manga
- Pages cache
- Other cache
- Storage usage
- Available
- %s - %s
- Removed from favourites
- Options
- Content not found or removed
- %1$s · %2$s
- Incognito mode
- No chapters
- Automatic scroll
- Ch. %1$d/%2$d Pg. %3$d/%4$d
- Show information bar in reader
- Comics archive
- Folder with images
- Importing manga
- Import completed
- You can delete the original file from storage to save space
- Import will start soon
- Feed
- Error details:<br><tt>%1$s</tt><br><br>1. Try to <a href="%2$s">open manga in a web browser</a> to ensure it is available on its source<br>2. Make sure you are using the <a href="kotatsu://about">latest version of Kotatsu</a><br>3. If it is available, send an error report to the developers.
- Show recent manga shortcuts
- Make recent manga available by long pressing on application icon
- Tapping on the right edge, or pressing the right key, always switches to the next page.
- Ergonomic reader control
- Color correction
- Brightness
- Contrast
- Reset
- The chosen color settings will be remembered for this manga
- Save or discard unsaved changes\?
- Discard
- No space left on device
- Show page switching slider
- Webtoon zoom
- Different languages
- Network is not available
- Turn on Wi-Fi or mobile network to read manga online
- Server side error (%1$d). Please try again later
- Also clear information about new chapters
- Compact
- MyAnimeList
- Source disabled
- Content preloading
- Mark as current
- Language
- Share logs
- Enable logging
- Record some actions for debug purposes. Don\'t turn it on if you\'re not sure what you\'re doing
- Show suspicious content
- Dynamic
- Color scheme
- Show in grid view
- Miku
- Asuka
- Mion
- Rikka
- Sakura
- Mamimi
- Kanade
- There is nothing here
- To track reading progress, select Menu → Track on the manga details screen.
- Services
- Allow unstable updates
- Receive notifications about unstable builds
- Download started
- Got it
- Tap and hold on an item to reorder them
- UserAgent header
- Please restart the application to apply these changes
- You can select one or more .cbz or .zip files, each file will be recognized as a separate manga.
- You can select a directory with archives or images. Each archive (or subdirectory) will be recognized as a chapter.
- Speed
- Import a previously created backup of user data
- Show on the Shelf
- You can sign in into an existing account or create a new one
- Find similar
- Synchronization settings
- Server address
- You can use a self-hosted synchronization server or a default one. Don\'t change this if you\'re not sure what you\'re doing.
- Ignore SSL errors
- Choose mirror automatically
- Automatically switch domains for manga sources on errors if mirrors are available
- Pause
- Resume
- Paused
-
+ Pages
+ Clear
+ Clear all reading history permanently?
+ Remove
+ \"%s\" deleted from local storage
+ Save page
+ Saved
+ Share image
+ Import
+ Delete
+ This operation is not supported
+ Either pick a ZIP or CBZ file.
+ No description
+ Clear page cache
+ B|kB|MB|GB|TB
+ Standard
+ Webtoon
+ Read mode
+ Grid size
+ Search on %s
+ Delete manga
+ Permanently delete \"%s\" from device?
+ Reader settings
+ Switch pages
+ Edge taps
+ Volume buttons
+ Continue
+ Error
+ Clear thumbnails cache
+ Clear search history
+ Cleared
+ Gestures only
+ Internal storage
+ External storage
+ Domain
+ A new version of the app is available
+ Open in web browser
+ This manga has %s. Save all of it?
+ Save
+ Notifications
+ %1$d of %2$d on
+ New chapters
+ Download
+ Notifications settings
+ Notification sound
+ LED indicator
+ Vibration
+ Favourite categories
+ Remove
+ It\'s kind of empty here…
+ Try to reformulate the query.
+ What you read will be displayed here
+ Find what to read in the «Explore» section
+ Your manga will be displayed here
+ Find what to read in the «Explore» section
+ Save something first
+ Save something from an online catalog or import it from a file.
+ Shelf
+ Recent
+ Page animation
+ Downloads folder
+ Not available
+ No available storage
+ Other storage
+ Done
+ All favourites
+ Empty category
+ Read later
+ Updates
+ New chapters of what you are reading are shown here
+ Search results
+ New version: %s
+ Size: %s
+ Clear updates feed
+ Cleared
+ Rotate screen
+ Update
+ Feed update will start soon
+ Look for updates
+ Don\'t check
+ Enter password
+ Wrong password
+ Protect the app
+ Ask for password when starting Kotatsu
+ Repeat the password
+ Mismatching passwords
+ About
+ Version %s
+ Check for updates
+ No updates available
+ Right-to-left
+ New category
+ Scale mode
+ Fit center
+ Fit to height
+ Fit to width
+ Keep at start
+ Black
+ Uses less power on AMOLED screens
+ Backup and restore
+ Create data backup
+ Restore from backup
+ Restored
+ Preparing…
+ File not found
+ All data was restored
+ The data was restored, but there are errors
+ You can create backup of your history and favourites and restore it
+ Just now
+ Yesterday
+ Long ago
+ Group
+ Today
+ Tap to try again
+ The chosen configuration will be remembered for this manga
+ Silent
+ CAPTCHA required
+ Solve
+ Clear cookies
+ All cookies were removed
+ Clear feed
+ Clear all update history permanently?
+ Check for new chapters
+ Reverse
+ Sign in
+ Sign in to view this content
+ Default: %s
+ Next
+ Enter a password to start the app with
+ Confirm
+ The password must be 4 characters or more
+ Remove all recent search queries permanently?
+ Welcome
+ Backup saved
+ Some devices have different system behavior, which may break background tasks.
+ Read more
+ Queued
+ The chapter is missing
+ Translate this app
+ Translation
+ Authorized
+ Logging in on %s is not supported
+ You will be logged out from all sources
+ Genres
+ Finished
+ Ongoing
+ Default
+ Exclude NSFW manga from history
+ Numbered pages
+ Used sources
+ Available sources
+ Screenshot policy
+ Allow
+ Block on NSFW
+ Always block
+ Suggestions
+ Enable suggestions
+ Suggest manga based on your preferences
+ All data is only analyzed locally on this device and never sent anywhere.
+ Start reading manga and you will get personalized suggestions
+ Do not suggest NSFW manga
+ Enabled
+ Disabled
+ Unable to load genres list
+ Reset filter
+ Select languages which you want to read manga. You can change it later in settings.
+ Never
+ Only on Wi-Fi
+ Always
+ Preload pages
+ Logged in as %s
+ 18+
+ Various languages
+ Find chapter
+ No chapters in this manga
+ %1$s%%
+ Appearance
+ Suggestions updating
+ Exclude genres
+ Specify genres that you do not want to see in the suggestions
+ Delete selected items from device permanently?
+ Removal completed
+ Shikimori
+ AniList
+ Download slowdown
+ Helps avoid blocking your IP address
+ Saved manga processing
+ Chapters will be removed in the background
+ Canceled
+ Account already exists
+ Back
+ Synchronization
+ Sync your data
+ Enter your email to continue
+ Hide
+ New manga sources are available
+ Check for new chapters and notify about it
+ You will receive notifications about updates of manga you are reading
+ You will not receive notifications but new chapters will be highlighted in the lists
+ Enable notifications
+ Name
+ Edit
+ Edit category
+ Tracking
+ No favourite categories
+ Log out
+ Add bookmark
+ Remove bookmark
+ Bookmarks
+ Bookmark removed
+ Bookmark added
+ Undo
+ Removed from history
+ DNS over HTTPS
+ Default mode
+ Autodetect reader mode
+ Automatically detect if manga is webtoon
+ Disable battery optimization
+ Helps with background updates checks
+ Something went wrong. Please submit a bug report to the developers to help us fix it.
+ Send
+ Planned
+ Reading
+ Re-reading
+ Completed
+ On hold
+ Dropped
+ Disable all
+ Use fingerprint if available
+ Manga from your favourites
+ Your recently read manga
+ Report
+ Show reading progress indicators
+ Data deletion
+ Show percentage read in history and favourites
+ Manga marked as NSFW will never be added to the history and your progress will not be saved
+ Can help in case of some issues. All authorizations will be invalidated
+ Show all
+ Invalid domain
+ Select range
+ Clear all history
+ Last 2 hours
+ History cleared
+ Manage
+ No bookmarks yet
+ You can create bookmark while reading manga
+ Bookmarks removed
+ No manga sources
+ Enable manga sources to read manga online
+ Random
+ Are you sure you want to delete the selected favorite categories?\nAll manga in it will be lost and this cannot be undone.
+ Reorder
+ Empty
+ Explore
+ Press "Back" again to exit
+ Press "Back" twice to exit the app
+ Exit confirmation
+ Saved manga
+ Pages cache
+ Other cache
+ Storage usage
+ Available
+ %s - %s
+ Removed from favourites
+ Options
+ Content not found or removed
+ %1$s · %2$s
+ Incognito mode
+ No chapters
+ Automatic scroll
+ Ch. %1$d/%2$d Pg. %3$d/%4$d
+ Show information bar in reader
+ Comics archive
+ Folder with images
+ Importing manga
+ Import completed
+ You can delete the original file from storage to save space
+ Import will start soon
+ Feed
+ Error details:<br><tt>%1$s</tt><br><br>1. Try to <a href="%2$s">open manga in a web browser</a> to ensure it is available on its source<br>2. Make sure you are using the <a href="kotatsu://about">latest version of Kotatsu</a><br>3. If it is available, send an error report to the developers.
+ Show recent manga shortcuts
+ Make recent manga available by long pressing on application icon
+ Tapping on the right edge, or pressing the right key, always switches to the next page.
+ Ergonomic reader control
+ Color correction
+ Brightness
+ Contrast
+ Reset
+ The chosen color settings will be remembered for this manga
+ Save or discard unsaved changes\?
+ Discard
+ No space left on device
+ Show page switching slider
+ Webtoon zoom
+ Different languages
+ Network is not available
+ Turn on Wi-Fi or mobile network to read manga online
+ Server side error (%1$d). Please try again later
+ Also clear information about new chapters
+ Compact
+ MyAnimeList
+ Source disabled
+ Content preloading
+ Mark as current
+ Language
+ Share logs
+ Enable logging
+ Record some actions for debug purposes. Don\'t turn it on if you\'re not sure what you\'re doing
+ Show suspicious content
+ Dynamic
+ Color scheme
+ Show in grid view
+ Miku
+ Asuka
+ Mion
+ Rikka
+ Sakura
+ Mamimi
+ Kanade
+ There is nothing here
+ To track reading progress, select Menu → Track on the manga details screen.
+ Services
+ Allow unstable updates
+ Receive notifications about unstable builds
+ Download started
+ Got it
+ Tap and hold on an item to reorder them
+ UserAgent header
+ Please restart the application to apply these changes
+ You can select one or more .cbz or .zip files, each file will be recognized as a separate manga.
+ You can select a directory with archives or images. Each archive (or subdirectory) will be recognized as a chapter.
+ Speed
+ Import a previously created backup of user data
+ Show on the Shelf
+ You can sign in into an existing account or create a new one
+ Find similar
+ Synchronization settings
+ Server address
+ You can use a self-hosted synchronization server or a default one. Don\'t change this if you\'re not sure what you\'re doing.
+ Ignore SSL errors
+ Choose mirror automatically
+ Automatically switch domains for manga sources on errors if mirrors are available
+ Pause
+ Resume
+ Paused
+
Remove completed
- Cancel all
- Download only via Wi-Fi
- Stop downloading when switching to a mobile network
- Suggestion: %s
- Sometimes show notifications with suggested manga
- More
- Enable
- No thanks
- All active downloads will be cancelled, partially downloaded data will be lost
- Your downloads history will be permanently deleted
- You don\'t have any downloads
- Downloads have been resumed
- Downloads have been paused
- Downloads have been removed
- Downloads have been cancelled
- Do you want to receive personalized manga suggestions?
- Translations
- WebView not available: check if WebView provider is installed
- Clear network cache
- Type
- Address
- Port
- Proxy
- Invalid value
- Kitsu
- Enter your email and password to continue
- %1$s (%2$s)
- Downloaded
- Images optimization proxy
- Use the wsrv.nl service to reduce traffic usage and speed up image loading if possible
- Invert colors
- Username
- Password
- Authorization (optional)
- Invalid port number
- Network
- Data and privacy
- Restore previously created backup
- Allow zoom in gesture in webtoon mode
- Show the current time and reading progress at the top of the screen
- Show page numbers in bottom corner
- Animate page switching
- Press and hold the Read button to see more options
- Clear cookies for specified domain only. In most cases will invalidate authorization
- All chapters with translation %s
- The whole manga
- First %s
- Next unread %s
- All unread chapters
- All unread chapters (%s)
- Select chapters manually
- Custom directory
- Pick custom directory
- You have no access to this file or directory
- Local manga directories
- Description
- This month
- Voice search
- Related manga
- Light
- Dark
- White
- Black
- Background
- Data was not restored
- Make sure you have selected the correct backup file
- Manage categories
- Do not update suggestions using metered network connections
- Do not check for new chapters using metered network connections
- Enter manga title, genre or source name
- Progress
- Added
- View list
- Show
- %s requires a captcha to be resolved to work properly
- Languages
- Unknown
- In progress
- Disable NSFW
- Too many requests. Try again later
- Show a list of related manga. In some cases it may be inaccurate or missing
- Advanced
- Default section
- Manga list
- Invalid data is returned or file is corrupted
- On device
- Directories
- Main screen sections
- No more items can be added
- To top
- Moved to top
- Zoom out
- Zoom in
- Show zoom buttons
- Whether to show zoom control buttons in the bottom right corner
- Keep screen on
- Do not turn the screen off while you\'re reading manga
- Dropped
- Reduces banding, but may impact performance
- 32-bit color mode
- Suggest new sources after app update
- Prompt to enable newly added sources after updating the application
- List options
- Relevance
- Categories
- Online variant
- Periodic backups
- Backup creation frequency
- Every day
- Every 2 days
- Once per week
- Twice per month
- Once per month
- Enable periodic backups
- Backups output directory
- Last successful backup: %s
- x%.1f
- Lock screen rotation
- Manga
- Hentai
- Comics
- Other
- %1$s, %2$s
- Sources catalog
- Source enabled
- There are no sources available in this section, or all of it might have been already added.\nStay tuned
- No available manga sources found by your query
- Catalog
- Manage sources
- Manual
- Available: %1$d
- Disable NSFW sources and hide adult manga from list if possible
- Paused
- Reduce memory consumption (beta)
- Reduce offscreen pages quality to use less memory
- State
- Filtering by multiple genres is not supported by this manga source
- Filtering by multiple states is not supported by this manga source
- Search is not supported by this manga source
- You can enable download slowdown for each manga source individually in the source settings if you are having problems with server-side blocking
- Skip
- Grayscale
- Globally
- This manga
- These settings can be applied globally or only to the current manga. If applied globally, individual settings will not be overridden.
- Apply
- Filtering by both genres and locale is not supported by this source
- Filtering by both genres and states is not supported by this source
- Start typing the genre name
- Might help with getting the download started if you have any issues with it
- Please select which content sources you would like to enable. This can also be configured later in settings
- Login to sync account
- Restore
- Backup date: %s
- Upcoming
- Name reversed
- Content rating
- Exclude genres
- Safe
- Suggestive
- Adult
- Default tab
- Mark as completed
- Mark selected manga as completely read?\n\nWarning: current reading progress will be lost.
- This category was hidden from the main screen and is accessible through Menu → Manage categories
- Approximate reading time
- Approximate remaining time
- %1$s %2$s
- Volume %d
- Unknown volume
- Your reading progress will not be saved
- Vertical
- Last read
- Two pages
- Show menu
- Show/hide UI
- Previous chapter
- Next chapter
- Previous page
- Next page
- Reader actions
- Configure actions for tappable screen areas
- Enable volume buttons
- Use volume buttons for switching pages
- Tap action
- Long tap action
- None
- Reset settings to default values? This action cannot be undone.
- Use two pages layout on landscape orientation (beta)
- Default webtoon zoom out
- Fullscreen mode
- Hide system status and navigation bars
- %1$d/%2$d
- Show estimated reading time
- The time estimation value may be inaccurate
- Suggestions feature is disabled
- Checking for new chapters is disabled
- Show labels in navigation bar
- Saving pages
- Ask for the destination dir every time
- Default page save directory
- Remove from history
+ Cancel all
+ Download only via Wi-Fi
+ Stop downloading when switching to a mobile network
+ Suggestion: %s
+ Sometimes show notifications with suggested manga
+ More
+ Enable
+ No thanks
+ All active downloads will be cancelled, partially downloaded data will be lost
+ Your downloads history will be permanently deleted
+ You don\'t have any downloads
+ Downloads have been resumed
+ Downloads have been paused
+ Downloads have been removed
+ Downloads have been cancelled
+ Do you want to receive personalized manga suggestions?
+ Translations
+ WebView not available: check if WebView provider is installed
+ Clear network cache
+ Type
+ Address
+ Port
+ Proxy
+ Invalid value
+ Kitsu
+ Enter your email and password to continue
+ %1$s (%2$s)
+ Downloaded
+ Images optimization proxy
+ Use the wsrv.nl service to reduce traffic usage and speed up image loading if possible
+ Invert colors
+ Username
+ Password
+ Authorization (optional)
+ Invalid port number
+ Network
+ Data and privacy
+ Restore previously created backup
+ Allow zoom in gesture in webtoon mode
+ Show the current time and reading progress at the top of the screen
+ Show page numbers in bottom corner
+ Animate page switching
+ Press and hold the Read button to see more options
+ Clear cookies for specified domain only. In most cases will invalidate authorization
+ All chapters with translation %s
+ The whole manga
+ First %s
+ Next unread %s
+ All unread chapters
+ All unread chapters (%s)
+ Select chapters manually
+ Custom directory
+ Pick custom directory
+ You have no access to this file or directory
+ Local manga directories
+ Description
+ This month
+ Voice search
+ Related manga
+ Light
+ Dark
+ White
+ Black
+ Background
+ Data was not restored
+ Make sure you have selected the correct backup file
+ Manage categories
+ Do not update suggestions using metered network connections
+ Do not check for new chapters using metered network connections
+ Enter manga title, genre or source name
+ Progress
+ Added
+ View list
+ Show
+ %s requires a captcha to be resolved to work properly
+ Languages
+ Unknown
+ In progress
+ Disable NSFW
+ Too many requests. Try again later
+ Show a list of related manga. In some cases it may be inaccurate or missing
+ Advanced
+ Default section
+ Manga list
+ Invalid data is returned or file is corrupted
+ On device
+ Directories
+ Main screen sections
+ No more items can be added
+ To top
+ Moved to top
+ Zoom out
+ Zoom in
+ Show zoom buttons
+ Whether to show zoom control buttons in the bottom right corner
+ Keep screen on
+ Do not turn the screen off while you\'re reading manga
+ Dropped
+ Reduces banding, but may impact performance
+ 32-bit color mode
+ Suggest new sources after app update
+ Prompt to enable newly added sources after updating the application
+ List options
+ Relevance
+ Categories
+ Online variant
+ Periodic backups
+ Backup creation frequency
+ Every day
+ Every 2 days
+ Once per week
+ Twice per month
+ Once per month
+ Enable periodic backups
+ Backups output directory
+ Last successful backup: %s
+ x%.1f
+ Lock screen rotation
+ Manga
+ Hentai
+ Comics
+ Other
+ %1$s, %2$s
+ Sources catalog
+ Source enabled
+ There are no sources available in this section, or all of it might have been already added.\nStay tuned
+ No available manga sources found by your query
+ Catalog
+ Manage sources
+ Manual
+ Available: %1$d
+ Disable NSFW sources and hide adult manga from list if possible
+ Paused
+ Reduce memory consumption (beta)
+ Reduce offscreen pages quality to use less memory
+ State
+ Filtering by multiple genres is not supported by this manga source
+ Filtering by multiple states is not supported by this manga source
+ Search is not supported by this manga source
+ You can enable download slowdown for each manga source individually in the source settings if you are having problems with server-side blocking
+ Skip
+ Grayscale
+ Globally
+ This manga
+ These settings can be applied globally or only to the current manga. If applied globally, individual settings will not be overridden.
+ Apply
+ Filtering by both genres and locale is not supported by this source
+ Filtering by both genres and states is not supported by this source
+ Start typing the genre name
+ Might help with getting the download started if you have any issues with it
+ Please select which content sources you would like to enable. This can also be configured later in settings
+ Login to sync account
+ Restore
+ Backup date: %s
+ Upcoming
+ Name reversed
+ Content rating
+ Exclude genres
+ Safe
+ Suggestive
+ Adult
+ Default tab
+ Mark as completed
+ Mark selected manga as completely read?\n\nWarning: current reading progress will be lost.
+ This category was hidden from the main screen and is accessible through Menu → Manage categories
+ Approximate reading time
+ Approximate remaining time
+ %1$s %2$s
+ Volume %d
+ Unknown volume
+ Your reading progress will not be saved
+ Vertical
+ Last read
+ Two pages
+ Show menu
+ Show/hide UI
+ Previous chapter
+ Next chapter
+ Previous page
+ Next page
+ Reader actions
+ Configure actions for tappable screen areas
+ Enable volume buttons
+ Use volume buttons for switching pages
+ Tap action
+ Long tap action
+ None
+ Reset settings to default values? This action cannot be undone.
+ Use two pages layout on landscape orientation (beta)
+ Default webtoon zoom out
+ Fullscreen mode
+ Hide system status and navigation bars
+ %1$d/%2$d
+ Show estimated reading time
+ The time estimation value may be inaccurate
+ Suggestions feature is disabled
+ Checking for new chapters is disabled
+ Show labels in navigation bar
+ Saving pages
+ Ask for the destination dir every time
+ Default page save directory
+ Remove from history
Location
Preferred download format
Automatic
@@ -618,4 +618,9 @@
Three months
There are no statistics for the selected period
Pages read: %s
+ Alternatives
+ Migrate
+ Manga \"%1$s\" from \"%2$s\" will be replaced with \"%3$s\" from \"%4$s\" in your history and favorites (if present)
+ Manga migration
+ Migration completed