Use Tachi previews info + chapters manga page, plus of course SY features integrated into it

Add missed invert tap settings
Add missed extension open in settings overflow menu option
Cleanup
This commit is contained in:
Jobobby04 2020-07-12 19:21:29 -04:00
parent 8ab2a823b5
commit 372e570fac
90 changed files with 3115 additions and 6190 deletions

View File

@ -67,6 +67,8 @@ class AppModule(val app: Application) : InjektModule {
GlobalScope.launch { get<DownloadManager>() }
// SY -->
GlobalScope.launch { get<CustomMangaManager>() }
// SY <--
}
}

View File

@ -105,6 +105,10 @@ class BackupRestoreService : Service() {
// SY -->
private val throttleManager = EHentaiThrottleManager()
private var skippedAmount = 0
private var totalAmount = 0
// SY <--
/**
@ -117,12 +121,6 @@ class BackupRestoreService : Service() {
*/
private var restoreAmount = 0
// SY -->
private var skippedAmount = 0
private var totalAmount = 0
// SY <--
/**
* Mapping of source ID to source name from backup data
*/

View File

@ -14,7 +14,9 @@ object MangaTypeAdapter {
write {
beginArray()
value(it.url)
// SY -->
value(it.originalTitle)
// SY <--
value(it.source)
value(it.viewer)
value(it.chapter_flags)

View File

@ -52,11 +52,13 @@ class MangaPutResolver : DefaultPutResolver<Manga>() {
put(COL_ID, obj.id)
put(COL_SOURCE, obj.source)
put(COL_URL, obj.url)
// SY -->
put(COL_ARTIST, obj.originalArtist)
put(COL_AUTHOR, obj.originalAuthor)
put(COL_DESCRIPTION, obj.originalDescription)
put(COL_GENRE, obj.originalGenre)
put(COL_TITLE, obj.originalTitle)
// SY <--
put(COL_STATUS, obj.status)
put(COL_THUMBNAIL_URL, obj.thumbnail_url)
put(COL_FAVORITE, obj.favorite)

View File

@ -11,6 +11,7 @@ open class MangaImpl : Manga {
override lateinit var url: String
// SY -->
private val customMangaManager: CustomMangaManager by injectLazy()
override var title: String
@ -39,6 +40,7 @@ open class MangaImpl : Manga {
override var genre: String?
get() = if (favorite) customMangaManager.getManga(this)?.genre ?: ogGenre else ogGenre
set(value) { ogGenre = value }
// SY <--
override var status: Int = 0
@ -58,6 +60,7 @@ open class MangaImpl : Manga {
override var cover_last_modified: Long = 0
// SY -->
lateinit var ogTitle: String
private set
var ogAuthor: String? = null
@ -68,6 +71,7 @@ open class MangaImpl : Manga {
private set
var ogGenre: String? = null
private set
// SY <--
override fun equals(other: Any?): Boolean {
if (this === other) return true

View File

@ -22,7 +22,7 @@ import uy.kohesive.injekt.injectLazy
*
* @param context the application context.
*/
class DownloadManager(val context: Context) {
class DownloadManager(/* SY private */ val context: Context) {
/**
* The sources manager.

View File

@ -118,7 +118,9 @@ class DownloadProvider(private val context: Context) {
* @param manga the manga to query.
*/
fun getMangaDirName(manga: Manga): String {
// SY -->
return DiskUtil.buildValidFilename(manga.originalTitle)
// SY <--
}
/**

View File

@ -67,6 +67,8 @@ object PreferenceKeys {
const val landscapeColumns = "pref_library_columns_landscape_key"
const val jumpToChapters = "jump_to_chapters"
const val updateOnlyNonCompleted = "pref_update_only_non_completed_key"
const val autoUpdateTrack = "pref_auto_update_manga_sync_key"
@ -243,8 +245,6 @@ object PreferenceKeys {
const val eh_is_hentai_enabled = "eh_is_hentai_enabled"
const val eh_use_new_manga_interface = "eh_use_new_manga_interface"
const val eh_use_auto_webtoon = "eh_use_auto_webtoon"
const val eh_watched_list_default_state = "eh_watched_list_default_state"

View File

@ -131,6 +131,8 @@ class PreferencesHelper(val context: Context) {
fun landscapeColumns() = flowPrefs.getInt(Keys.landscapeColumns, 0)
fun jumpToChapters() = prefs.getBoolean(Keys.jumpToChapters, false)
fun updateOnlyNonCompleted() = prefs.getBoolean(Keys.updateOnlyNonCompleted, false)
fun autoUpdateTrack() = prefs.getBoolean(Keys.autoUpdateTrack, true)
@ -347,8 +349,6 @@ class PreferencesHelper(val context: Context) {
fun eh_preload_size() = flowPrefs.getInt(Keys.eh_preload_size, 4)
fun eh_useNewMangaInterface() = flowPrefs.getBoolean(Keys.eh_use_new_manga_interface, true)
fun eh_useAutoWebtoon() = flowPrefs.getBoolean(Keys.eh_use_auto_webtoon, true)
fun eh_watchedListDefaultState() = flowPrefs.getBoolean(Keys.eh_watched_list_default_state, false)

View File

@ -136,6 +136,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
return Observable.just(MangasPage(mangas, false))
}
// SY -->
fun updateMangaInfo(manga: SManga) {
val directory = getBaseDirectories(context).mapNotNull { File(it, manga.url) }.find {
it.exists()
@ -173,6 +174,7 @@ class LocalSource(private val context: Context) : CatalogueSource {
return title.hashCode()
}
}
// SY <--
override fun fetchLatestUpdates(page: Int) = fetchSearchManga(page, "", LATEST_FILTERS)

View File

@ -29,7 +29,9 @@ sealed class Filter<T>(val name: String, var state: T) {
data class Selection(val index: Int, val ascending: Boolean)
}
// SY -->
abstract class AutoComplete(name: String, val hint: String, val values: List<String>, val skipAutoFillTags: List<String> = emptyList(), val excludePrefix: String? = null, state: List<String>) : Filter<List<String>>(name, state)
// SY <--
override fun equals(other: Any?): Boolean {
if (this === other) return true

View File

@ -23,6 +23,7 @@ interface SManga : Serializable {
var initialized: Boolean
// SY -->
val originalTitle: String
get() = (this as? MangaImpl)?.ogTitle ?: title
val originalAuthor: String?
@ -33,6 +34,7 @@ interface SManga : Serializable {
get() = (this as? MangaImpl)?.ogDesc ?: description
val originalGenre: String?
get() = (this as? MangaImpl)?.ogGenre ?: genre
// SY <--
fun copyFrom(other: SManga) {
// EXH -->
@ -42,19 +44,19 @@ interface SManga : Serializable {
// EXH <--
if (other.author != null) {
author = other.originalAuthor
author = /* SY --> */ other.originalAuthor /* SY <-- */
}
if (other.artist != null) {
artist = other.originalArtist
artist = /* SY --> */ other.originalArtist /* SY <-- */
}
if (other.description != null) {
description = other.originalDescription
description = /* SY --> */ other.originalDescription /* SY <-- */
}
if (other.genre != null) {
genre = other.originalGenre
genre = /* SY --> */ other.originalGenre /* SY <-- */
}
if (other.thumbnail_url != null) {

View File

@ -48,7 +48,9 @@ class ExtensionHolder(view: View, override val adapter: ExtensionAdapter) :
extension is Extension.Untrusted -> itemView.context.getString(R.string.ext_untrusted).toUpperCase()
extension is Extension.Installed && extension.isObsolete -> itemView.context.getString(R.string.ext_obsolete).toUpperCase()
extension is Extension.Installed && extension.isUnofficial -> itemView.context.getString(R.string.ext_unofficial).toUpperCase()
// SY -->
extension is Extension.Installed && extension.isRedundant -> itemView.context.getString(R.string.ext_redundant).toUpperCase()
// SY <--
else -> null
}
@ -91,12 +93,14 @@ class ExtensionHolder(view: View, override val adapter: ExtensionAdapter) :
setText(R.string.ext_update)
}
else -> {
// SY -->
if (extension.sources.any { it is ConfigurableSource }) {
@SuppressLint("SetTextI18n")
text = context.getString(R.string.action_settings) + "+"
} else {
setText(R.string.action_settings)
}
// SY <--
}
}
} else if (extension is Extension.Untrusted) {

View File

@ -2,7 +2,10 @@ package eu.kanade.tachiyomi.ui.browse.extension.details
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.Menu
@ -180,6 +183,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
when (item.itemId) {
R.id.action_enable_all -> toggleAllSources(true)
R.id.action_disable_all -> toggleAllSources(false)
R.id.action_open_in_settings -> openInSettings()
}
return super.onOptionsItemSelected(item)
}
@ -204,6 +208,13 @@ class ExtensionDetailsController(bundle: Bundle? = null) :
)
}
private fun openInSettings() {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = Uri.fromParts("package", presenter.pkgName, null)
}
startActivity(intent)
}
private fun Source.isEnabled(): Boolean {
return id.toString() !in preferences.disabledSources().get()
}

View File

@ -17,7 +17,6 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.LatestAdapter
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.LatestPresenter
import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import kotlinx.coroutines.flow.launchIn
@ -72,11 +71,7 @@ open class LatestController :
*/
override fun onMangaClick(manga: Manga) {
// Open MangaController.
if (presenter.preferences.eh_useNewMangaInterface().get()) {
parentController?.router?.pushController(MangaAllInOneController(manga, true).withFadeTransaction())
} else {
parentController?.router?.pushController(MangaController(manga, true).withFadeTransaction())
}
parentController?.router?.pushController(MangaController(manga, true).withFadeTransaction())
}
/**

View File

@ -29,7 +29,6 @@ import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.MigrationMangaDialog
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.launchUI
@ -427,7 +426,7 @@ class MigrationListController(bundle: Bundle? = null) :
private fun navigateOut() {
if (migratingManga?.size == 1) {
launchUI {
val hasDetails = router.backstack.any { it.controller() is MangaController } || router.backstack.any { it.controller() is MangaAllInOneController }
val hasDetails = router.backstack.any { it.controller() is MangaController }
if (hasDetails) {
val manga = migratingManga?.firstOrNull()?.searchResult?.get()?.let {
db.getManga(it).executeOnIO()
@ -435,10 +434,9 @@ class MigrationListController(bundle: Bundle? = null) :
if (manga != null) {
val newStack = router.backstack.filter {
it.controller() !is MangaController &&
it.controller() !is MangaAllInOneController &&
it.controller() !is MigrationListController &&
it.controller() !is PreMigrationController
} + if (preferences.eh_useNewMangaInterface().get()) MangaAllInOneController(manga).withFadeTransaction() else MangaController(manga).withFadeTransaction()
} + MangaController(manga).withFadeTransaction()
router.setBackstack(newStack, FadeChangeHandler())
return@launchUI
}

View File

@ -9,13 +9,11 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.getResourceColor
@ -38,7 +36,6 @@ import kotlinx.android.synthetic.main.migration_process_item.migration_menu
import kotlinx.android.synthetic.main.migration_process_item.skip_manga
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
@ -85,21 +82,12 @@ class MigrationProcessHolder(
withContext(Dispatchers.Main) {
migration_manga_card_from.attachManga(manga, source)
migration_manga_card_from.setOnClickListener {
if (Injekt.get<PreferencesHelper>().eh_useNewMangaInterface().get()) {
adapter.controller.router.pushController(
MangaAllInOneController(
manga,
true
).withFadeTransaction()
)
} else {
adapter.controller.router.pushController(
MangaController(
manga,
true
).withFadeTransaction()
)
}
adapter.controller.router.pushController(
MangaController(
manga,
true
).withFadeTransaction()
)
}
}

View File

@ -54,7 +54,7 @@ class SourceController(bundle: Bundle? = null) :
FlexibleAdapter.OnItemLongClickListener,
SourceAdapter.OnBrowseClickListener,
SourceAdapter.OnLatestClickListener,
ChangeSourceCategoriesDialog.Listener {
/*SY -->*/ ChangeSourceCategoriesDialog.Listener /*SY <--*/ {
private val preferences: PreferencesHelper = Injekt.get()

View File

@ -42,7 +42,6 @@ import eu.kanade.tachiyomi.ui.browse.extension.details.SourcePreferencesControll
import eu.kanade.tachiyomi.ui.browse.source.SourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.SourceFilterSheet.FilterNavigationView.Companion.MAX_SAVED_SEARCHES
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.connectivityManager
@ -304,7 +303,6 @@ open class BrowseSourceController(bundle: Bundle) :
}
// EXH <--
)
filterSheet?.setFilters(presenter.filterItems)
// TODO: [ExtendedFloatingActionButton] hide/show methods don't work properly
@ -713,23 +711,13 @@ open class BrowseSourceController(bundle: Bundle) :
// SY -->
when (mode) {
Mode.CATALOGUE -> {
if (preferences.eh_useNewMangaInterface().get()) {
router.pushController(
MangaAllInOneController(
item.manga,
true,
args.getParcelable(SMART_SEARCH_CONFIG_KEY)
).withFadeTransaction()
)
} else {
router.pushController(
MangaController(
item.manga,
true,
args.getParcelable(SMART_SEARCH_CONFIG_KEY)
).withFadeTransaction()
)
}
router.pushController(
MangaController(
item.manga,
true,
args.getParcelable(MangaController.SMART_SEARCH_CONFIG_EXTRA)
).withFadeTransaction()
)
}
Mode.RECOMMENDS -> openSmartSearch(item.manga.originalTitle)
}

View File

@ -35,14 +35,16 @@ class SourceListHolder(private val view: View, adapter: FlexibleAdapter<*>) :
* @param manga the manga to bind.
*/
override fun onSetValues(manga: Manga) {
title.text = manga.title
title.setTextColor(if (manga.favorite) favoriteColor else unfavoriteColor)
// Set alpha of thumbnail.
thumbnail.alpha = if (manga.favorite) 0.3f else 1.0f
setImage(manga)
}
override fun setImage(manga: Manga) {
title.text = manga.title
GlideApp.with(view.context).clear(thumbnail)
if (!manga.thumbnail_url.isNullOrEmpty()) {

View File

@ -16,7 +16,6 @@ import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
@ -76,14 +75,7 @@ open class GlobalSearchController(
* @param manga clicked item containing manga information.
*/
override fun onMangaClick(manga: Manga) {
// Open MangaController.
// SY -->
if (presenter.preferences.eh_useNewMangaInterface().get()) {
router.pushController(MangaAllInOneController(manga, true).withFadeTransaction())
} else {
router.pushController(MangaController(manga, true).withFadeTransaction())
}
// SY <--
router.pushController(MangaController(manga, true).withFadeTransaction())
}
/**

View File

@ -38,7 +38,6 @@ import eu.kanade.tachiyomi.ui.browse.migration.sources.MigrationSourcesControlle
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.toast
@ -572,13 +571,7 @@ class LibraryController(
// Notify the presenter a manga is being opened.
presenter.onOpenManga()
// SY -->
if (preferences.eh_useNewMangaInterface().get()) {
router.pushController(MangaAllInOneController(manga).withFadeTransaction())
} else {
router.pushController(MangaController(manga).withFadeTransaction())
}
// SY <--
router.pushController(MangaController(manga).withFadeTransaction())
}
/**

View File

@ -36,7 +36,6 @@ import eu.kanade.tachiyomi.ui.browse.BrowseController
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
import eu.kanade.tachiyomi.ui.download.DownloadController
import eu.kanade.tachiyomi.ui.library.LibraryController
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.more.MoreController
import eu.kanade.tachiyomi.ui.recent.history.HistoryController
@ -309,13 +308,7 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
router.popToRoot()
}
setSelectedNavItem(R.id.nav_library)
// SY -->
if (preferences.eh_useNewMangaInterface().get()) {
router.pushController(RouterTransaction.with(MangaAllInOneController(extras)))
} else {
router.pushController(RouterTransaction.with(MangaController(extras)))
}
// SY <--
router.pushController(RouterTransaction.with(MangaController(extras)))
}
SHORTCUT_DOWNLOADS -> {
if (router.backstackSize > 1) {

View File

@ -48,9 +48,9 @@ class EditMangaDialog : DialogController {
private var willResetCover = false
private val infoController
get() = targetController as MangaAllInOneController
get() = targetController as MangaController
constructor(target: MangaAllInOneController, manga: Manga) : super(
constructor(target: MangaController, manga: Manga) : super(
Bundle()
.apply {
putLong(KEY_MANGA, manga.id!!)

View File

@ -1,82 +0,0 @@
package eu.kanade.tachiyomi.ui.manga
import android.content.Context
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.manga.chapter.MangaAllInOneChapterItem
import eu.kanade.tachiyomi.util.system.getResourceColor
import java.text.DateFormat
import java.text.DecimalFormat
import java.text.DecimalFormatSymbols
import java.util.Date
import kotlinx.coroutines.CoroutineScope
import uy.kohesive.injekt.injectLazy
class MangaAllInOneAdapter(
controller: MangaAllInOneController,
context: Context
) : FlexibleAdapter<IFlexible<*>>(null, controller, true) {
val delegate: MangaAllInOneInterface = controller
val preferences: PreferencesHelper by injectLazy()
var items: List<MangaAllInOneChapterItem> = emptyList()
val readColor = context.getResourceColor(R.attr.colorOnSurface, 0.38f)
val unreadColor = context.getResourceColor(R.attr.colorOnSurface)
val bookmarkedColor = context.getResourceColor(R.attr.colorAccent)
val decimalFormat = DecimalFormat(
"#.###",
DecimalFormatSymbols()
.apply { decimalSeparator = '.' }
)
val dateFormat: DateFormat = preferences.dateFormat()
override fun updateDataSet(items: List<IFlexible<*>>?) {
this.items = items as List<MangaAllInOneChapterItem>? ?: emptyList()
super.updateDataSet(items)
}
fun indexOf(item: MangaAllInOneChapterItem): Int {
return items.indexOf(item)
}
interface MangaAllInOneInterface : MangaHeaderInterface
interface MangaHeaderInterface {
fun openSmartSearch()
fun mangaPresenter(): MangaAllInOnePresenter
fun openRecommends()
fun onNextManga(manga: Manga, source: Source, chapters: List<MangaAllInOneChapterItem>, lastUpdateDate: Date, chapterCount: Float)
fun setMangaInfo(manga: Manga, source: Source?, chapters: List<MangaAllInOneChapterItem>, lastUpdateDate: Date, chapterCount: Float)
fun openInWebView()
fun shareManga()
fun fetchMangaFromSource(manualFetch: Boolean = false, fetchManga: Boolean = true, fetchChapters: Boolean = true)
fun onFetchMangaDone()
fun onFetchMangaError(error: Throwable)
fun setRefreshing(value: Boolean)
fun onFavoriteClick()
fun onCategoriesClick()
fun updateCategoriesForMangas(mangas: List<Manga>, categories: List<Category>)
fun performGlobalSearch(query: String)
fun wrapTag(namespace: String, tag: String): String
fun isEHentaiBasedSource(): Boolean
fun performSearch(query: String)
fun openTracking()
suspend fun mergeWithAnother()
fun copyToClipboard(label: String, text: String)
fun migrateManga()
fun isInitialLoadAndFromSource(): Boolean
fun removeInitialLoad()
val controllerScope: CoroutineScope
}
}

View File

@ -1,48 +0,0 @@
package eu.kanade.tachiyomi.ui.manga
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.browse.source.SourceController
class MangaAllInOneHeaderItem(val manga: Manga, val source: Source, var smartSearchConfig: SourceController.SmartSearchConfig? = null) :
AbstractFlexibleItem<MangaAllInOneHolder>() {
override fun getLayoutRes(): Int {
return R.layout.manga_all_in_one_header
}
override fun isSelectable(): Boolean {
return false
}
override fun isSwipeable(): Boolean {
return false
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MangaAllInOneHolder {
return MangaAllInOneHolder(view, adapter as MangaAllInOneAdapter, smartSearchConfig)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: MangaAllInOneHolder,
position: Int,
payloads: MutableList<Any?>?
) {
holder.bind(manga, source)
}
override fun equals(other: Any?): Boolean {
return (this === other)
}
override fun hashCode(): Int {
return -(manga.id).hashCode()
}
}

View File

@ -1,413 +0,0 @@
package eu.kanade.tachiyomi.ui.manga
import android.content.Context
import android.view.View
import androidx.core.content.ContextCompat
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.google.gson.Gson
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.databinding.MangaAllInOneHeaderBinding
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.ui.browse.source.SourceController
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.util.view.visibleIf
import exh.MERGED_SOURCE_ID
import exh.util.setChipsExtended
import java.text.DateFormat
import java.text.DecimalFormat
import java.util.Date
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
class MangaAllInOneHolder(
view: View,
private val adapter: MangaAllInOneAdapter,
smartSearchConfig: SourceController.SmartSearchConfig? = null
) : BaseFlexibleViewHolder(view, adapter) {
private val preferences: PreferencesHelper by injectLazy()
private val gson: Gson by injectLazy()
private val dateFormat: DateFormat by lazy {
preferences.dateFormat()
}
private val sourceManager: SourceManager by injectLazy()
var binding: MangaAllInOneHeaderBinding
init {
val presenter = adapter.delegate.mangaPresenter()
binding = MangaAllInOneHeaderBinding.bind(itemView)
// Setting this via XML doesn't work
binding.mangaCover.clipToOutline = true
binding.btnFavorite.clicks()
.onEach { adapter.delegate.onFavoriteClick() }
.launchIn(adapter.delegate.controllerScope)
if ((Injekt.get<TrackManager>().hasLoggedServices())) {
binding.btnTracking.visible()
}
setTrackingIcon(
Injekt.get<DatabaseHelper>().getTracks(presenter.manga).executeAsBlocking().any {
val status = Injekt.get<TrackManager>().getService(it.sync_id)?.getStatus(it.status)
status != null
}
)
binding.btnTracking.clicks()
.onEach { adapter.delegate.openTracking() }
.launchIn(adapter.delegate.controllerScope)
if (presenter.manga.favorite && presenter.getCategories().isNotEmpty()) {
binding.btnCategories.visible()
}
binding.btnCategories.clicks()
.onEach { adapter.delegate.onCategoriesClick() }
.launchIn(adapter.delegate.controllerScope)
if (presenter.source is HttpSource) {
binding.btnWebview.visible()
binding.btnShare.visible()
binding.btnWebview.clicks()
.onEach { adapter.delegate.openInWebView() }
.launchIn(adapter.delegate.controllerScope)
binding.btnShare.clicks()
.onEach { adapter.delegate.shareManga() }
.launchIn(adapter.delegate.controllerScope)
}
if (presenter.manga.favorite) {
binding.btnMigrate.visible()
binding.btnSmartSearch.visible()
}
binding.btnMigrate.clicks()
.onEach {
adapter.delegate.migrateManga()
}
.launchIn(adapter.delegate.controllerScope)
binding.btnSmartSearch.clicks()
.onEach { adapter.delegate.openSmartSearch() }
.launchIn(adapter.delegate.controllerScope)
binding.mangaFullTitle.longClicks()
.onEach {
adapter.delegate.copyToClipboard(view.context.getString(R.string.title), binding.mangaFullTitle.text.toString())
}
.launchIn(adapter.delegate.controllerScope)
binding.mangaFullTitle.clicks()
.onEach {
adapter.delegate.performGlobalSearch(binding.mangaFullTitle.text.toString())
}
.launchIn(adapter.delegate.controllerScope)
binding.mangaAuthor.longClicks()
.onEach {
// EXH Special case E-Hentai/ExHentai to ignore author field (unused)
if (!adapter.delegate.isEHentaiBasedSource()) {
adapter.delegate.copyToClipboard("author", binding.mangaAuthor.text.toString())
}
}
.launchIn(adapter.delegate.controllerScope)
binding.mangaAuthor.clicks()
.onEach {
// EXH Special case E-Hentai/ExHentai to ignore author field (unused)
if (!adapter.delegate.isEHentaiBasedSource()) {
adapter.delegate.performGlobalSearch(binding.mangaAuthor.text.toString())
}
}
.launchIn(adapter.delegate.controllerScope)
binding.mangaSummary.longClicks()
.onEach {
adapter.delegate.copyToClipboard(view.context.getString(R.string.description), binding.mangaSummary.text.toString())
}
.launchIn(adapter.delegate.controllerScope)
binding.mangaCover.longClicks()
.onEach {
adapter.delegate.copyToClipboard(view.context.getString(R.string.title), presenter.manga.title)
}
.launchIn(adapter.delegate.controllerScope)
// EXH -->
if (smartSearchConfig == null) {
binding.recommendBtn.visible()
binding.recommendBtn.clicks()
.onEach { adapter.delegate.openRecommends() }
.launchIn(adapter.delegate.controllerScope)
}
smartSearchConfig?.let {
if (it.origMangaId != null) { binding.mergeBtn.visible() }
binding.mergeBtn.clicks()
.onEach {
adapter.delegate.mergeWithAnother()
}
.launchIn(adapter.delegate.controllerScope)
}
// EXH <--
}
fun bind(manga: Manga, source: Source?) {
binding.mangaFullTitle.text = if (manga.title.isBlank()) {
itemView.context.getString(R.string.unknown)
} else {
manga.title
}
// Update author/artist TextView.
val authors: MutableSet<String> = mutableSetOf()
val author = manga.author
val artist = manga.artist
val splitRegex = "([,\\-])".toRegex()
if (author != null) {
authors += author.split(splitRegex).map { it.trim() }.filter { !it.isBlank() }.toMutableSet()
}
if (artist != null) {
authors += artist.split(splitRegex).map { it.trim() }.filter { !it.isBlank() }.toMutableSet()
}
binding.mangaAuthor.text = if (authors.isEmpty()) {
itemView.context.getString(R.string.unknown)
} else {
authors.joinToString(", ")
}
// If manga source is known update source TextView.
val mangaSource = source?.toString()
with(binding.mangaSource) {
// EXH -->
when {
mangaSource == null -> {
text = itemView.context.getString(R.string.unknown)
}
source.id == MERGED_SOURCE_ID -> {
text = eu.kanade.tachiyomi.source.online.all.MergedSource.MangaConfig.readFromUrl(gson, manga.url).children.map {
sourceManager.getOrStub(it.source).toString()
}.distinct().joinToString()
}
else -> {
text = mangaSource
setOnClickListener {
val sourceManager = Injekt.get<SourceManager>()
adapter.delegate.performSearch(sourceManager.getOrStub(source.id).name)
}
}
}
// EXH <--
}
// EXH -->
if (source?.id == MERGED_SOURCE_ID) {
binding.mangaSourceLabel.setText(R.string.label_sources)
} else {
binding.mangaSourceLabel.setText(R.string.manga_info_source_label)
}
// EXH <--
// Update status TextView.
binding.mangaStatus.setText(
when (manga.status) {
SManga.ONGOING -> R.string.ongoing
SManga.COMPLETED -> R.string.completed
SManga.LICENSED -> R.string.licensed
else -> R.string.unknown
}
)
setChapterCount(0F)
setLastUpdateDate(Date(0L))
// Set the favorite drawable to the correct one.
setFavoriteButtonState(manga.favorite)
// Set cover if it wasn't already.
val mangaThumbnail = manga.toMangaThumbnail()
GlideApp.with(itemView.context)
.load(mangaThumbnail)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
.into(binding.mangaCover)
GlideApp.with(itemView.context)
.load(mangaThumbnail)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
.into(binding.backdrop)
// Manga info section
if (manga.description.isNullOrBlank() && manga.genre.isNullOrBlank()) {
hideMangaInfo()
} else {
// Update description TextView.
binding.mangaSummary.text = if (manga.description.isNullOrBlank()) {
itemView.context.getString(R.string.unknown)
} else {
manga.description
}
// Update genres list
if (!manga.genre.isNullOrBlank()) {
binding.mangaGenresTagsCompactChips.setChipsExtended(manga.getGenres(), this::performSearch, this::performGlobalSearch, manga.source)
binding.mangaGenresTagsFullChips.setChipsExtended(manga.getGenres(), this::performSearch, this::performGlobalSearch, manga.source)
} else {
binding.mangaGenresTagsWrapper.gone()
}
// Handle showing more or less info
binding.mangaSummary.clicks()
.onEach { toggleMangaInfo(itemView.context) }
.launchIn(adapter.delegate.controllerScope)
binding.mangaInfoToggle.clicks()
.onEach { toggleMangaInfo(itemView.context) }
.launchIn(adapter.delegate.controllerScope)
// Expand manga info if navigated from source listing
if (adapter.delegate.isInitialLoadAndFromSource()) {
adapter.delegate.removeInitialLoad()
toggleMangaInfo(itemView.context)
}
}
}
private fun hideMangaInfo() {
binding.mangaSummaryLabel.gone()
binding.mangaSummary.gone()
binding.mangaGenresTagsWrapper.gone()
binding.mangaInfoToggle.gone()
}
private fun toggleMangaInfo(context: Context) {
val isExpanded = binding.mangaInfoToggle.text == context.getString(R.string.manga_info_collapse)
binding.mangaInfoToggle.text =
if (isExpanded) {
context.getString(R.string.manga_info_expand)
} else {
context.getString(R.string.manga_info_collapse)
}
with(binding.mangaSummary) {
maxLines =
if (isExpanded) {
3
} else {
Int.MAX_VALUE
}
ellipsize =
if (isExpanded) {
android.text.TextUtils.TruncateAt.END
} else {
null
}
}
binding.mangaGenresTagsCompact.visibleIf { isExpanded }
binding.mangaGenresTagsFullChips.visibleIf { !isExpanded }
}
/**
* Update chapter count TextView.
*
* @param count number of chapters.
*/
fun setChapterCount(count: Float) {
if (count > 0f) {
binding.mangaChapters.text = DecimalFormat("#.#").format(count)
} else {
binding.mangaChapters.text = itemView.context.getString(R.string.unknown)
}
}
fun setLastUpdateDate(date: Date) {
if (date.time != 0L) {
binding.mangaLastUpdate.text = dateFormat.format(date)
} else {
binding.mangaLastUpdate.text = itemView.context.getString(R.string.unknown)
}
}
/**
* Toggles the favorite status and asks for confirmation to delete downloaded chapters.
*/
fun toggleFavorite() {
val presenter = adapter.delegate.mangaPresenter()
val isNowFavorite = presenter.toggleFavorite()
if (!isNowFavorite && presenter.hasDownloads()) {
itemView.snack(itemView.context.getString(R.string.delete_downloads_for_manga)) {
setAction(R.string.action_delete) {
presenter.deleteDownloads()
}
}
}
binding.btnCategories.visibleIf { isNowFavorite && presenter.getCategories().isNotEmpty() }
if (isNowFavorite) {
binding.btnSmartSearch.visible()
binding.btnMigrate.visible()
} else {
binding.btnSmartSearch.gone()
binding.btnMigrate.gone()
}
}
/**
* Update favorite button with correct drawable and text.
*
* @param isFavorite determines if manga is favorite or not.
*/
fun setFavoriteButtonState(isFavorite: Boolean) {
// Set the Favorite drawable to the correct one.
// Border drawable if false, filled drawable if true.
binding.btnFavorite.apply {
icon = ContextCompat.getDrawable(context, if (isFavorite) R.drawable.ic_favorite_24dp else R.drawable.ic_favorite_border_24dp)
text = context.getString(if (isFavorite) R.string.in_library else R.string.add_to_library)
isChecked = isFavorite
}
}
private fun performSearch(query: String) {
adapter.delegate.performSearch(query)
}
private fun performGlobalSearch(query: String) {
adapter.delegate.performGlobalSearch(query)
}
fun setTrackingIcon(tracked: Boolean) {
if (tracked) {
binding.btnTracking.setIconResource(R.drawable.ic_cloud_24dp)
}
}
}

View File

@ -1,9 +1,11 @@
package eu.kanade.tachiyomi.ui.manga
import android.content.Context
import android.net.Uri
import android.os.Bundle
import com.google.gson.Gson
import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
@ -16,19 +18,17 @@ import eu.kanade.tachiyomi.data.library.CustomMangaManager
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.source.SourceController
import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersPresenter
import eu.kanade.tachiyomi.ui.manga.chapter.MangaAllInOneChapterItem
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.prepUpdateCover
import eu.kanade.tachiyomi.util.removeCovers
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import eu.kanade.tachiyomi.util.updateCoverLastModified
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.MERGED_SOURCE_ID
import exh.debug.DebugToggles
import exh.eh.EHentaiUpdateHelper
@ -36,11 +36,7 @@ import exh.isEhBasedSource
import exh.util.await
import exh.util.trimOrNull
import java.util.Date
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import rx.Observable
import rx.Subscription
@ -51,36 +47,35 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
/**
* Presenter of MangaInfoFragment.
* Contains information and data for fragment.
* Observable updates should be called from here.
*/
class MangaAllInOnePresenter(
val controller: MangaAllInOneController,
class MangaPresenter(
val manga: Manga,
val source: Source,
val smartSearchConfig: SourceController.SmartSearchConfig?,
val preferences: PreferencesHelper = Injekt.get(),
private val db: DatabaseHelper = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(),
private val gson: Gson = Injekt.get(),
val preferences: PreferencesHelper = Injekt.get()
) : BasePresenter<MangaAllInOneController>() {
// SY -->
private val gson: Gson = Injekt.get()
// SY <--
) : BasePresenter<MangaController>() {
/**
* Subscription to update the manga from the source.
*/
private var fetchMangaSubscription: Subscription? = null
/**
* List of chapters of the manga. It's always unfiltered and unsorted.
*/
var chapters: List<MangaAllInOneChapterItem> = emptyList()
var chapters: List<ChapterItem> = emptyList()
private set
private var lastUpdateDate: Date = Date(0L)
private var chapterCount: Float = 0F
private val scope = CoroutineScope(Job() + Dispatchers.Default)
private val customMangaManager: CustomMangaManager by injectLazy()
/**
* Subject of list of chapters to allow updating the view without going to DB.
*/
private val chaptersRelay: PublishRelay<List<ChapterItem>> by lazy {
PublishRelay.create<List<ChapterItem>>()
}
/**
* Whether the chapter list has been requested to the source.
@ -88,120 +83,124 @@ class MangaAllInOnePresenter(
var hasRequested = false
private set
/**
* Subscription to retrieve the new list of chapters from the source.
*/
private var fetchChaptersSubscription: Subscription? = null
/**
* Subscription to observe download status changes.
*/
private var observeDownloadsSubscription: Subscription? = null
// EXH -->
private val customMangaManager: CustomMangaManager by injectLazy()
private val updateHelper: EHentaiUpdateHelper by injectLazy()
private val redirectUserRelay = BehaviorRelay.create<ChaptersPresenter.EXHRedirect>()
// EXH <--
private val redirectUserRelay = BehaviorRelay.create<EXHRedirect>()
var headerItem = MangaAllInOneHeaderItem(manga, source, smartSearchConfig)
data class EXHRedirect(val manga: Manga, val update: Boolean)
// EXH <--
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
updateManga()
// Manga info - start
// Listen for download status changes
observeDownloads()
getMangaObservable()
.subscribeLatestCache({ view, manga -> view.onNextMangaInfo(manga, source) })
// Prepare the relay.
chaptersRelay.flatMap { applyChapterFilters(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(MangaController::onNextChapters) { _, error -> Timber.e(error) }
// Manga info - end
// Chapters list - start
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
// changes, and sends the list of chapters to the relay.
add(
db.getChapters(manga).asRxObservable().subscribe {
scope.launch(Dispatchers.IO) {
updateChaptersView(updateInfo = true)
db.getChapters(manga).asRxObservable()
.map { chapters ->
// Convert every chapter to a model.
chapters.map { it.toModel() }
}
}
)
}
.doOnNext { chapters ->
// Find downloaded chapters
setDownloadedChapters(chapters)
private suspend fun updateChapters() {
val chapters = db.getChapters(manga).await().map { it.toModel() }
// Store the last emission
this.chapters = chapters
// Find downloaded chapters
setDownloadedChapters(chapters)
// Listen for download status changes
observeDownloads()
// EXH -->
if (chapters.isNotEmpty() && (source.isEhBasedSource()) && DebugToggles.ENABLE_EXH_ROOT_REDIRECT.enabled) {
// Check for gallery in library and accept manga with lowest id
// Find chapters sharing same root
add(
updateHelper.findAcceptedRootAndDiscardOthers(manga.source, chapters)
.subscribeOn(Schedulers.io())
.subscribe { (acceptedChain, _) ->
// Redirect if we are not the accepted root
if (manga.id != acceptedChain.manga.id) {
// Update if any of our chapters are not in accepted manga's chapters
val ourChapterUrls = chapters.map { it.url }.toSet()
val acceptedChapterUrls = acceptedChain.chapters.map { it.url }.toSet()
val update = (ourChapterUrls - acceptedChapterUrls).isNotEmpty()
redirectUserRelay.call(
ChaptersPresenter.EXHRedirect(
acceptedChain.manga,
update
)
)
}
// SY -->
if (chapters.isNotEmpty() && (source.isEhBasedSource()) && DebugToggles.ENABLE_EXH_ROOT_REDIRECT.enabled) {
// Check for gallery in library and accept manga with lowest id
// Find chapters sharing same root
add(
updateHelper.findAcceptedRootAndDiscardOthers(manga.source, chapters)
.subscribeOn(Schedulers.io())
.subscribe { (acceptedChain, _) ->
// Redirect if we are not the accepted root
if (manga.id != acceptedChain.manga.id) {
// Update if any of our chapters are not in accepted manga's chapters
val ourChapterUrls = chapters.map { it.url }.toSet()
val acceptedChapterUrls = acceptedChain.chapters.map { it.url }.toSet()
val update = (ourChapterUrls - acceptedChapterUrls).isNotEmpty()
redirectUserRelay.call(
EXHRedirect(
acceptedChain.manga,
update
)
)
}
}
)
}
// SY <--
}
.subscribe { chaptersRelay.call(it) }
)
// Chapters list - end
}
// Manga info - start
private fun getMangaObservable(): Observable<Manga> {
return db.getManga(manga.url, manga.source).asRxObservable()
.observeOn(AndroidSchedulers.mainThread())
}
/**
* Fetch manga information from source.
*/
fun fetchMangaFromSource(manualFetch: Boolean = false) {
if (!fetchMangaSubscription.isNullOrUnsubscribed()) return
fetchMangaSubscription = Observable.defer { source.fetchMangaDetails(manga) }
.map { networkManga ->
manga.prepUpdateCover(coverCache, networkManga, manualFetch)
manga.copyFrom(networkManga)
manga.initialized = true
db.insertManga(manga).executeAsBlocking()
manga
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
{ view, _ ->
view.onFetchMangaInfoDone()
},
MangaController::onFetchMangaInfoError
)
}
// EXH <--
this.chapters = chapters
}
private fun getUpdatedChapters(): List<MangaAllInOneChapterItem> = applyChapterFilters(chapters)
private fun updateChaptersView(updateInfo: Boolean = false) {
scope.launch(Dispatchers.IO) {
updateChapters()
val chapterList = getUpdatedChapters()
if (updateInfo) {
updateChapterInfo()
}
withContext(Dispatchers.Main) {
Observable.just(manga)
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache({ view, manga -> view.onNextManga(manga, source, chapterList, lastUpdateDate, chapterCount) })
}
}
}
private fun updateChapterInfo() {
scope.launch(Dispatchers.IO) {
lastUpdateDate = Date(
chapters.maxBy { it.date_upload }?.date_upload ?: 0
)
chapterCount = chapters.maxBy { it.chapter_number }?.chapter_number ?: 0f
}
}
private fun updateManga(updateInfo: Boolean = true) {
scope.launch(Dispatchers.IO) {
var manga2: Manga? = null
var chapterList = getUpdatedChapters()
if (updateInfo) {
manga2 = db.getManga(manga.url, manga.source).await()
updateChapters()
updateChapterInfo()
chapterList = getUpdatedChapters()
}
withContext(Dispatchers.Main) {
if (manga2 != null) {
Observable.just(manga2)
} else {
Observable.just(manga)
}.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache({ view, manga -> view.onNextManga(manga, source, chapterList, lastUpdateDate, chapterCount) })
}
}
}
// SY -->
fun updateMangaInfo(
title: String?,
author: String?,
@ -240,14 +239,28 @@ class MangaAllInOnePresenter(
if (uri != null) {
editCoverWithStream(uri)
} else if (resetCover) {
controller.setRefreshing(true)
coverCache.deleteCustomCover(manga)
}
if (uri == null && resetCover) {
fetchMangaFromSource(manualFetch = true, fetchChapters = false)
Observable.just(manga)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(
{ view, _ ->
view.setRefreshing()
}
)
fetchMangaFromSource(manualFetch = true)
} else {
updateManga(updateInfo = false)
Observable.just(manga)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(
{ view, _ ->
view.onNextMangaInfo(manga, source)
}
)
}
}
@ -268,136 +281,6 @@ class MangaAllInOnePresenter(
return false
}
/**
* Fetch manga information from source.
*/
fun fetchMangaFromSource(manualFetch: Boolean = false, fetchManga: Boolean = true, fetchChapters: Boolean = true) {
if (fetchChapters) {
hasRequested = true
}
scope.launch(Dispatchers.IO) {
if (fetchManga) {
val networkManga = try {
source.fetchMangaDetails(manga).toBlocking().single()
} catch (e: Exception) {
controller.onFetchMangaError(e)
return@launch
}
if (networkManga != null) {
manga.prepUpdateCover(coverCache, networkManga, manualFetch)
manga.copyFrom(networkManga)
manga.initialized = true
db.insertManga(manga).await()
}
}
var chapters: List<SChapter> = listOf()
if (fetchChapters) {
try {
chapters = source.fetchChapterList(manga).toBlocking().single()
} catch (e: Exception) {
controller.onFetchMangaError(e)
return@launch
}
}
try {
if (fetchChapters) {
val chapterLists = syncChaptersWithSource(db, chapters, manga, source)
if (manualFetch) {
downloadNewChapters(chapterLists.first)
}
updateChapters()
updateChapterInfo()
}
withContext(Dispatchers.Main) {
updateManga(updateInfo = false)
controller.onFetchMangaDone()
}
} catch (e: Exception) {
controller.onFetchMangaError(e)
}
}
}
/**
* Update favorite status of manga, (removes / adds) manga (to / from) library.
*
* @return the new status of the manga.
*/
fun toggleFavorite(): Boolean {
manga.favorite = !manga.favorite
controller.setFavoriteButtonState(manga.favorite)
if (!manga.favorite) {
manga.removeCovers(coverCache)
}
db.insertManga(manga).executeAsBlocking()
return manga.favorite
}
private fun setFavorite(favorite: Boolean) {
if (manga.favorite == favorite) {
return
}
toggleFavorite()
}
/**
* Returns true if the manga has any downloads.
*/
fun hasDownloads(): Boolean {
return downloadManager.getDownloadCount(manga) > 0
}
/**
* Deletes all the downloads for the manga.
*/
fun deleteDownloads() {
downloadManager.deleteManga(manga, source)
}
/**
* Get user categories.
*
* @return List of categories, not including the default category
*/
fun getCategories(): List<Category> {
return db.getCategories().executeAsBlocking()
}
/**
* Gets the category id's the manga is in, if the manga is not in a category, returns the default id.
*
* @param manga the manga to get categories from.
* @return Array of category ids the manga is in, if none returns default id
*/
fun getMangaCategoryIds(manga: Manga): Array<Int> {
val categories = db.getCategoriesForManga(manga).executeAsBlocking()
return categories.mapNotNull { it.id }.toTypedArray()
}
/**
* Move the given manga to categories.
*
* @param manga the manga to move.
* @param categories the selected categories.
*/
fun moveMangaToCategories(manga: Manga, categories: List<Category>) {
val mc = categories.filter { it.id != 0 }.map { MangaCategory.create(manga, it) }
db.setMangaCategories(mc, listOf(manga))
}
/**
* Move the given manga to the category.
*
* @param manga the manga to move.
* @param category the selected category, or null for default category.
*/
fun moveMangaToCategory(manga: Manga, category: Category?) {
moveMangaToCategories(manga, listOfNotNull(category))
}
suspend fun smartSearchMerge(manga: Manga, originalMangaId: Long): Manga {
val originalManga = db.getManga(originalMangaId).await()
?: throw IllegalArgumentException("Unknown manga ID: $originalMangaId")
@ -458,6 +341,126 @@ class MangaAllInOnePresenter(
return toInsert
}
// SY <--
/**
* Update favorite status of manga, (removes / adds) manga (to / from) library.
*
* @return the new status of the manga.
*/
fun toggleFavorite(): Boolean {
manga.favorite = !manga.favorite
manga.date_added = when (manga.favorite) {
true -> Date().time
false -> 0
}
if (!manga.favorite) {
manga.removeCovers(coverCache)
}
db.insertManga(manga).executeAsBlocking()
return manga.favorite
}
/**
* Returns true if the manga has any downloads.
*/
fun hasDownloads(): Boolean {
return downloadManager.getDownloadCount(manga) > 0
}
/**
* Deletes all the downloads for the manga.
*/
fun deleteDownloads() {
downloadManager.deleteManga(manga, source)
}
/**
* Get user categories.
*
* @return List of categories, not including the default category
*/
fun getCategories(): List<Category> {
return db.getCategories().executeAsBlocking()
}
/**
* Gets the category id's the manga is in, if the manga is not in a category, returns the default id.
*
* @param manga the manga to get categories from.
* @return Array of category ids the manga is in, if none returns default id
*/
fun getMangaCategoryIds(manga: Manga): Array<Int> {
val categories = db.getCategoriesForManga(manga).executeAsBlocking()
return categories.mapNotNull { it.id }.toTypedArray()
}
/**
* Move the given manga to categories.
*
* @param manga the manga to move.
* @param categories the selected categories.
*/
fun moveMangaToCategories(manga: Manga, categories: List<Category>) {
val mc = categories.filter { it.id != 0 }.map { MangaCategory.create(manga, it) }
db.setMangaCategories(mc, listOf(manga))
}
/**
* Move the given manga to the category.
*
* @param manga the manga to move.
* @param category the selected category, or null for default category.
*/
fun moveMangaToCategory(manga: Manga, category: Category?) {
moveMangaToCategories(manga, listOfNotNull(category))
}
/**
* Update cover with local file.
*
* @param manga the manga edited.
* @param context Context.
* @param data uri of the cover resource.
*/
fun editCover(manga: Manga, context: Context, data: Uri) {
Observable
.fromCallable {
context.contentResolver.openInputStream(data)?.use {
if (manga.isLocal()) {
LocalSource.updateCover(context, manga, it)
manga.updateCoverLastModified(db)
} else if (manga.favorite) {
coverCache.setCustomCoverToCache(manga, it)
manga.updateCoverLastModified(db)
}
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
{ view, _ -> view.onSetCoverSuccess() },
{ view, e -> view.onSetCoverError(e) }
)
}
fun deleteCustomCover(manga: Manga) {
Observable
.fromCallable {
coverCache.deleteCustomCover(manga)
manga.updateCoverLastModified(db)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
{ view, _ -> view.onSetCoverSuccess() },
{ view, e -> view.onSetCoverError(e) }
)
}
// Manga info - end
// Chapters list - start
private fun observeDownloads() {
observeDownloadsSubscription?.let { remove(it) }
@ -465,7 +468,7 @@ class MangaAllInOnePresenter(
.observeOn(AndroidSchedulers.mainThread())
.filter { download -> download.manga.id == manga.id }
.doOnNext { onDownloadStatusChange(it) }
.subscribeLatestCache(MangaAllInOneController::onChapterStatusChange) { _, error ->
.subscribeLatestCache(MangaController::onChapterStatusChange) { _, error ->
Timber.e(error)
}
}
@ -473,9 +476,9 @@ class MangaAllInOnePresenter(
/**
* Converts a chapter from the database to an extended model, allowing to store new fields.
*/
private fun Chapter.toModel(): MangaAllInOneChapterItem {
private fun Chapter.toModel(): ChapterItem {
// Create the model object.
val model = MangaAllInOneChapterItem(this, manga)
val model = ChapterItem(this, manga)
// Find an active download for this chapter.
val download = downloadManager.queue.find { it.chapter.id == id }
@ -492,29 +495,60 @@ class MangaAllInOnePresenter(
*
* @param chapters the list of chapter from the database.
*/
private fun setDownloadedChapters(chapters: List<MangaAllInOneChapterItem>) {
private fun setDownloadedChapters(chapters: List<ChapterItem>) {
chapters
.filter { downloadManager.isChapterDownloaded(it, manga) }
.forEach { it.status = Download.DOWNLOADED }
}
/**
* Requests an updated list of chapters from the source.
*/
fun fetchChaptersFromSource(manualFetch: Boolean = false) {
hasRequested = true
if (!fetchChaptersSubscription.isNullOrUnsubscribed()) return
fetchChaptersSubscription = Observable.defer { source.fetchChapterList(manga) }
.subscribeOn(Schedulers.io())
.map { syncChaptersWithSource(db, it, manga, source) }
.doOnNext {
if (manualFetch) {
downloadNewChapters(it.first)
}
}
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
{ view, _ ->
view.onFetchChaptersDone()
},
MangaController::onFetchChaptersError
)
}
/**
* Updates the UI after applying the filters.
*/
private fun refreshChapters() {
chaptersRelay.call(chapters)
}
/**
* Applies the view filters to the list of chapters obtained from the database.
* @param chapters the list of chapters from the database
* @return an observable of the list of chapters filtered and sorted.
*/
private fun applyChapterFilters(chapterList: List<MangaAllInOneChapterItem>): List<MangaAllInOneChapterItem> {
var chapters = chapterList
private fun applyChapterFilters(chapters: List<ChapterItem>): Observable<List<ChapterItem>> {
var observable = Observable.from(chapters).subscribeOn(Schedulers.io())
if (onlyUnread()) {
chapters = chapters.filter { !it.read }
observable = observable.filter { !it.read }
} else if (onlyRead()) {
chapters = chapters.filter { it.read }
observable = observable.filter { it.read }
}
if (onlyDownloaded()) {
chapters = chapters.filter { it.isDownloaded || it.manga.source == LocalSource.ID }
observable = observable.filter { it.isDownloaded || it.manga.isLocal() }
}
if (onlyBookmarked()) {
chapters = chapters.filter { it.bookmark }
observable = observable.filter { it.bookmark }
}
val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
Manga.SORTING_SOURCE -> when (sortDescending()) {
@ -529,10 +563,9 @@ class MangaAllInOnePresenter(
true -> { c1, c2 -> c2.date_upload.compareTo(c1.date_upload) }
false -> { c1, c2 -> c1.date_upload.compareTo(c2.date_upload) }
}
else -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
else -> throw NotImplementedError("Unimplemented sorting method")
}
chapters = chapters.sortedWith(Comparator(sortFunction))
return chapters
return observable.toSortedList(sortFunction)
}
/**
@ -551,14 +584,14 @@ class MangaAllInOnePresenter(
// Force UI update if downloaded filter active and download finished.
if (onlyDownloaded() && download.status == Download.DOWNLOADED) {
updateChaptersView()
refreshChapters()
}
}
/**
* Returns the next unread chapter or null if everything is read.
*/
fun getNextUnreadChapter(): MangaAllInOneChapterItem? {
fun getNextUnreadChapter(): ChapterItem? {
return chapters.sortedByDescending { it.source_order }.find { !it.read }
}
@ -567,21 +600,22 @@ class MangaAllInOnePresenter(
* @param selectedChapters the list of selected chapters.
* @param read whether to mark chapters as read or unread.
*/
fun markChaptersRead(selectedChapters: List<MangaAllInOneChapterItem>, read: Boolean) {
Observable.from(selectedChapters)
.doOnNext { chapter ->
chapter.read = read
if (!read /* --> EH */ && !preferences
.eh_preserveReadingPosition()
.get() /* <-- EH */
) {
chapter.last_page_read = 0
}
fun markChaptersRead(selectedChapters: List<ChapterItem>, read: Boolean) {
val chapters = selectedChapters.map { chapter ->
chapter.read = read
if (!read) {
chapter.last_page_read = 0
}
.toList()
.flatMap { db.updateChaptersProgress(it).asRxObservable() }
.subscribeOn(Schedulers.io())
.subscribe()
chapter
}
launchIO {
db.updateChaptersProgress(chapters).executeAsBlocking()
if (preferences.removeAfterMarkedAsRead()) {
deleteChapters(chapters)
}
}
}
/**
@ -596,7 +630,7 @@ class MangaAllInOnePresenter(
* Bookmarks the given list of chapters.
* @param selectedChapters the list of chapters to bookmark.
*/
fun bookmarkChapters(selectedChapters: List<MangaAllInOneChapterItem>, bookmarked: Boolean) {
fun bookmarkChapters(selectedChapters: List<ChapterItem>, bookmarked: Boolean) {
Observable.from(selectedChapters)
.doOnNext { chapter ->
chapter.bookmark = bookmarked
@ -611,22 +645,22 @@ class MangaAllInOnePresenter(
* Deletes the given list of chapter.
* @param chapters the list of chapters to delete.
*/
fun deleteChapters(chapters: List<MangaAllInOneChapterItem>) {
fun deleteChapters(chapters: List<ChapterItem>) {
Observable.just(chapters)
.doOnNext { deleteChaptersInternal(chapters) }
.doOnNext { if (onlyDownloaded()) updateChaptersView() }
.doOnNext { if (onlyDownloaded()) refreshChapters() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
{ view, _ ->
view.onChaptersDeleted(chapters)
},
MangaAllInOneController::onChaptersDeletedError
MangaController::onChaptersDeletedError
)
}
private fun downloadNewChapters(chapters: List<Chapter>) {
if (chapters.isEmpty() || !manga.shouldDownloadNewChapters(db, preferences) /* SY --> */ || manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID/* SY <-- */) return
if (chapters.isEmpty() || !manga.shouldDownloadNewChapters(db, preferences)) return
downloadChapters(chapters)
}
@ -635,7 +669,7 @@ class MangaAllInOnePresenter(
* Deletes a list of chapters from disk. This method is called in a background thread.
* @param chapters the chapters to delete.
*/
private fun deleteChaptersInternal(chapters: List<MangaAllInOneChapterItem>) {
private fun deleteChaptersInternal(chapters: List<ChapterItem>) {
downloadManager.deleteChapters(chapters, manga, source)
chapters.forEach {
it.status = Download.NOT_DOWNLOADED
@ -646,10 +680,10 @@ class MangaAllInOnePresenter(
/**
* Reverses the sorting and requests an UI update.
*/
fun revertSortOrder() {
fun reverseSortOrder() {
manga.setChapterOrder(if (sortDescending()) Manga.SORT_ASC else Manga.SORT_DESC)
db.updateFlags(manga).executeAsBlocking()
updateChaptersView()
refreshChapters()
}
/**
@ -659,7 +693,7 @@ class MangaAllInOnePresenter(
fun setUnreadFilter(onlyUnread: Boolean) {
manga.readFilter = if (onlyUnread) Manga.SHOW_UNREAD else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking()
updateChaptersView()
refreshChapters()
}
/**
@ -669,7 +703,7 @@ class MangaAllInOnePresenter(
fun setReadFilter(onlyRead: Boolean) {
manga.readFilter = if (onlyRead) Manga.SHOW_READ else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking()
updateChaptersView()
refreshChapters()
}
/**
@ -679,7 +713,7 @@ class MangaAllInOnePresenter(
fun setDownloadedFilter(onlyDownloaded: Boolean) {
manga.downloadedFilter = if (onlyDownloaded) Manga.SHOW_DOWNLOADED else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking()
updateChaptersView()
refreshChapters()
}
/**
@ -689,7 +723,7 @@ class MangaAllInOnePresenter(
fun setBookmarkedFilter(onlyBookmarked: Boolean) {
manga.bookmarkedFilter = if (onlyBookmarked) Manga.SHOW_BOOKMARKED else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking()
updateChaptersView()
refreshChapters()
}
/**
@ -700,14 +734,7 @@ class MangaAllInOnePresenter(
manga.downloadedFilter = Manga.SHOW_ALL
manga.bookmarkedFilter = Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking()
updateChaptersView()
}
/**
* Adds manga to library
*/
fun addToLibrary() {
setFavorite(true)
refreshChapters()
}
/**
@ -726,7 +753,7 @@ class MangaAllInOnePresenter(
fun setSorting(sort: Int) {
manga.sorting = sort
db.updateFlags(manga).executeAsBlocking()
updateChaptersView()
refreshChapters()
}
/**
@ -770,4 +797,6 @@ class MangaAllInOnePresenter(
fun sortDescending(): Boolean {
return manga.sortDescending()
}
// Chapters list - end
}

View File

@ -8,7 +8,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneAdapter
import eu.kanade.tachiyomi.util.view.visibleIf
import java.util.Date
import kotlinx.android.synthetic.main.chapters_item.bookmark_icon
@ -77,65 +76,3 @@ class ChapterHolder(
}
}
}
class MangaAllInOneChapterHolder(
view: View,
private val adapter: MangaAllInOneAdapter
) : BaseFlexibleViewHolder(view, adapter) {
fun bind(item: MangaAllInOneChapterItem, manga: Manga) {
val chapter = item.chapter
chapter_title.text = when (manga.displayMode) {
Manga.DISPLAY_NUMBER -> {
val number = adapter.decimalFormat.format(chapter.chapter_number.toDouble())
itemView.context.getString(R.string.display_mode_chapter, number)
}
else -> chapter.name
}
// Set correct text color
val chapterColor = when {
chapter.read -> adapter.readColor
chapter.bookmark -> adapter.bookmarkedColor
else -> adapter.unreadColor
}
chapter_title.setTextColor(chapterColor)
chapter_description.setTextColor(chapterColor)
bookmark_icon.visibleIf { chapter.bookmark }
val descriptions = mutableListOf<CharSequence>()
if (chapter.date_upload > 0) {
descriptions.add(adapter.dateFormat.format(Date(chapter.date_upload)))
}
if (!chapter.read && chapter.last_page_read > 0) {
val lastPageRead = SpannableString(itemView.context.getString(R.string.chapter_progress, chapter.last_page_read + 1)).apply {
setSpan(ForegroundColorSpan(adapter.readColor), 0, length, SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE)
}
descriptions.add(lastPageRead)
}
if (!chapter.scanlator.isNullOrBlank()) {
descriptions.add(chapter.scanlator!!)
}
if (descriptions.isNotEmpty()) {
chapter_description.text = descriptions.joinTo(SpannableStringBuilder(), "")
} else {
chapter_description.text = ""
}
notifyStatus(item.status)
}
fun notifyStatus(status: Int) = with(download_text) {
when (status) {
Download.QUEUE -> setText(R.string.chapter_queued)
Download.DOWNLOADING -> setText(R.string.chapter_downloading)
Download.DOWNLOADED -> setText(R.string.chapter_downloaded)
Download.ERROR -> setText(R.string.chapter_error)
else -> text = ""
}
}
}

View File

@ -9,7 +9,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneAdapter
class ChapterItem(val chapter: Chapter, val manga: Manga) :
AbstractFlexibleItem<ChapterHolder>(),
@ -58,51 +57,3 @@ class ChapterItem(val chapter: Chapter, val manga: Manga) :
return chapter.id!!.hashCode()
}
}
class MangaAllInOneChapterItem(val chapter: Chapter, val manga: Manga) :
AbstractFlexibleItem<MangaAllInOneChapterHolder>(),
Chapter by chapter {
private var _status: Int = 0
var status: Int
get() = download?.status ?: _status
set(value) {
_status = value
}
@Transient
var download: Download? = null
val isDownloaded: Boolean
get() = status == Download.DOWNLOADED
override fun getLayoutRes(): Int {
return R.layout.chapters_item
}
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): MangaAllInOneChapterHolder {
return MangaAllInOneChapterHolder(view, adapter as MangaAllInOneAdapter)
}
override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: MangaAllInOneChapterHolder,
position: Int,
payloads: List<Any?>?
) {
holder.bind(this, manga)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other is MangaAllInOneChapterItem) {
return chapter.id!! == other.chapter.id!!
}
return false
}
override fun hashCode(): Int {
return chapter.id!!.hashCode()
}
}

View File

@ -4,6 +4,7 @@ import android.content.Context
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.getResourceColor
import java.text.DateFormat
import java.text.DecimalFormat
@ -11,7 +12,7 @@ import java.text.DecimalFormatSymbols
import uy.kohesive.injekt.injectLazy
class ChaptersAdapter(
controller: Any,
controller: MangaController,
context: Context
) : FlexibleAdapter<ChapterItem>(null, controller, true) {

View File

@ -1,612 +0,0 @@
package eu.kanade.tachiyomi.ui.manga.chapter
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.app.Activity
import android.content.Intent
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.graphics.drawable.DrawableCompat
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.SelectableAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.databinding.ChaptersControllerBinding
import eu.kanade.tachiyomi.source.LocalSource
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.getCoordinates
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.shrinkOnScroll
import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.util.view.visible
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.swiperefreshlayout.refreshes
import timber.log.Timber
class ChaptersController :
NucleusController<ChaptersControllerBinding, ChaptersPresenter>(),
ActionMode.Callback,
FlexibleAdapter.OnItemClickListener,
FlexibleAdapter.OnItemLongClickListener,
DownloadCustomChaptersDialog.Listener,
DeleteChaptersDialog.Listener {
/**
* Adapter containing a list of chapters.
*/
private var adapter: ChaptersAdapter? = null
/**
* Action mode for multiple selection.
*/
private var actionMode: ActionMode? = null
/**
* Selected items. Used to restore selections after a rotation.
*/
private val selectedItems = mutableSetOf<ChapterItem>()
private var lastClickPosition = -1
init {
setHasOptionsMenu(true)
setOptionsMenuHidden(true)
}
override fun createPresenter(): ChaptersPresenter {
val ctrl = parentController as MangaController
return ChaptersPresenter(
ctrl.manga!!, ctrl.source!!,
ctrl.chapterCountRelay, ctrl.lastUpdateRelay, ctrl.mangaFavoriteRelay
)
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = ChaptersControllerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
val ctrl = parentController as MangaController
if (ctrl.manga == null || ctrl.source == null) return
// Init RecyclerView and adapter
adapter = ChaptersAdapter(this, view.context)
binding.recycler.adapter = adapter
binding.recycler.layoutManager = LinearLayoutManager(view.context)
binding.recycler.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL))
binding.recycler.setHasFixedSize(true)
adapter?.fastScroller = binding.fastScroller
binding.swipeRefresh.refreshes()
.onEach { fetchChaptersFromSource(manualFetch = true) }
.launchIn(scope)
binding.fab.clicks()
.onEach {
val item = presenter.getNextUnreadChapter()
if (item != null) {
// Create animation listener
val revealAnimationListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
openChapter(item.chapter, true)
}
}
// Get coordinates and start animation
val coordinates = binding.fab.getCoordinates()
if (!binding.revealView.showRevealEffect(coordinates.x, coordinates.y, revealAnimationListener)) {
openChapter(item.chapter)
}
} else {
view.context.toast(R.string.no_next_chapter)
}
}
.launchIn(scope)
binding.fab.shrinkOnScroll(binding.recycler)
binding.actionToolbar.offsetAppbarHeight(activity!!)
binding.fab.offsetAppbarHeight(activity!!)
}
override fun onDestroyView(view: View) {
destroyActionModeIfNeeded()
binding.actionToolbar.destroy()
adapter = null
super.onDestroyView(view)
}
override fun onActivityResumed(activity: Activity) {
if (view == null) return
// Check if animation view is visible
if (binding.revealView.visibility == View.VISIBLE) {
// Show the unreveal effect
val coordinates = binding.fab.getCoordinates()
binding.revealView.hideRevealEffect(coordinates.x, coordinates.y, 1920)
}
super.onActivityResumed(activity)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.chapters, menu)
}
override fun onPrepareOptionsMenu(menu: Menu) {
// Initialize menu items.
val menuFilterRead = menu.findItem(R.id.action_filter_read) ?: return
val menuFilterUnread = menu.findItem(R.id.action_filter_unread)
val menuFilterDownloaded = menu.findItem(R.id.action_filter_downloaded)
val menuFilterBookmarked = menu.findItem(R.id.action_filter_bookmarked)
val menuFilterEmpty = menu.findItem(R.id.action_filter_empty)
// Set correct checkbox values.
menuFilterRead.isChecked = presenter.onlyRead()
menuFilterUnread.isChecked = presenter.onlyUnread()
menuFilterDownloaded.isChecked = presenter.onlyDownloaded()
menuFilterDownloaded.isEnabled = !presenter.forceDownloaded()
menuFilterBookmarked.isChecked = presenter.onlyBookmarked()
val filterSet = presenter.onlyRead() || presenter.onlyUnread() || presenter.onlyDownloaded() || presenter.onlyBookmarked()
if (filterSet) {
val filterColor = activity!!.getResourceColor(R.attr.colorFilterActive)
DrawableCompat.setTint(menu.findItem(R.id.action_filter).icon, filterColor)
}
// Only show remove filter option if there's a filter set.
menuFilterEmpty.isVisible = filterSet
// Disable unread filter option if read filter is enabled.
if (presenter.onlyRead()) {
menuFilterUnread.isEnabled = false
}
// Disable read filter option if unread filter is enabled.
if (presenter.onlyUnread()) {
menuFilterRead.isEnabled = false
}
// Display mode submenu
if (presenter.manga.displayMode == Manga.DISPLAY_NAME) {
menu.findItem(R.id.display_title).isChecked = true
} else {
menu.findItem(R.id.display_chapter_number).isChecked = true
}
// Sorting mode submenu
val sortingItem = when (presenter.manga.sorting) {
Manga.SORTING_SOURCE -> R.id.sort_by_source
Manga.SORTING_NUMBER -> R.id.sort_by_number
Manga.SORTING_UPLOAD_DATE -> R.id.sort_by_upload_date
else -> throw NotImplementedError("Unimplemented sorting method")
}
menu.findItem(sortingItem).isChecked = true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.display_title -> {
item.isChecked = true
setDisplayMode(Manga.DISPLAY_NAME)
}
R.id.display_chapter_number -> {
item.isChecked = true
setDisplayMode(Manga.DISPLAY_NUMBER)
}
R.id.sort_by_source -> {
item.isChecked = true
presenter.setSorting(Manga.SORTING_SOURCE)
}
R.id.sort_by_number -> {
item.isChecked = true
presenter.setSorting(Manga.SORTING_NUMBER)
}
R.id.sort_by_upload_date -> {
item.isChecked = true
presenter.setSorting(Manga.SORTING_UPLOAD_DATE)
}
R.id.download_next, R.id.download_next_5, R.id.download_next_10,
R.id.download_custom, R.id.download_unread, R.id.download_all
-> downloadChapters(item.itemId)
R.id.action_filter_unread -> {
item.isChecked = !item.isChecked
presenter.setUnreadFilter(item.isChecked)
activity?.invalidateOptionsMenu()
}
R.id.action_filter_read -> {
item.isChecked = !item.isChecked
presenter.setReadFilter(item.isChecked)
activity?.invalidateOptionsMenu()
}
R.id.action_filter_downloaded -> {
item.isChecked = !item.isChecked
presenter.setDownloadedFilter(item.isChecked)
activity?.invalidateOptionsMenu()
}
R.id.action_filter_bookmarked -> {
item.isChecked = !item.isChecked
presenter.setBookmarkedFilter(item.isChecked)
activity?.invalidateOptionsMenu()
}
R.id.action_filter_empty -> {
presenter.removeFilters()
activity?.invalidateOptionsMenu()
}
R.id.action_sort -> presenter.revertSortOrder()
}
return super.onOptionsItemSelected(item)
}
fun onNextChapters(chapters: List<ChapterItem>) {
// If the list is empty and it hasn't requested previously, fetch chapters from source
// We use presenter chapters instead because they are always unfiltered
if (!presenter.hasRequested && presenter.chapters.isEmpty()) {
fetchChaptersFromSource()
}
val mangaController = parentController as MangaController
if (mangaController.update ||
// Auto-update old format galleries
(
(presenter.manga.source == EH_SOURCE_ID || presenter.manga.source == EXH_SOURCE_ID) &&
chapters.size == 1 && chapters.first().date_upload == 0L
)
) {
mangaController.update = false
fetchChaptersFromSource()
}
val adapter = adapter ?: return
adapter.updateDataSet(chapters)
if (selectedItems.isNotEmpty()) {
adapter.clearSelection() // we need to start from a clean state, index may have changed
createActionModeIfNeeded()
selectedItems.forEach { item ->
val position = adapter.indexOf(item)
if (position != -1 && !adapter.isSelected(position)) {
adapter.toggleSelection(position)
}
}
actionMode?.invalidate()
}
val context = view?.context
if (context != null && chapters.any { it.read }) {
binding.fab.text = context.getString(R.string.action_resume)
}
}
private fun fetchChaptersFromSource(manualFetch: Boolean = false) {
binding.swipeRefresh.isRefreshing = true
presenter.fetchChaptersFromSource(manualFetch)
}
fun onFetchChaptersDone() {
binding.swipeRefresh.isRefreshing = false
}
fun onFetchChaptersError(error: Throwable) {
binding.swipeRefresh.isRefreshing = false
activity?.toast(error.message)
}
fun onChapterStatusChange(download: Download) {
getHolder(download.chapter)?.notifyStatus(download.status)
}
private fun getHolder(chapter: Chapter): ChapterHolder? {
return binding.recycler.findViewHolderForItemId(chapter.id!!) as? ChapterHolder
}
fun openChapter(chapter: Chapter, hasAnimation: Boolean = false) {
val activity = activity ?: return
val intent = ReaderActivity.newIntent(activity, presenter.manga, chapter)
if (hasAnimation) {
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
}
startActivity(intent)
}
override fun onItemClick(view: View?, position: Int): Boolean {
val adapter = adapter ?: return false
val item = adapter.getItem(position) ?: return false
return if (actionMode != null && adapter.mode == SelectableAdapter.Mode.MULTI) {
lastClickPosition = position
toggleSelection(position)
true
} else {
openChapter(item.chapter)
false
}
}
override fun onItemLongClick(position: Int) {
createActionModeIfNeeded()
when {
lastClickPosition == -1 -> setSelection(position)
lastClickPosition > position ->
for (i in position until lastClickPosition)
setSelection(i)
lastClickPosition < position ->
for (i in lastClickPosition + 1..position)
setSelection(i)
else -> setSelection(position)
}
lastClickPosition = position
adapter?.notifyDataSetChanged()
}
// SELECTIONS & ACTION MODE
private fun toggleSelection(position: Int) {
val adapter = adapter ?: return
val item = adapter.getItem(position) ?: return
adapter.toggleSelection(position)
adapter.notifyDataSetChanged()
if (adapter.isSelected(position)) {
selectedItems.add(item)
} else {
selectedItems.remove(item)
}
actionMode?.invalidate()
}
private fun setSelection(position: Int) {
val adapter = adapter ?: return
val item = adapter.getItem(position) ?: return
if (!adapter.isSelected(position)) {
adapter.toggleSelection(position)
selectedItems.add(item)
actionMode?.invalidate()
}
}
private fun getSelectedChapters(): List<ChapterItem> {
val adapter = adapter ?: return emptyList()
return adapter.selectedPositions.mapNotNull { adapter.getItem(it) }
}
private fun createActionModeIfNeeded() {
if (actionMode == null) {
actionMode = (activity as? AppCompatActivity)?.startSupportActionMode(this)
binding.actionToolbar.show(
actionMode!!,
R.menu.chapter_selection
) { onActionItemClicked(it!!) }
}
}
private fun destroyActionModeIfNeeded() {
lastClickPosition = -1
actionMode?.finish()
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
mode.menuInflater.inflate(R.menu.generic_selection, menu)
adapter?.mode = SelectableAdapter.Mode.MULTI
return true
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
val count = adapter?.selectedItemCount ?: 0
if (count == 0) {
// Destroy action mode if there are no items selected.
destroyActionModeIfNeeded()
} else {
mode.title = count.toString()
val isLocalSource = presenter.source.id == LocalSource.ID
val chapters = getSelectedChapters()
binding.actionToolbar.findItem(R.id.action_download)?.isVisible = !isLocalSource && chapters.any { !it.isDownloaded }
binding.actionToolbar.findItem(R.id.action_delete)?.isVisible = !isLocalSource && chapters.any { it.isDownloaded }
binding.actionToolbar.findItem(R.id.action_bookmark)?.isVisible = chapters.any { !it.chapter.bookmark }
binding.actionToolbar.findItem(R.id.action_remove_bookmark)?.isVisible = chapters.all { it.chapter.bookmark }
binding.actionToolbar.findItem(R.id.action_mark_as_read)?.isVisible = chapters.any { !it.chapter.read }
binding.actionToolbar.findItem(R.id.action_mark_as_unread)?.isVisible = chapters.all { it.chapter.read }
// Hide FAB to avoid interfering with the bottom action toolbar
// binding.fab.hide()
binding.fab.gone()
}
return false
}
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
return onActionItemClicked(item)
}
private fun onActionItemClicked(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_select_all -> selectAll()
R.id.action_select_inverse -> selectInverse()
R.id.action_download -> downloadChapters(getSelectedChapters())
R.id.action_delete -> showDeleteChaptersConfirmationDialog()
R.id.action_bookmark -> bookmarkChapters(getSelectedChapters(), true)
R.id.action_remove_bookmark -> bookmarkChapters(getSelectedChapters(), false)
R.id.action_mark_as_read -> markAsRead(getSelectedChapters())
R.id.action_mark_as_unread -> markAsUnread(getSelectedChapters())
R.id.action_mark_previous_as_read -> markPreviousAsRead(getSelectedChapters())
else -> return false
}
return true
}
override fun onDestroyActionMode(mode: ActionMode) {
binding.actionToolbar.hide()
adapter?.mode = SelectableAdapter.Mode.SINGLE
adapter?.clearSelection()
selectedItems.clear()
actionMode = null
// TODO: there seems to be a bug in MaterialComponents where the [ExtendedFloatingActionButton]
// fails to show up properly
// binding.fab.show()
binding.fab.visible()
}
override fun onDetach(view: View) {
destroyActionModeIfNeeded()
super.onDetach(view)
}
// SELECTION MODE ACTIONS
private fun selectAll() {
val adapter = adapter ?: return
adapter.selectAll()
selectedItems.addAll(adapter.items)
actionMode?.invalidate()
}
private fun selectInverse() {
val adapter = adapter ?: return
selectedItems.clear()
for (i in 0..adapter.itemCount) {
adapter.toggleSelection(i)
}
selectedItems.addAll(adapter.selectedPositions.mapNotNull { adapter.getItem(it) })
actionMode?.invalidate()
adapter.notifyDataSetChanged()
}
private fun markAsRead(chapters: List<ChapterItem>) {
presenter.markChaptersRead(chapters, true)
if (presenter.preferences.removeAfterMarkedAsRead()) {
deleteChapters(chapters)
}
destroyActionModeIfNeeded()
}
private fun markAsUnread(chapters: List<ChapterItem>) {
presenter.markChaptersRead(chapters, false)
destroyActionModeIfNeeded()
}
private fun downloadChapters(chapters: List<ChapterItem>) {
val view = view
presenter.downloadChapters(chapters)
if (view != null && !presenter.manga.favorite) {
binding.recycler.snack(view.context.getString(R.string.snack_add_to_library), Snackbar.LENGTH_INDEFINITE) {
setAction(R.string.action_add) {
presenter.addToLibrary()
}
}
}
destroyActionModeIfNeeded()
}
private fun showDeleteChaptersConfirmationDialog() {
DeleteChaptersDialog(this).showDialog(router)
}
override fun deleteChapters() {
deleteChapters(getSelectedChapters())
}
private fun markPreviousAsRead(chapters: List<ChapterItem>) {
val adapter = adapter ?: return
val prevChapters = if (presenter.sortDescending()) adapter.items.reversed() else adapter.items
val chapterPos = prevChapters.indexOf(chapters.last())
if (chapterPos != -1) {
markAsRead(prevChapters.take(chapterPos))
}
destroyActionModeIfNeeded()
}
private fun bookmarkChapters(chapters: List<ChapterItem>, bookmarked: Boolean) {
presenter.bookmarkChapters(chapters, bookmarked)
destroyActionModeIfNeeded()
}
fun deleteChapters(chapters: List<ChapterItem>) {
if (chapters.isEmpty()) return
presenter.deleteChapters(chapters)
destroyActionModeIfNeeded()
}
fun onChaptersDeleted(chapters: List<ChapterItem>) {
// this is needed so the downloaded text gets removed from the item
chapters.forEach {
adapter?.updateItem(it)
}
adapter?.notifyDataSetChanged()
}
fun onChaptersDeletedError(error: Throwable) {
Timber.e(error)
}
// OVERFLOW MENU DIALOGS
private fun setDisplayMode(id: Int) {
presenter.setDisplayMode(id)
adapter?.notifyDataSetChanged()
}
private fun getUnreadChaptersSorted() = presenter.chapters
.filter { !it.read && it.status == Download.NOT_DOWNLOADED }
.distinctBy { it.name }
.sortedByDescending { it.source_order }
private fun downloadChapters(choice: Int) {
val chaptersToDownload = when (choice) {
R.id.download_next -> getUnreadChaptersSorted().take(1)
R.id.download_next_5 -> getUnreadChaptersSorted().take(5)
R.id.download_next_10 -> getUnreadChaptersSorted().take(10)
R.id.download_custom -> {
showCustomDownloadDialog()
return
}
R.id.download_unread -> presenter.chapters.filter { !it.read }
R.id.download_all -> presenter.chapters
else -> emptyList()
}
if (chaptersToDownload.isNotEmpty()) {
downloadChapters(chaptersToDownload)
}
destroyActionModeIfNeeded()
}
private fun showCustomDownloadDialog() {
DownloadCustomChaptersDialog(this, presenter.chapters.size).showDialog(router)
}
override fun downloadCustomChapters(amount: Int) {
val chaptersToDownload = getUnreadChaptersSorted().take(amount)
if (chaptersToDownload.isNotEmpty()) {
downloadChapters(chaptersToDownload)
}
}
}

View File

@ -1,488 +0,0 @@
package eu.kanade.tachiyomi.ui.manga.chapter
import android.os.Bundle
import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.debug.DebugToggles
import exh.eh.EHentaiUpdateHelper
import java.util.Date
import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import timber.log.Timber
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
class ChaptersPresenter(
val manga: Manga,
val source: Source,
private val chapterCountRelay: BehaviorRelay<Float>,
private val lastUpdateRelay: BehaviorRelay<Date>,
private val mangaFavoriteRelay: PublishRelay<Boolean>,
val preferences: PreferencesHelper = Injekt.get(),
private val db: DatabaseHelper = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get()
) : BasePresenter<ChaptersController>() {
/**
* List of chapters of the manga. It's always unfiltered and unsorted.
*/
var chapters: List<ChapterItem> = emptyList()
private set
/**
* Subject of list of chapters to allow updating the view without going to DB.
*/
private val chaptersRelay: PublishRelay<List<ChapterItem>> by lazy {
PublishRelay.create<List<ChapterItem>>()
}
/**
* Whether the chapter list has been requested to the source.
*/
var hasRequested = false
private set
/**
* Subscription to retrieve the new list of chapters from the source.
*/
private var fetchChaptersSubscription: Subscription? = null
/**
* Subscription to observe download status changes.
*/
private var observeDownloadsSubscription: Subscription? = null
// EXH -->
private val updateHelper: EHentaiUpdateHelper by injectLazy()
val redirectUserRelay = BehaviorRelay.create<EXHRedirect>()
data class EXHRedirect(val manga: Manga, val update: Boolean)
// EXH <--
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
// Prepare the relay.
chaptersRelay.flatMap { applyChapterFilters(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(ChaptersController::onNextChapters) { _, error -> Timber.e(error) }
// Add the subscription that retrieves the chapters from the database, keeps subscribed to
// changes, and sends the list of chapters to the relay.
add(
db.getChapters(manga).asRxObservable()
.map { chapters ->
// Convert every chapter to a model.
chapters.map { it.toModel() }
}
.doOnNext { chapters ->
// Find downloaded chapters
setDownloadedChapters(chapters)
// Store the last emission
this.chapters = chapters
// Listen for download status changes
observeDownloads()
// Emit the number of chapters to the info tab.
chapterCountRelay.call(
chapters.maxBy { it.chapter_number }?.chapter_number
?: 0f
)
// Emit the upload date of the most recent chapter
lastUpdateRelay.call(
Date(
chapters.maxBy { it.date_upload }?.date_upload
?: 0
)
)
// EXH -->
if (chapters.isNotEmpty() &&
(source.id == EXH_SOURCE_ID || source.id == EH_SOURCE_ID) &&
DebugToggles.ENABLE_EXH_ROOT_REDIRECT.enabled
) {
// Check for gallery in library and accept manga with lowest id
// Find chapters sharing same root
add(
updateHelper.findAcceptedRootAndDiscardOthers(manga.source, chapters)
.subscribeOn(Schedulers.io())
.subscribe { (acceptedChain, _) ->
// Redirect if we are not the accepted root
if (manga.id != acceptedChain.manga.id) {
// Update if any of our chapters are not in accepted manga's chapters
val ourChapterUrls = chapters.map { it.url }.toSet()
val acceptedChapterUrls = acceptedChain.chapters.map { it.url }.toSet()
val update = (ourChapterUrls - acceptedChapterUrls).isNotEmpty()
redirectUserRelay.call(EXHRedirect(acceptedChain.manga, update))
}
}
)
}
// EXH <--
}
.subscribe { chaptersRelay.call(it) }
)
}
private fun observeDownloads() {
observeDownloadsSubscription?.let { remove(it) }
observeDownloadsSubscription = downloadManager.queue.getStatusObservable()
.observeOn(AndroidSchedulers.mainThread())
.filter { download -> download.manga.id == manga.id }
.doOnNext { onDownloadStatusChange(it) }
.subscribeLatestCache(ChaptersController::onChapterStatusChange) { _, error ->
Timber.e(error)
}
}
/**
* Converts a chapter from the database to an extended model, allowing to store new fields.
*/
private fun Chapter.toModel(): ChapterItem {
// Create the model object.
val model = ChapterItem(this, manga)
// Find an active download for this chapter.
val download = downloadManager.queue.find { it.chapter.id == id }
if (download != null) {
// If there's an active download, assign it.
model.download = download
}
return model
}
/**
* Finds and assigns the list of downloaded chapters.
*
* @param chapters the list of chapter from the database.
*/
private fun setDownloadedChapters(chapters: List<ChapterItem>) {
chapters
.filter { downloadManager.isChapterDownloaded(it, manga) }
.forEach { it.status = Download.DOWNLOADED }
}
/**
* Requests an updated list of chapters from the source.
*/
fun fetchChaptersFromSource(manualFetch: Boolean = false) {
hasRequested = true
if (!fetchChaptersSubscription.isNullOrUnsubscribed()) return
fetchChaptersSubscription = Observable.defer { source.fetchChapterList(manga) }
.subscribeOn(Schedulers.io())
.map { syncChaptersWithSource(db, it, manga, source) }
.doOnNext {
if (manualFetch) {
downloadNewChapters(it.first)
}
}
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
{ view, _ ->
view.onFetchChaptersDone()
},
ChaptersController::onFetchChaptersError
)
}
/**
* Updates the UI after applying the filters.
*/
private fun refreshChapters() {
chaptersRelay.call(chapters)
}
/**
* Applies the view filters to the list of chapters obtained from the database.
* @param chapters the list of chapters from the database
* @return an observable of the list of chapters filtered and sorted.
*/
private fun applyChapterFilters(chapters: List<ChapterItem>): Observable<List<ChapterItem>> {
var observable = Observable.from(chapters).subscribeOn(Schedulers.io())
if (onlyUnread()) {
observable = observable.filter { !it.read }
} else if (onlyRead()) {
observable = observable.filter { it.read }
}
if (onlyDownloaded()) {
observable = observable.filter { it.isDownloaded || it.manga.isLocal() }
}
if (onlyBookmarked()) {
observable = observable.filter { it.bookmark }
}
val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
Manga.SORTING_SOURCE -> when (sortDescending()) {
true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
false -> { c1, c2 -> c2.source_order.compareTo(c1.source_order) }
}
Manga.SORTING_NUMBER -> when (sortDescending()) {
true -> { c1, c2 -> c2.chapter_number.compareTo(c1.chapter_number) }
false -> { c1, c2 -> c1.chapter_number.compareTo(c2.chapter_number) }
}
Manga.SORTING_UPLOAD_DATE -> when (sortDescending()) {
true -> { c1, c2 -> c2.date_upload.compareTo(c1.date_upload) }
false -> { c1, c2 -> c1.date_upload.compareTo(c2.date_upload) }
}
else -> throw NotImplementedError("Unimplemented sorting method")
}
return observable.toSortedList(sortFunction)
}
/**
* Called when a download for the active manga changes status.
* @param download the download whose status changed.
*/
private fun onDownloadStatusChange(download: Download) {
// Assign the download to the model object.
if (download.status == Download.QUEUE) {
chapters.find { it.id == download.chapter.id }?.let {
if (it.download == null) {
it.download = download
}
}
}
// Force UI update if downloaded filter active and download finished.
if (onlyDownloaded() && download.status == Download.DOWNLOADED) {
refreshChapters()
}
}
/**
* Returns the next unread chapter or null if everything is read.
*/
fun getNextUnreadChapter(): ChapterItem? {
return chapters.sortedByDescending { it.source_order }.find { !it.read }
}
/**
* Mark the selected chapter list as read/unread.
* @param selectedChapters the list of selected chapters.
* @param read whether to mark chapters as read or unread.
*/
fun markChaptersRead(selectedChapters: List<ChapterItem>, read: Boolean) {
Observable.from(selectedChapters)
.doOnNext { chapter ->
chapter.read = read
if (!read /* --> EH */ && !preferences
.eh_preserveReadingPosition()
.get() /* <-- EH */
) {
chapter.last_page_read = 0
}
}
.toList()
.flatMap { db.updateChaptersProgress(it).asRxObservable() }
.subscribeOn(Schedulers.io())
.subscribe()
}
/**
* Downloads the given list of chapters with the manager.
* @param chapters the list of chapters to download.
*/
fun downloadChapters(chapters: List<Chapter>) {
downloadManager.downloadChapters(manga, chapters)
}
/**
* Bookmarks the given list of chapters.
* @param selectedChapters the list of chapters to bookmark.
*/
fun bookmarkChapters(selectedChapters: List<ChapterItem>, bookmarked: Boolean) {
Observable.from(selectedChapters)
.doOnNext { chapter ->
chapter.bookmark = bookmarked
}
.toList()
.flatMap { db.updateChaptersProgress(it).asRxObservable() }
.subscribeOn(Schedulers.io())
.subscribe()
}
/**
* Deletes the given list of chapter.
* @param chapters the list of chapters to delete.
*/
fun deleteChapters(chapters: List<ChapterItem>) {
Observable.just(chapters)
.doOnNext { deleteChaptersInternal(chapters) }
.doOnNext { if (onlyDownloaded()) refreshChapters() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
{ view, _ ->
view.onChaptersDeleted(chapters)
},
ChaptersController::onChaptersDeletedError
)
}
private fun downloadNewChapters(chapters: List<Chapter>) {
if (chapters.isEmpty() || !manga.shouldDownloadNewChapters(db, preferences) /* SY --> */ || manga.source == EH_SOURCE_ID || manga.source == EXH_SOURCE_ID/* SY <-- */) return
downloadChapters(chapters)
}
/**
* Deletes a list of chapters from disk. This method is called in a background thread.
* @param chapters the chapters to delete.
*/
private fun deleteChaptersInternal(chapters: List<ChapterItem>) {
downloadManager.deleteChapters(chapters, manga, source)
chapters.forEach {
it.status = Download.NOT_DOWNLOADED
it.download = null
}
}
/**
* Reverses the sorting and requests an UI update.
*/
fun revertSortOrder() {
manga.setChapterOrder(if (sortDescending()) Manga.SORT_ASC else Manga.SORT_DESC)
db.updateFlags(manga).executeAsBlocking()
refreshChapters()
}
/**
* Sets the read filter and requests an UI update.
* @param onlyUnread whether to display only unread chapters or all chapters.
*/
fun setUnreadFilter(onlyUnread: Boolean) {
manga.readFilter = if (onlyUnread) Manga.SHOW_UNREAD else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking()
refreshChapters()
}
/**
* Sets the read filter and requests an UI update.
* @param onlyRead whether to display only read chapters or all chapters.
*/
fun setReadFilter(onlyRead: Boolean) {
manga.readFilter = if (onlyRead) Manga.SHOW_READ else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking()
refreshChapters()
}
/**
* Sets the download filter and requests an UI update.
* @param onlyDownloaded whether to display only downloaded chapters or all chapters.
*/
fun setDownloadedFilter(onlyDownloaded: Boolean) {
manga.downloadedFilter = if (onlyDownloaded) Manga.SHOW_DOWNLOADED else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking()
refreshChapters()
}
/**
* Sets the bookmark filter and requests an UI update.
* @param onlyBookmarked whether to display only bookmarked chapters or all chapters.
*/
fun setBookmarkedFilter(onlyBookmarked: Boolean) {
manga.bookmarkedFilter = if (onlyBookmarked) Manga.SHOW_BOOKMARKED else Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking()
refreshChapters()
}
/**
* Removes all filters and requests an UI update.
*/
fun removeFilters() {
manga.readFilter = Manga.SHOW_ALL
manga.downloadedFilter = Manga.SHOW_ALL
manga.bookmarkedFilter = Manga.SHOW_ALL
db.updateFlags(manga).executeAsBlocking()
refreshChapters()
}
/**
* Adds manga to library
*/
fun addToLibrary() {
mangaFavoriteRelay.call(true)
}
/**
* Sets the active display mode.
* @param mode the mode to set.
*/
fun setDisplayMode(mode: Int) {
manga.displayMode = mode
db.updateFlags(manga).executeAsBlocking()
}
/**
* Sets the sorting method and requests an UI update.
* @param sort the sorting mode.
*/
fun setSorting(sort: Int) {
manga.sorting = sort
db.updateFlags(manga).executeAsBlocking()
refreshChapters()
}
/**
* Whether downloaded only mode is enabled.
*/
fun forceDownloaded(): Boolean {
return manga.favorite && preferences.downloadedOnly().get()
}
/**
* Whether the display only downloaded filter is enabled.
*/
fun onlyDownloaded(): Boolean {
return forceDownloaded() || manga.downloadedFilter == Manga.SHOW_DOWNLOADED
}
/**
* Whether the display only downloaded filter is enabled.
*/
fun onlyBookmarked(): Boolean {
return manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED
}
/**
* Whether the display only unread filter is enabled.
*/
fun onlyUnread(): Boolean {
return manga.readFilter == Manga.SHOW_UNREAD
}
/**
* Whether the display only read filter is enabled.
*/
fun onlyRead(): Boolean {
return manga.readFilter == Manga.SHOW_READ
}
/**
* Whether the sorting method is descending or ascending.
*/
fun sortDescending(): Boolean {
return manga.sortDescending()
}
}

View File

@ -0,0 +1,47 @@
package eu.kanade.tachiyomi.ui.manga.chapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.MangaChaptersHeaderBinding
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
class MangaChaptersHeaderAdapter :
RecyclerView.Adapter<MangaChaptersHeaderAdapter.HeaderViewHolder>() {
private var numChapters: Int? = null
private val scope = CoroutineScope(Job() + Dispatchers.Main)
private lateinit var binding: MangaChaptersHeaderBinding
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder {
binding = MangaChaptersHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return HeaderViewHolder(binding.root)
}
override fun getItemCount(): Int = 1
override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) {
holder.bind()
}
fun setNumChapters(numChapters: Int) {
this.numChapters = numChapters
notifyDataSetChanged()
}
inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
fun bind() {
binding.chaptersLabel.text = if (numChapters == null) {
view.context.getString(R.string.chapters)
} else {
view.context.resources.getQuantityString(R.plurals.manga_num_chapters, numChapters!!, numChapters)
}
}
}
}

View File

@ -0,0 +1,24 @@
package eu.kanade.tachiyomi.ui.manga.info
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
import kotlin.math.min
/**
* A custom ImageView for holding a manga cover with:
* - width: min(maxWidth attr, 33% of parent width)
* - height: 2:3 width:height ratio
*
* Should be defined with a width of match_parent.
*/
class MangaCoverImageView(context: Context, attrs: AttributeSet?) : AppCompatImageView(context, attrs) {
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = min(maxWidth, measuredWidth / 3)
val height = width / 2 * 3
setMeasuredDimension(width, height)
}
}

View File

@ -1,713 +0,0 @@
package eu.kanade.tachiyomi.ui.manga.info
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.google.gson.Gson
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.MangaInfoControllerBinding
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.advanced.design.PreMigrationController
import eu.kanade.tachiyomi.ui.browse.source.SourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
import eu.kanade.tachiyomi.ui.library.LibraryController
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.recent.history.HistoryController
import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.util.view.visibleIf
import exh.EH_SOURCE_ID
import exh.EXH_SOURCE_ID
import exh.MERGED_SOURCE_ID
import exh.util.setChipsExtended
import java.text.DateFormat
import java.text.DecimalFormat
import java.util.Date
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks
import reactivecircus.flowbinding.swiperefreshlayout.refreshes
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
/**
* Fragment that shows manga information.
* Uses R.layout.manga_info_controller.
* UI related actions should be called from here.
*/
class MangaInfoController(private val fromSource: Boolean = false) :
NucleusController<MangaInfoControllerBinding, MangaInfoPresenter>(),
ChangeMangaCategoriesDialog.Listener,
CoroutineScope {
private val preferences: PreferencesHelper by injectLazy()
private val dateFormat: DateFormat by lazy {
preferences.dateFormat()
}
private var initialLoad: Boolean = true
// EXH -->
private var lastMangaThumbnail: String? = null
private val smartSearchConfig get() = (parentController as MangaController).smartSearchConfig
override val coroutineContext: CoroutineContext = Job() + Dispatchers.Main
private val gson: Gson by injectLazy()
private val sourceManager: SourceManager by injectLazy()
// EXH <--
override fun createPresenter(): MangaInfoPresenter {
val ctrl = parentController as MangaController
return MangaInfoPresenter(
ctrl.manga!!, ctrl.source!!,
ctrl.chapterCountRelay, ctrl.lastUpdateRelay, ctrl.mangaFavoriteRelay, ctrl.smartSearchConfig
)
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
binding = MangaInfoControllerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View) {
super.onViewCreated(view)
// For rounded corners
binding.mangaCover.clipToOutline = true
binding.btnFavorite.clicks()
.onEach { onFavoriteClick() }
.launchIn(scope)
if (presenter.manga.favorite && presenter.getCategories().isNotEmpty()) {
binding.btnCategories.visible()
}
binding.btnCategories.clicks()
.onEach { onCategoriesClick() }
.launchIn(scope)
if (presenter.source is HttpSource) {
binding.btnWebview.visible()
binding.btnShare.visible()
binding.btnWebview.clicks()
.onEach { openInWebView() }
.launchIn(scope)
binding.btnShare.clicks()
.onEach { shareManga() }
.launchIn(scope)
}
if (presenter.manga.favorite) {
binding.btnMigrate.visible()
binding.btnSmartSearch.visible()
}
binding.btnMigrate.clicks()
.onEach {
PreMigrationController.navigateToMigration(
preferences.skipPreMigration().get(),
router,
listOf(presenter.manga.id!!)
)
}
.launchIn(scope)
binding.btnSmartSearch.clicks()
.onEach { openSmartSearch() }
.launchIn(scope)
// Set SwipeRefresh to refresh manga data.
binding.swipeRefresh.refreshes()
.onEach { fetchMangaFromSource(manualFetch = true) }
.launchIn(scope)
binding.mangaFullTitle.longClicks()
.onEach {
activity?.copyToClipboard(
view.context.getString(R.string.title),
binding.mangaFullTitle.text.toString()
)
}
.launchIn(scope)
binding.mangaFullTitle.clicks()
.onEach {
performGlobalSearch(binding.mangaFullTitle.text.toString())
}
.launchIn(scope)
binding.mangaAuthor.longClicks()
.onEach {
// EXH Special case E-Hentai/ExHentai to ignore author field (unused)
if (!isEHentaiBasedSource()) {
activity?.copyToClipboard(
binding.mangaAuthor.text.toString(),
binding.mangaAuthor.text.toString()
)
}
}
.launchIn(scope)
binding.mangaAuthor.clicks()
.onEach {
// EXH Special case E-Hentai/ExHentai to ignore author field (unused)
if (!isEHentaiBasedSource()) {
performGlobalSearch(binding.mangaAuthor.text.toString())
}
}
.launchIn(scope)
binding.mangaSummary.longClicks()
.onEach {
activity?.copyToClipboard(
view.context.getString(R.string.description),
binding.mangaSummary.text.toString()
)
}
.launchIn(scope)
binding.mangaCover.longClicks()
.onEach {
activity?.copyToClipboard(
view.context.getString(R.string.title),
presenter.manga.title
)
}
.launchIn(scope)
// EXH -->
if (smartSearchConfig == null) {
binding.recommendBtn.visible()
binding.recommendBtn.clicks()
.onEach { openRecommends() }
.launchIn(scope)
}
smartSearchConfig?.let { smartSearchConfig ->
if (smartSearchConfig.origMangaId != null) { binding.mergeBtn.visible() }
binding.mergeBtn.clicks()
.onEach {
// Init presenter here to avoid threading issues
presenter
launch {
try {
val mergedManga = withContext(Dispatchers.IO + NonCancellable) {
presenter.smartSearchMerge(presenter.manga, smartSearchConfig.origMangaId!!)
}
parentController?.router?.pushController(
MangaController(
mergedManga,
true,
update = true
).withFadeTransaction()
)
applicationContext?.toast("Manga merged!")
} catch (e: Exception) {
if (e is CancellationException) throw e
else {
applicationContext?.toast("Failed to merge manga: ${e.message}")
}
}
}
}
.launchIn(scope)
}
// EXH <--
}
// EXH -->
private fun openSmartSearch() {
val smartSearchConfig = SourceController.SmartSearchConfig(presenter.manga.originalTitle, presenter.manga.id!!)
parentController?.router?.pushController(
SourceController(
Bundle().apply {
putParcelable(SourceController.SMART_SEARCH_CONFIG, smartSearchConfig)
}
).withFadeTransaction()
)
}
// EXH <--
// AZ -->
private fun openRecommends() {
val recommendsConfig = BrowseSourceController.RecommendsConfig(presenter.manga)
parentController?.router?.pushController(
BrowseSourceController(
Bundle().apply {
putParcelable(BrowseSourceController.RECOMMENDS_CONFIG, recommendsConfig)
}
).withFadeTransaction()
)
}
// AZ <--
/**
* Check if manga is initialized.
* If true update view with manga information,
* if false fetch manga information
*
* @param manga manga object containing information about manga.
* @param source the source of the manga.
*/
fun onNextManga(manga: Manga, source: Source) {
if (manga.initialized) {
// Update view.
setMangaInfo(manga, source)
} else {
// Initialize manga.
fetchMangaFromSource()
}
}
/**
* Update the view with manga information.
*
* @param manga manga object containing information about manga.
* @param source the source of the manga.
*/
private fun setMangaInfo(manga: Manga, source: Source?) {
val view = view ?: return
// update full title TextView.
binding.mangaFullTitle.text = if (manga.title.isBlank()) {
view.context.getString(R.string.unknown)
} else {
manga.title
}
// Update author/artist TextView.
val authors = listOf(manga.author, manga.artist).filter { !it.isNullOrBlank() }.distinct()
binding.mangaAuthor.text = if (authors.isEmpty()) {
view.context.getString(R.string.unknown)
} else {
authors.joinToString(", ")
}
// If manga source is known update source TextView.
val mangaSource = source?.toString()
with(binding.mangaSource) {
// EXH -->
if (mangaSource == null) {
text = view.context.getString(R.string.unknown)
} else if (source.id == MERGED_SOURCE_ID) {
text = MergedSource.MangaConfig.readFromUrl(gson, manga.url).children.map {
sourceManager.getOrStub(it.source).toString()
}.distinct().joinToString()
} else {
text = mangaSource
setOnClickListener {
val sourceManager = Injekt.get<SourceManager>()
performSearch(sourceManager.getOrStub(source.id).name)
}
}
// EXH <--
}
// EXH -->
if (source?.id == MERGED_SOURCE_ID) {
binding.mangaSourceLabel.text = "Sources"
} else {
binding.mangaSourceLabel.setText(R.string.manga_info_source_label)
}
// EXH <--
// Update status TextView.
binding.mangaStatus.setText(
when (manga.status) {
SManga.ONGOING -> R.string.ongoing
SManga.COMPLETED -> R.string.completed
SManga.LICENSED -> R.string.licensed
else -> R.string.unknown
}
)
// Set the favorite drawable to the correct one.
setFavoriteButtonState(manga.favorite)
// Set cover if it wasn't already.
val mangaThumbnail = manga.toMangaThumbnail()
GlideApp.with(view.context)
.load(mangaThumbnail)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
.into(binding.mangaCover)
binding.backdrop?.let {
GlideApp.with(view.context)
.load(mangaThumbnail)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
.into(it)
}
// Manga info section
if (manga.description.isNullOrBlank() && manga.genre.isNullOrBlank()) {
hideMangaInfo()
} else {
// Update description TextView.
binding.mangaSummary.text = if (manga.description.isNullOrBlank()) {
view.context.getString(R.string.unknown)
} else {
manga.description
}
// Update genres list
if (!manga.genre.isNullOrBlank()) {
binding.mangaGenresTagsCompactChips.setChipsExtended(manga.getGenres(), this::performSearch, this::performGlobalSearch, manga.source)
binding.mangaGenresTagsFullChips.setChipsExtended(manga.getGenres(), this::performSearch, this::performGlobalSearch, manga.source)
} else {
binding.mangaGenresTagsWrapper.gone()
}
// Handle showing more or less info
binding.mangaSummary.clicks()
.onEach { toggleMangaInfo(view.context) }
.launchIn(scope)
binding.mangaInfoToggle.clicks()
.onEach { toggleMangaInfo(view.context) }
.launchIn(scope)
// Expand manga info if navigated from source listing
if (initialLoad && fromSource) {
toggleMangaInfo(view.context)
initialLoad = false
}
}
}
private fun hideMangaInfo() {
binding.mangaSummaryLabel.gone()
binding.mangaSummary.gone()
binding.mangaGenresTagsWrapper.gone()
binding.mangaInfoToggle.gone()
}
private fun toggleMangaInfo(context: Context) {
val isExpanded =
binding.mangaInfoToggle.text == context.getString(R.string.manga_info_collapse)
binding.mangaInfoToggle.text =
if (isExpanded) {
context.getString(R.string.manga_info_expand)
} else {
context.getString(R.string.manga_info_collapse)
}
with(binding.mangaSummary) {
maxLines =
if (isExpanded) {
3
} else {
Int.MAX_VALUE
}
ellipsize =
if (isExpanded) {
TextUtils.TruncateAt.END
} else {
null
}
}
binding.mangaGenresTagsCompact.visibleIf { isExpanded }
binding.mangaGenresTagsFullChips.visibleIf { !isExpanded }
}
/**
* Update chapter count TextView.
*
* @param count number of chapters.
*/
fun setChapterCount(count: Float) {
if (count > 0f) {
binding.mangaChapters.text = DecimalFormat("#.#").format(count)
} else {
binding.mangaChapters.text = resources?.getString(R.string.unknown)
}
}
fun setLastUpdateDate(date: Date) {
if (date.time != 0L) {
binding.mangaLastUpdate.text = dateFormat.format(date)
} else {
binding.mangaLastUpdate.text = resources?.getString(R.string.unknown)
}
}
/**
* Toggles the favorite status and asks for confirmation to delete downloaded chapters.
*/
private fun toggleFavorite() {
val view = view
val isNowFavorite = presenter.toggleFavorite()
if (view != null && !isNowFavorite && presenter.hasDownloads()) {
view.snack(view.context.getString(R.string.delete_downloads_for_manga)) {
setAction(R.string.action_delete) {
presenter.deleteDownloads()
}
}
}
binding.btnCategories.visibleIf { isNowFavorite && presenter.getCategories().isNotEmpty() }
if (isNowFavorite) {
binding.btnSmartSearch.visible()
binding.btnMigrate.visible()
} else {
binding.btnSmartSearch.gone()
binding.btnMigrate.gone()
}
}
private fun openInWebView() {
val source = presenter.source as? HttpSource ?: return
val url = try {
source.mangaDetailsRequest(presenter.manga).url.toString()
} catch (e: Exception) {
return
}
val activity = activity ?: return
val intent = WebViewActivity.newIntent(activity, url, source.id, presenter.manga.title)
startActivity(intent)
}
/**
* Called to run Intent with [Intent.ACTION_SEND], which show share dialog.
*/
private fun shareManga() {
val context = view?.context ?: return
val source = presenter.source as? HttpSource ?: return
try {
val url = source.mangaDetailsRequest(presenter.manga).url.toString()
val intent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, url)
}
startActivity(Intent.createChooser(intent, context.getString(R.string.action_share)))
} catch (e: Exception) {
context.toast(e.message)
}
}
/**
* Update favorite button with correct drawable and text.
*
* @param isFavorite determines if manga is favorite or not.
*/
private fun setFavoriteButtonState(isFavorite: Boolean) {
// Set the Favorite drawable to the correct one.
// Border drawable if false, filled drawable if true.
binding.btnFavorite.apply {
icon = ContextCompat.getDrawable(
context,
if (isFavorite) R.drawable.ic_favorite_24dp else R.drawable.ic_favorite_border_24dp
)
text =
context.getString(if (isFavorite) R.string.in_library else R.string.add_to_library)
isChecked = isFavorite
}
}
/**
* Start fetching manga information from source.
*/
private fun fetchMangaFromSource(manualFetch: Boolean = false) {
setRefreshing(true)
// Call presenter and start fetching manga information
presenter.fetchMangaFromSource(manualFetch)
}
/**
* Update swipe refresh to stop showing refresh in progress spinner.
*/
fun onFetchMangaDone() {
setRefreshing(false)
}
/**
* Update swipe refresh to start showing refresh in progress spinner.
*/
fun onFetchMangaError(error: Throwable) {
setRefreshing(false)
activity?.toast(error.message)
}
/**
* Set swipe refresh status.
*
* @param value whether it should be refreshing or not.
*/
private fun setRefreshing(value: Boolean) {
binding.swipeRefresh.isRefreshing = value
}
private fun onFavoriteClick() {
val manga = presenter.manga
if (manga.favorite) {
toggleFavorite()
activity?.toast(activity?.getString(R.string.manga_removed_library))
} else {
val categories = presenter.getCategories()
val defaultCategoryId = preferences.defaultCategory()
val defaultCategory = categories.find { it.id == defaultCategoryId }
when {
// Default category set
defaultCategory != null -> {
toggleFavorite()
presenter.moveMangaToCategory(manga, defaultCategory)
activity?.toast(activity?.getString(R.string.manga_added_library))
}
// Automatic 'Default' or no categories
defaultCategoryId == 0 || categories.isEmpty() -> {
toggleFavorite()
presenter.moveMangaToCategory(manga, null)
activity?.toast(activity?.getString(R.string.manga_added_library))
}
// Choose a category
else -> {
val ids = presenter.getMangaCategoryIds(manga)
val preselected = ids.mapNotNull { id ->
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
}.toTypedArray()
ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected)
.showDialog(router)
}
}
}
}
private fun onCategoriesClick() {
val manga = presenter.manga
val categories = presenter.getCategories()
val ids = presenter.getMangaCategoryIds(manga)
val preselected = ids.mapNotNull { id ->
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
}.toTypedArray()
ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected)
.showDialog(router)
}
override fun updateCategoriesForMangas(mangas: List<Manga>, categories: List<Category>) {
val manga = mangas.firstOrNull() ?: return
if (!manga.favorite) {
toggleFavorite()
activity?.toast(activity?.getString(R.string.manga_added_library))
}
presenter.moveMangaToCategories(manga, categories)
}
/**
* Perform a global search using the provided query.
*
* @param query the search query to pass to the search controller
*/
private fun performGlobalSearch(query: String) {
val router = parentController?.router ?: return
router.pushController(GlobalSearchController(query).withFadeTransaction())
}
// --> EH
private fun wrapTag(namespace: String, tag: String) =
if (tag.contains(' ')) {
"$namespace:\"$tag$\""
} else {
"$namespace:$tag$"
}
private fun parseTag(tag: String) = tag.substringBefore(':').trim() to tag.substringAfter(':').trim()
private fun isEHentaiBasedSource(): Boolean {
val sourceId = presenter.source.id
return sourceId == EH_SOURCE_ID ||
sourceId == EXH_SOURCE_ID
}
// <-- EH
/**
* Perform a search using the provided query.
*
* @param query the search query to the parent controller
*/
private fun performSearch(query: String) {
val router = parentController?.router ?: return
if (router.backstackSize < 2) {
return
}
when (val previousController = router.backstack[router.backstackSize - 2].controller()) {
is LibraryController -> {
router.handleBack()
previousController.search(query)
}
is UpdatesController,
is HistoryController -> {
// Manually navigate to LibraryController
router.handleBack()
(router.activity as MainActivity).setSelectedNavItem(R.id.nav_library)
val controller = router.getControllerWithTag(R.id.nav_library.toString()) as LibraryController
controller.search(query)
}
is BrowseSourceController -> {
router.handleBack()
previousController.searchWithQuery(query)
}
}
}
}

View File

@ -0,0 +1,397 @@
package eu.kanade.tachiyomi.ui.manga.info
import android.content.Context
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.load.engine.DiskCacheStrategy
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.glide.GlideApp
import eu.kanade.tachiyomi.data.glide.MangaThumbnail
import eu.kanade.tachiyomi.data.glide.toMangaThumbnail
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.databinding.MangaInfoHeaderBinding
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.view.gone
import eu.kanade.tachiyomi.util.view.setChips
import eu.kanade.tachiyomi.util.view.setTooltip
import eu.kanade.tachiyomi.util.view.visible
import eu.kanade.tachiyomi.util.view.visibleIf
import exh.MERGED_SOURCE_ID
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks
import reactivecircus.flowbinding.android.view.longClicks
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class MangaInfoHeaderAdapter(
private val controller: MangaController,
private val fromSource: Boolean
) :
RecyclerView.Adapter<MangaInfoHeaderAdapter.HeaderViewHolder>() {
private var manga: Manga = controller.presenter.manga
private var source: Source = controller.presenter.source
private val scope = CoroutineScope(Job() + Dispatchers.Main)
private lateinit var binding: MangaInfoHeaderBinding
private var initialLoad: Boolean = true
private var currentMangaThumbnail: MangaThumbnail? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder {
binding = MangaInfoHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return HeaderViewHolder(binding.root)
}
override fun getItemCount(): Int = 1
override fun onBindViewHolder(holder: HeaderViewHolder, position: Int) {
holder.bind()
}
/**
* Update the view with manga information.
*
* @param manga manga object containing information about manga.
* @param source the source of the manga.
*/
fun update(manga: Manga, source: Source) {
this.manga = manga
this.source = source
notifyDataSetChanged()
}
inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
fun bind() {
// For rounded corners
binding.mangaCover.clipToOutline = true
binding.btnFavorite.clicks()
.onEach { controller.onFavoriteClick() }
.launchIn(scope)
if (controller.presenter.manga.favorite && Injekt.get<TrackManager>().hasLoggedServices()) {
binding.btnTracking.visible()
binding.btnTracking.clicks()
.onEach { controller.onTrackingClick() }
.launchIn(scope)
} else {
binding.btnTracking.gone()
}
if (controller.presenter.manga.favorite && controller.presenter.getCategories().isNotEmpty()) {
binding.btnCategories.visible()
binding.btnCategories.clicks()
.onEach { controller.onCategoriesClick() }
.launchIn(scope)
binding.btnCategories.setTooltip(R.string.action_move_category)
} else {
binding.btnCategories.gone()
}
if (controller.presenter.source is HttpSource) {
binding.btnWebview.visible()
binding.btnWebview.clicks()
.onEach { controller.openMangaInWebView() }
.launchIn(scope)
binding.btnWebview.setTooltip(R.string.action_open_in_web_view)
binding.btnShare.visible()
binding.btnShare.clicks()
.onEach { controller.shareManga() }
.launchIn(scope)
binding.btnShare.setTooltip(R.string.action_share)
}
// SY -->
if (controller.presenter.manga.favorite) {
binding.btnMigrate.visible()
binding.btnMigrate.clicks()
.onEach { controller.migrateManga() }
.launchIn(scope)
binding.btnMigrate.setTooltip(R.string.migrate)
binding.btnSmartSearch.visible()
binding.btnSmartSearch.clicks()
.onEach { controller.openSmartSearch() }
.launchIn(scope)
binding.btnSmartSearch.setTooltip(R.string.eh_merge_with_another_source)
}
// SY <--
binding.mangaFullTitle.longClicks()
.onEach {
controller.activity?.copyToClipboard(
view.context.getString(R.string.title),
binding.mangaFullTitle.text.toString()
)
}
.launchIn(scope)
binding.mangaFullTitle.clicks()
.onEach {
controller.performGlobalSearch(binding.mangaFullTitle.text.toString())
}
.launchIn(scope)
binding.mangaAuthor.longClicks()
.onEach {
controller.activity?.copyToClipboard(
binding.mangaAuthor.text.toString(),
binding.mangaAuthor.text.toString()
)
}
.launchIn(scope)
binding.mangaAuthor.clicks()
.onEach {
controller.performGlobalSearch(binding.mangaAuthor.text.toString())
}
.launchIn(scope)
binding.mangaArtist.longClicks()
.onEach {
controller.activity?.copyToClipboard(
binding.mangaArtist.text.toString(),
binding.mangaArtist.text.toString()
)
}
.launchIn(scope)
binding.mangaArtist.clicks()
.onEach {
controller.performGlobalSearch(binding.mangaArtist.text.toString())
}
.launchIn(scope)
binding.mangaSummary.longClicks()
.onEach {
controller.activity?.copyToClipboard(
view.context.getString(R.string.description),
binding.mangaSummary.text.toString()
)
}
.launchIn(scope)
binding.mangaCover.longClicks()
.onEach {
controller.activity?.copyToClipboard(
view.context.getString(R.string.title),
controller.presenter.manga.title
)
}
.launchIn(scope)
// EXH -->
if (controller.smartSearchConfig == null) {
binding.recommendBtn.visible()
binding.recommendBtn.clicks()
.onEach { controller.openRecommends() }
.launchIn(scope)
} else {
if (controller.smartSearchConfig.origMangaId != null) { binding.mergeBtn.visible() }
binding.mergeBtn.clicks()
.onEach {
controller.mergeWithAnother()
}
.launchIn(scope)
}
// EXH <--
setMangaInfo(manga, source)
}
/**
* Update the view with manga information.
*
* @param manga manga object containing information about manga.
* @param source the source of the manga.
*/
private fun setMangaInfo(manga: Manga, source: Source?) {
// Update full title TextView.
binding.mangaFullTitle.text = if (manga.title.isBlank()) {
view.context.getString(R.string.unknown)
} else {
manga.title
}
// Update author TextView.
binding.mangaAuthor.text = if (manga.author.isNullOrBlank()) {
view.context.getString(R.string.unknown_author)
} else {
manga.author
}
// Update artist TextView.
val hasArtist = !manga.artist.isNullOrBlank() && manga.artist != manga.author
binding.mangaArtist.isVisible = hasArtist
if (hasArtist) {
binding.mangaArtist.text = manga.artist
}
// If manga source is known update source TextView.
val mangaSource = source?.toString()
with(binding.mangaSource) {
// SY -->
if (source != null && source.id == MERGED_SOURCE_ID) {
text = MergedSource.MangaConfig.readFromUrl(Injekt.get(), manga.url).children.map {
Injekt.get<SourceManager>().getOrStub(it.source).toString()
}.distinct().joinToString()
} else /* SY <-- */ if (mangaSource != null) {
text = mangaSource
setOnClickListener {
val sourceManager = Injekt.get<SourceManager>()
controller.performSearch(sourceManager.getOrStub(source.id).name)
}
} else {
text = view.context.getString(R.string.unknown)
}
}
// Update status TextView.
binding.mangaStatus.setText(
when (manga.status) {
SManga.ONGOING -> R.string.ongoing
SManga.COMPLETED -> R.string.completed
SManga.LICENSED -> R.string.licensed
else -> R.string.unknown_status
}
)
// Set the favorite drawable to the correct one.
setFavoriteButtonState(manga.favorite)
// Set cover if changed.
val mangaThumbnail = manga.toMangaThumbnail()
if (mangaThumbnail != currentMangaThumbnail) {
currentMangaThumbnail = mangaThumbnail
listOf(binding.mangaCover, binding.backdrop)
.forEach {
GlideApp.with(view.context)
.load(mangaThumbnail)
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.centerCrop()
.into(it)
}
}
// Manga info section
val hasInfoContent = !manga.description.isNullOrBlank() || !manga.genre.isNullOrBlank()
showMangaInfo(hasInfoContent)
if (hasInfoContent) {
// Update description TextView.
binding.mangaSummary.text = if (manga.description.isNullOrBlank()) {
view.context.getString(R.string.unknown)
} else {
manga.description
}
// Update genres list
if (!manga.genre.isNullOrBlank()) {
binding.mangaGenresTagsCompactChips.setChips(manga.getGenres(), controller::performSearch)
binding.mangaGenresTagsFullChips.setChips(manga.getGenres(), controller::performSearch)
} else {
binding.mangaGenresTagsWrapper.gone()
}
// Handle showing more or less info
binding.mangaSummary.clicks()
.onEach { toggleMangaInfo(view.context) }
.launchIn(scope)
binding.mangaInfoToggle.clicks()
.onEach { toggleMangaInfo(view.context) }
.launchIn(scope)
// Expand manga info if navigated from source listing
if (initialLoad && fromSource) {
toggleMangaInfo(view.context)
initialLoad = false
}
}
binding.btnCategories.visibleIf { manga.favorite && controller.presenter.getCategories().isNotEmpty() }
}
private fun showMangaInfo(visible: Boolean) {
binding.mangaSummaryLabel.visibleIf { visible }
binding.mangaSummary.visibleIf { visible }
binding.mangaGenresTagsWrapper.visibleIf { visible }
binding.mangaInfoToggle.visibleIf { visible }
}
private fun toggleMangaInfo(context: Context) {
val isExpanded =
binding.mangaInfoToggle.text == context.getString(R.string.manga_info_collapse)
with(binding.mangaInfoToggle) {
text = if (isExpanded) {
context.getString(R.string.manga_info_expand)
} else {
context.getString(R.string.manga_info_collapse)
}
icon = if (isExpanded) {
context.getDrawable(R.drawable.ic_baseline_expand_more_24dp)
} else {
context.getDrawable(R.drawable.ic_baseline_expand_less_24dp)
}
}
with(binding.mangaSummary) {
maxLines =
if (isExpanded) {
2
} else {
Int.MAX_VALUE
}
ellipsize =
if (isExpanded) {
TextUtils.TruncateAt.END
} else {
null
}
}
binding.mangaGenresTagsCompact.visibleIf { isExpanded }
binding.mangaGenresTagsFullChips.visibleIf { !isExpanded }
}
/**
* Update favorite button with correct drawable and text.
*
* @param isFavorite determines if manga is favorite or not.
*/
private fun setFavoriteButtonState(isFavorite: Boolean) {
// Set the Favorite drawable to the correct one.
// Border drawable if false, filled drawable if true.
binding.btnFavorite.apply {
icon = ContextCompat.getDrawable(
context,
if (isFavorite) R.drawable.ic_favorite_24dp else R.drawable.ic_favorite_border_24dp
)
text =
context.getString(if (isFavorite) R.string.in_library else R.string.add_to_library)
isChecked = isFavorite
}
}
}
}

View File

@ -1,244 +0,0 @@
package eu.kanade.tachiyomi.ui.manga.info
import android.os.Bundle
import com.google.gson.Gson
import com.jakewharton.rxrelay.BehaviorRelay
import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.database.models.MangaCategory
import eu.kanade.tachiyomi.data.download.DownloadManager
import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.online.all.MergedSource
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.ui.browse.source.SourceController
import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed
import eu.kanade.tachiyomi.util.prepUpdateCover
import eu.kanade.tachiyomi.util.removeCovers
import exh.MERGED_SOURCE_ID
import exh.util.await
import java.util.Date
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.withContext
import rx.Observable
import rx.Subscription
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* Presenter of MangaInfoFragment.
* Contains information and data for fragment.
* Observable updates should be called from here.
*/
class MangaInfoPresenter(
val manga: Manga,
val source: Source,
private val chapterCountRelay: BehaviorRelay<Float>,
private val lastUpdateRelay: BehaviorRelay<Date>,
private val mangaFavoriteRelay: PublishRelay<Boolean>,
val smartSearchConfig: SourceController.SmartSearchConfig?,
private val db: DatabaseHelper = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(),
private val coverCache: CoverCache = Injekt.get(),
private val gson: Gson = Injekt.get()
) : BasePresenter<MangaInfoController>() {
/**
* Subscription to update the manga from the source.
*/
private var fetchMangaSubscription: Subscription? = null
override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState)
getMangaObservable()
.subscribeLatestCache({ view, manga -> view.onNextManga(manga, source) })
// Update chapter count
chapterCountRelay.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(MangaInfoController::setChapterCount)
// Update favorite status
mangaFavoriteRelay.observeOn(AndroidSchedulers.mainThread())
.subscribe { setFavorite(it) }
.apply { add(this) }
// update last update date
lastUpdateRelay.observeOn(AndroidSchedulers.mainThread())
.subscribeLatestCache(MangaInfoController::setLastUpdateDate)
}
private fun getMangaObservable(): Observable<Manga> {
return db.getManga(manga.url, manga.source).asRxObservable()
.observeOn(AndroidSchedulers.mainThread())
}
/**
* Fetch manga information from source.
*/
fun fetchMangaFromSource(manualFetch: Boolean = false) {
if (!fetchMangaSubscription.isNullOrUnsubscribed()) return
fetchMangaSubscription = Observable.defer { source.fetchMangaDetails(manga) }
.map { networkManga ->
manga.prepUpdateCover(coverCache, networkManga, manualFetch)
manga.copyFrom(networkManga)
manga.initialized = true
db.insertManga(manga).executeAsBlocking()
manga
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeFirst(
{ view, _ ->
view.onFetchMangaDone()
},
MangaInfoController::onFetchMangaError
)
}
/**
* Update favorite status of manga, (removes / adds) manga (to / from) library.
*
* @return the new status of the manga.
*/
fun toggleFavorite(): Boolean {
manga.favorite = !manga.favorite
if (!manga.favorite) {
manga.removeCovers(coverCache)
}
db.insertManga(manga).executeAsBlocking()
return manga.favorite
}
private fun setFavorite(favorite: Boolean) {
if (manga.favorite == favorite) {
return
}
toggleFavorite()
}
/**
* Returns true if the manga has any downloads.
*/
fun hasDownloads(): Boolean {
return downloadManager.getDownloadCount(manga) > 0
}
/**
* Deletes all the downloads for the manga.
*/
fun deleteDownloads() {
downloadManager.deleteManga(manga, source)
}
/**
* Get user categories.
*
* @return List of categories, not including the default category
*/
fun getCategories(): List<Category> {
return db.getCategories().executeAsBlocking()
}
/**
* Gets the category id's the manga is in, if the manga is not in a category, returns the default id.
*
* @param manga the manga to get categories from.
* @return Array of category ids the manga is in, if none returns default id
*/
fun getMangaCategoryIds(manga: Manga): Array<Int> {
val categories = db.getCategoriesForManga(manga).executeAsBlocking()
return categories.mapNotNull { it.id }.toTypedArray()
}
/**
* Move the given manga to categories.
*
* @param manga the manga to move.
* @param categories the selected categories.
*/
fun moveMangaToCategories(manga: Manga, categories: List<Category>) {
val mc = categories.filter { it.id != 0 }.map { MangaCategory.create(manga, it) }
db.setMangaCategories(mc, listOf(manga))
}
/**
* Move the given manga to the category.
*
* @param manga the manga to move.
* @param category the selected category, or null for default category.
*/
fun moveMangaToCategory(manga: Manga, category: Category?) {
moveMangaToCategories(manga, listOfNotNull(category))
}
/*
suspend fun recommendationView(manga: Manga): Manga {
val title = manga.title
val source = manga.source
}*/
suspend fun smartSearchMerge(manga: Manga, originalMangaId: Long): Manga {
val originalManga = db.getManga(originalMangaId).await()
?: throw IllegalArgumentException("Unknown manga ID: $originalMangaId")
val toInsert = if (originalManga.source == MERGED_SOURCE_ID) {
originalManga.apply {
val originalChildren = MergedSource.MangaConfig.readFromUrl(gson, url).children
if (originalChildren.any { it.source == manga.source && it.url == manga.url }) {
throw IllegalArgumentException("This manga is already merged with the current manga!")
}
url = MergedSource.MangaConfig(
originalChildren + MergedSource.MangaSource(
manga.source,
manga.url
)
).writeAsUrl(gson)
}
} else {
val newMangaConfig = MergedSource.MangaConfig(
listOf(
MergedSource.MangaSource(
originalManga.source,
originalManga.url
),
MergedSource.MangaSource(
manga.source,
manga.url
)
)
)
Manga.create(newMangaConfig.writeAsUrl(gson), originalManga.originalTitle, MERGED_SOURCE_ID).apply {
copyFrom(originalManga)
favorite = true
last_update = originalManga.last_update
viewer = originalManga.viewer
chapter_flags = originalManga.chapter_flags
sorting = Manga.SORTING_NUMBER
}
}
// Note that if the manga are merged in a different order, this won't trigger, but I don't care lol
val existingManga = db.getManga(toInsert.url, toInsert.source).await()
if (existingManga != null) {
withContext(NonCancellable) {
if (toInsert.id != null) {
db.deleteManga(toInsert).await()
}
}
return existingManga
}
// Reload chapters immediately
toInsert.initialized = false
val newId = db.insertManga(toInsert).await().insertedId()
if (newId != null) toInsert.id = newId
return toInsert
}
}

View File

@ -2,31 +2,51 @@ package eu.kanade.tachiyomi.ui.manga.track
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.databinding.TrackControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.swiperefreshlayout.refreshes
import timber.log.Timber
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class TrackController(val fromAllInOne: Boolean = false, val manga: Manga? = null) :
NucleusController<TrackControllerBinding, TrackPresenter>(),
class TrackController :
NucleusController<TrackControllerBinding, TrackPresenter>,
TrackAdapter.OnClickListener,
SetTrackStatusDialog.Listener,
SetTrackChaptersDialog.Listener,
SetTrackScoreDialog.Listener,
SetTrackReadingDatesDialog.Listener {
constructor(manga: Manga?) : super(
Bundle().apply {
putLong(MANGA_EXTRA, manga?.id ?: 0)
}
) {
this.manga = manga
}
constructor(mangaId: Long) : this(
Injekt.get<DatabaseHelper>().getManga(mangaId).executeAsBlocking()
)
@Suppress("unused")
constructor(bundle: Bundle) : this(bundle.getLong(MANGA_EXTRA))
var manga: Manga? = null
private set
private var adapter: TrackAdapter? = null
init {
@ -35,16 +55,12 @@ class TrackController(val fromAllInOne: Boolean = false, val manga: Manga? = nul
setHasOptionsMenu(true)
}
override fun getTitle(): String? {
return manga?.title
}
override fun createPresenter(): TrackPresenter {
// SY -->
return (
if (fromAllInOne && manga != null) {
TrackPresenter(manga)
} else {
TrackPresenter((parentController as MangaController).manga!!)
}
)
// SY <--
return TrackPresenter(manga!!)
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
@ -55,6 +71,8 @@ class TrackController(val fromAllInOne: Boolean = false, val manga: Manga? = nul
override fun onViewCreated(view: View) {
super.onViewCreated(view)
if (manga == null) return
adapter = TrackAdapter(this)
binding.trackRecycler.layoutManager = LinearLayoutManager(view.context)
binding.trackRecycler.adapter = adapter
@ -73,13 +91,6 @@ class TrackController(val fromAllInOne: Boolean = false, val manga: Manga? = nul
val atLeastOneLink = trackings.any { it.track != null }
adapter?.items = trackings
binding.swipeRefresh.isEnabled = atLeastOneLink
// SY -->
if (!fromAllInOne) {
(parentController as? MangaController)?.setTrackingIcon(atLeastOneLink)
} else {
(parentController as? MangaAllInOneController)?.setTrackingIcon(atLeastOneLink)
}
// SY <--
}
fun onSearchResults(results: List<TrackSearch>) {
@ -183,6 +194,7 @@ class TrackController(val fromAllInOne: Boolean = false, val manga: Manga? = nul
}
private companion object {
const val MANGA_EXTRA = "manga"
const val TAG_SEARCH_CONTROLLER = "track_search_controller"
}
}

View File

@ -36,7 +36,7 @@ class TrackSearchAdapter(context: Context) :
} else {
holder = v.tag as TrackSearchHolder
}
holder.onSetValues(track!!)
holder.onSetValues(track)
return v
}

View File

@ -84,7 +84,7 @@ class TrackSearchDialog : DialogController {
// Do an initial search based on the manga's title
if (savedState == null) {
val title = trackController.presenter.manga.originalTitle
val title = trackController.presenter.manga.title
view.track_search.append(title)
search(title)
}

View File

@ -68,7 +68,7 @@ class AboutController : SettingsController() {
}
}
preference {
titleRes = R.string.changelog
titleRes = R.string.whats_new
onClick {
// SY -->

View File

@ -24,6 +24,7 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() {
private set
var currentChapter: ReaderChapter? = null
/**
* Updates this adapter with the given [chapters]. It handles setting a few pages of the
* next/previous chapter to allow seamless transitions and inverting the pages if the viewer

View File

@ -13,14 +13,12 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
import eu.kanade.tachiyomi.data.database.models.History
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.HistoryControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.RootController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.source.browse.ProgressItem
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.system.toast
@ -28,7 +26,6 @@ import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.appcompat.queryTextChanges
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
@ -163,11 +160,7 @@ class HistoryController :
override fun onItemClick(position: Int) {
val manga = (adapter?.getItem(position) as? HistoryItem)?.mch?.manga ?: return
if (Injekt.get<PreferencesHelper>().eh_useNewMangaInterface().get()) {
router.pushController(MangaAllInOneController(manga).withFadeTransaction())
} else {
router.pushController(MangaController(manga).withFadeTransaction())
}
router.pushController(MangaController(manga).withFadeTransaction())
}
override fun removeHistory(manga: Manga, history: History, all: Boolean) {

View File

@ -17,7 +17,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.UpdatesControllerBinding
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
@ -25,7 +24,6 @@ import eu.kanade.tachiyomi.ui.base.controller.RootController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.util.system.notificationManager
@ -35,8 +33,6 @@ import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.recyclerview.scrollStateChanges
import reactivecircus.flowbinding.swiperefreshlayout.refreshes
import timber.log.Timber
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* Fragment that shows recent chapters.
@ -287,13 +283,7 @@ class UpdatesController :
}
private fun openManga(chapter: UpdatesItem) {
// SY -->
if (Injekt.get<PreferencesHelper>().eh_useNewMangaInterface().get()) {
router.pushController(MangaAllInOneController(chapter.manga).withFadeTransaction())
} else {
router.pushController(MangaController(chapter.manga).withFadeTransaction())
}
// SY <--
router.pushController(MangaController(chapter.manga).withFadeTransaction())
}
/**

View File

@ -110,7 +110,7 @@ class SettingsAdvancedController : SettingsController() {
}
preferenceCategory {
titleRes = R.string.label_data
titleRes = R.string.label_network
preference {
titleRes = R.string.pref_clear_cookies

View File

@ -267,13 +267,6 @@ class SettingsGeneralController : SettingsController() {
"Use HIGHLY EXPERIMENTAL automatic ReCAPTCHA solver. Will be grayed out if unsupported by your device."
defaultValue = false
}
switchPreference {
key = Keys.eh_use_new_manga_interface
title = "Use New Manga Interface"
summary = "Use new all in one manga interface"
defaultValue = true
}
}
// <-- EXH
}

View File

@ -72,6 +72,11 @@ class SettingsLibraryController : SettingsController() {
}
.launchIn(scope)
}
switchPreference {
key = Keys.jumpToChapters
titleRes = R.string.pref_jump_to_chapters
defaultValue = false
}
}
preferenceCategory {

View File

@ -77,7 +77,7 @@ object ChapterRecognition {
}
// Remove manga title from chapter title.
val nameWithoutManga = name.replace(manga.originalTitle.toLowerCase(), "").trim()
val nameWithoutManga = name.replace(/* SY --> */ manga.originalTitle.toLowerCase()/* SY <-- */, "").trim()
// Check if first value is number after title remove.
if (updateChapter(withoutManga.find(nameWithoutManga), chapter)) {

View File

@ -160,7 +160,7 @@ fun syncChaptersWithSource(
// Set manga's last update time to latest chapter's fetch time if possible
val newestChapter = db.getChapters(manga).executeAsBlocking().maxBy { it.date_fetch }
manga.last_update = newestChapter?.date_fetch ?: manga.last_update
manga.last_update = newestChapter?.date_fetch ?: Date().time
db.updateLastUpdated(manga).executeAsBlocking()
}

View File

@ -8,7 +8,9 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.annotation.MenuRes
import androidx.annotation.StringRes
import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.TooltipCompat
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipGroup
@ -38,6 +40,15 @@ inline fun View.snack(message: String, length: Int = Snackbar.LENGTH_LONG, f: Sn
return snack
}
/**
* Adds a tooltip shown on long press.
*
* @param stringRes String resource for tooltip.
*/
inline fun View.setTooltip(@StringRes stringRes: Int) {
TooltipCompat.setTooltipText(this, context.getString(stringRes))
}
/**
* Shows a popup menu on top of this view.
*

View File

@ -4,7 +4,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.EhSmartSearchBinding
import eu.kanade.tachiyomi.source.CatalogueSource
import eu.kanade.tachiyomi.source.SourceManager
@ -12,7 +11,6 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.source.SourceController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.CoroutineScope
@ -21,7 +19,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
@ -61,11 +58,7 @@ class SmartSearchController(bundle: Bundle? = null) : NucleusController<EhSmartS
launch(Dispatchers.Default) {
for (event in presenter.smartSearchChannel) {
if (event is SmartSearchPresenter.SearchResults.Found) {
val transaction = if (Injekt.get<PreferencesHelper>().eh_useNewMangaInterface().get()) {
MangaAllInOneController(event.manga, true, smartSearchConfig).withFadeTransaction()
} else {
MangaController(event.manga, true, smartSearchConfig).withFadeTransaction()
}
val transaction = MangaController(event.manga, true, smartSearchConfig).withFadeTransaction()
withContext(Dispatchers.Main) {
router.replaceTopController(transaction)
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z" />
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M7.78,7C9.08,7.04 10,7.53 10.57,8.46C11.13,9.4 11.41,10.56 11.39,11.95C11.4,13.5 11.09,14.73 10.5,15.62C9.88,16.5 8.95,16.97 7.71,17C6.45,16.96 5.54,16.5 4.96,15.56C4.38,14.63 4.09,13.45 4.09,12C4.09,10.55 4.39,9.36 5,8.44C5.59,7.5 6.5,7.04 7.78,7M7.75,8.63C7.31,8.63 6.96,8.9 6.7,9.46C6.44,10 6.32,10.87 6.32,12C6.31,13.15 6.44,14 6.69,14.54C6.95,15.1 7.31,15.37 7.77,15.37C8.69,15.37 9.16,14.24 9.17,12C9.17,9.77 8.7,8.65 7.75,8.63M13.33,17V15.22L13.76,15.24L14.3,15.22L15.34,15.03C15.68,14.92 16,14.78 16.26,14.58C16.59,14.35 16.86,14.08 17.07,13.76C17.29,13.45 17.44,13.12 17.53,12.78L17.5,12.77C17.05,13.19 16.38,13.4 15.47,13.41C14.62,13.4 13.91,13.15 13.34,12.65C12.77,12.15 12.5,11.43 12.46,10.5C12.47,9.5 12.81,8.69 13.47,8.03C14.14,7.37 15,7.03 16.12,7C17.37,7.04 18.29,7.45 18.88,8.24C19.47,9 19.76,10 19.76,11.19C19.75,12.15 19.61,13 19.32,13.76C19.03,14.5 18.64,15.13 18.12,15.64C17.66,16.06 17.11,16.38 16.47,16.61C15.83,16.83 15.12,16.96 14.34,17H13.33M16.06,8.63C15.65,8.64 15.32,8.8 15.06,9.11C14.81,9.42 14.68,9.84 14.68,10.36C14.68,10.8 14.8,11.16 15.03,11.46C15.27,11.77 15.63,11.92 16.11,11.93C16.43,11.93 16.7,11.86 16.92,11.74C17.14,11.61 17.3,11.46 17.41,11.28C17.5,11.17 17.53,10.97 17.53,10.71C17.54,10.16 17.43,9.69 17.2,9.28C16.97,8.87 16.59,8.65 16.06,8.63M9.25,5L12.5,1.75L15.75,5H9.25M15.75,19L12.5,22.25L9.25,19H15.75Z"/>
</vector>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<gradient
android:angle="90"
android:startColor="?android:attr/colorBackground"
android:endColor="#00ffffff"/>
<corners android:radius="0dp" />
</shape>

View File

@ -1,64 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<eu.kanade.tachiyomi.widget.RevealAnimationView
android:id="@+id/reveal_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorAccent"
android:elevation="5dp"
android:visibility="invisible" />
<eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout
android:id="@id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.browse.source.browse.BrowseSourceController">
<FrameLayout
android:id="@+id/linear_recycler_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:descendantFocusability="blocksDescendants"
android:paddingBottom="@dimen/fab_list_padding"
tools:listitem="@layout/chapters_item" />
</FrameLayout>
</eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout>
<eu.kanade.tachiyomi.widget.MaterialFastScroll
android:id="@+id/fast_scroller"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_gravity="end"
app:fastScrollerBubbleEnabled="false"
tools:visibility="visible" />
<eu.kanade.tachiyomi.widget.ActionToolbar
android:id="@+id/action_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:layout_dodgeInsetEdges="bottom" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab"
style="@style/Theme.Widget.FAB"
android:text="@string/action_start"
app:icon="@drawable/ic_play_arrow_24dp"
app:layout_anchor="@id/recycler" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,373 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".ui.browse.source.browse.BrowseSourceController">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.21" />
<ImageView
android:id="@+id/backdrop"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0.2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/material_grey_700" />
<ImageView
android:id="@+id/manga_cover"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
android:background="@drawable/rounded_rectangle"
android:contentDescription="@string/description_cover"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,2:3"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/manga_info_section"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/manga_full_title"
style="@style/TextAppearance.Medium.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:paddingBottom="8dp"
android:text="@string/manga_info_full_title_label"
android:textIsSelectable="false"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="12sp"
app:autoSizeStepGranularity="2sp"
app:autoSizeTextType="uniform"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/manga_author"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="2"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_full_title"
tools:text="Author" />
<TextView
android:id="@+id/manga_chapters_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/manga_info_last_chapter_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_author" />
<TextView
android:id="@+id/manga_chapters"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_chapters_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_chapters_label" />
<TextView
android:id="@+id/manga_last_update_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_latest_data_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_chapters_label" />
<TextView
android:id="@+id/manga_last_update"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_last_update_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_last_update_label" />
<TextView
android:id="@+id/manga_status_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_status_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_last_update_label" />
<TextView
android:id="@+id/manga_status"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_status_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_status_label" />
<TextView
android:id="@+id/manga_source_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_source_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_status_label" />
<TextView
android:id="@+id/manga_source"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="4"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_source_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_source_label" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<HorizontalScrollView
android:id="@+id/actions_bar_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="horizontal">
<LinearLayout
android:id="@+id/actions_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_favorite"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_to_library"
app:icon="@drawable/ic_favorite_border_24dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_tracking"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/track"
android:visibility="gone"
tools:visibility="visible"
app:icon="@drawable/ic_cloud_off_24dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_categories"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_edit_categories"
android:visibility="gone"
app:icon="@drawable/ic_label_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_share"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_share"
android:visibility="gone"
app:icon="@drawable/ic_share_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_webview"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_open_in_web_view"
android:visibility="gone"
app:icon="@drawable/ic_public_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_migrate"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/migrate"
android:visibility="gone"
app:icon="@drawable/baseline_swap_calls_24"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_smart_search"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/eh_merge_with_another_source"
android:visibility="gone"
app:icon="@drawable/eh_ic_find_replace_white_24dp"
tools:visibility="visible" />
</LinearLayout>
</HorizontalScrollView>
<TextView
android:id="@+id/manga_summary_label"
style="@style/TextAppearance.Regular.SubHeading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:text="@string/manga_info_about_label"
android:textIsSelectable="false" />
<TextView
android:id="@+id/manga_summary"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:ellipsize="end"
android:focusable="true"
android:maxLines="3"
android:textIsSelectable="false" />
<FrameLayout
android:id="@+id/manga_genres_tags_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp">
<com.google.android.material.chip.ChipGroup
android:id="@+id/manga_genres_tags_full_chips"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="gone"
app:chipSpacingHorizontal="4dp" />
<HorizontalScrollView
android:id="@+id/manga_genres_tags_compact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="horizontal">
<com.google.android.material.chip.ChipGroup
android:id="@+id/manga_genres_tags_compact_chips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:chipSpacingHorizontal="4dp"
app:singleLine="true" />
</HorizontalScrollView>
</FrameLayout>
<Button
android:id="@+id/manga_info_toggle"
style="@style/Theme.Widget.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:text="@string/manga_info_expand"
android:textSize="12sp" />
<Button
android:id="@+id/merge_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:text="@string/eh_merge_with_another_source"
android:visibility="gone"
tools:visibility="visible" />
<Button
android:id="@+id/recommend_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:text="@string/az_recommends"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>

View File

@ -1,342 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.browse.source.browse.BrowseSourceController">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/manga_cover"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
android:background="@drawable/rounded_rectangle"
android:contentDescription="@string/description_cover"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,3:2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<androidx.core.widget.NestedScrollView
android:id="@+id/info_scrollview"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_cover"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<TextView
android:id="@+id/manga_full_title"
style="@style/TextAppearance.Medium.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:text="@string/manga_info_full_title_label"
android:textIsSelectable="false"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="12sp"
app:autoSizeStepGranularity="2sp"
app:autoSizeTextType="uniform"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/manga_author"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_full_title"
tools:text="Author" />
<TextView
android:id="@+id/manga_chapters_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/manga_info_last_chapter_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_author" />
<TextView
android:id="@+id/manga_chapters"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_chapters_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_chapters_label" />
<TextView
android:id="@+id/manga_last_update_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_latest_data_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_chapters_label" />
<TextView
android:id="@+id/manga_last_update"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_last_update_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_last_update_label" />
<TextView
android:id="@+id/manga_status_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_status_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_last_update_label" />
<TextView
android:id="@+id/manga_status"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_status_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_status_label" />
<TextView
android:id="@+id/manga_source_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_source_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_status_label" />
<TextView
android:id="@+id/manga_source"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="4"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_source_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_source_label" />
<LinearLayout
android:id="@+id/actions_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="8dp"
android:paddingBottom="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_source">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_favorite"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_to_library"
app:icon="@drawable/ic_favorite_border_24dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_categories"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_edit_categories"
android:visibility="gone"
app:icon="@drawable/ic_label_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_share"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_share"
android:visibility="gone"
app:icon="@drawable/ic_share_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_webview"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_open_in_web_view"
android:visibility="gone"
app:icon="@drawable/ic_public_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_migrate"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/migrate"
android:visibility="gone"
app:icon="@drawable/baseline_swap_calls_24"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_smart_search"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/eh_merge_with_another_source"
android:visibility="gone"
app:icon="@drawable/eh_ic_find_replace_white_24dp"
tools:visibility="visible" />
</LinearLayout>
<TextView
android:id="@+id/manga_summary_label"
style="@style/TextAppearance.Regular.SubHeading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/manga_info_about_label"
android:textIsSelectable="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/actions_bar" />
<TextView
android:id="@+id/manga_summary"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:ellipsize="end"
android:focusable="true"
android:maxLines="3"
android:textIsSelectable="false"
app:layout_constraintBottom_toTopOf="@id/manga_genres_tags_wrapper"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_summary_label" />
<FrameLayout
android:id="@+id/manga_genres_tags_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constrainedHeight="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_summary">
<com.google.android.material.chip.ChipGroup
android:id="@+id/manga_genres_tags_full_chips"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:chipSpacingHorizontal="4dp" />
<HorizontalScrollView
android:id="@+id/manga_genres_tags_compact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="horizontal">
<com.google.android.material.chip.ChipGroup
android:id="@+id/manga_genres_tags_compact_chips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipSpacingHorizontal="4dp"
app:singleLine="true" />
</HorizontalScrollView>
</FrameLayout>
<Button
android:id="@+id/manga_info_toggle"
style="@style/Theme.Widget.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/manga_info_expand"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_genres_tags_wrapper" />
<Button
android:id="@+id/merge_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_info_toggle"
android:text="@string/merge"
android:visibility="gone"
tools:visibility="visible"/>
<Button
android:id="@+id/recommend_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="@string/az_recommends"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_info_toggle"
tools:layout_constraintTop_toBottomOf="@+id/merge_btn"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout>

View File

@ -50,11 +50,4 @@
android:layout_gravity="bottom"
app:layout_dodgeInsetEdges="bottom" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab"
style="@style/Theme.Widget.FAB"
android:text="@string/action_start"
app:icon="@drawable/ic_play_arrow_24dp"
app:layout_anchor="@id/recycler" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,65 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<eu.kanade.tachiyomi.widget.RevealAnimationView
android:id="@+id/reveal_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorAccent"
android:elevation="5dp"
android:visibility="invisible" />
<eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout
android:id="@id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.browse.source.browse.BrowseSourceController">
<FrameLayout
android:id="@+id/linear_recycler_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:descendantFocusability="blocksDescendants"
android:paddingBottom="@dimen/fab_list_padding"
tools:listitem="@layout/chapters_item" />
</FrameLayout>
</eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout>
<eu.kanade.tachiyomi.widget.MaterialFastScroll
android:id="@+id/fast_scroller"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerHorizontal="true"
android:layout_gravity="end"
app:fastScrollerBubbleEnabled="false"
tools:visibility="visible" />
<eu.kanade.tachiyomi.widget.ActionToolbar
android:id="@+id/action_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:layout_dodgeInsetEdges="bottom" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/fab"
style="@style/Theme.Widget.FAB"
android:text="@string/action_start"
app:icon="@drawable/ic_play_arrow_24dp"
app:layout_anchor="@id/recycler" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,373 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".ui.browse.source.browse.BrowseSourceController">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.38" />
<ImageView
android:id="@+id/backdrop"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0.2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/material_grey_700" />
<ImageView
android:id="@+id/manga_cover"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
android:background="@drawable/rounded_rectangle"
android:contentDescription="@string/description_cover"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,2:3"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/manga_info_section"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/manga_full_title"
style="@style/TextAppearance.Medium.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:paddingBottom="8dp"
android:text="@string/manga_info_full_title_label"
android:textIsSelectable="false"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="12sp"
app:autoSizeStepGranularity="2sp"
app:autoSizeTextType="uniform"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/manga_author"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="2"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_full_title"
tools:text="Author" />
<TextView
android:id="@+id/manga_chapters_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/manga_info_last_chapter_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_author" />
<TextView
android:id="@+id/manga_chapters"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_chapters_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_chapters_label" />
<TextView
android:id="@+id/manga_last_update_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_latest_data_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_chapters_label" />
<TextView
android:id="@+id/manga_last_update"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_last_update_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_last_update_label" />
<TextView
android:id="@+id/manga_status_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_status_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_last_update_label" />
<TextView
android:id="@+id/manga_status"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_status_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_status_label" />
<TextView
android:id="@+id/manga_source_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_source_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_status_label" />
<TextView
android:id="@+id/manga_source"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="4"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_source_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_source_label" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<HorizontalScrollView
android:id="@+id/actions_bar_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="horizontal">
<LinearLayout
android:id="@+id/actions_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_favorite"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_to_library"
app:icon="@drawable/ic_favorite_border_24dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_tracking"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/track"
android:visibility="gone"
tools:visibility="visible"
app:icon="@drawable/ic_cloud_off_24dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_categories"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_edit_categories"
android:visibility="gone"
app:icon="@drawable/ic_label_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_share"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_share"
android:visibility="gone"
app:icon="@drawable/ic_share_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_webview"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_open_in_web_view"
android:visibility="gone"
app:icon="@drawable/ic_public_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_migrate"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/migrate"
android:visibility="gone"
app:icon="@drawable/baseline_swap_calls_24"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_smart_search"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/eh_merge_with_another_source"
android:visibility="gone"
app:icon="@drawable/eh_ic_find_replace_white_24dp"
tools:visibility="visible" />
</LinearLayout>
</HorizontalScrollView>
<TextView
android:id="@+id/manga_summary_label"
style="@style/TextAppearance.Regular.SubHeading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:text="@string/manga_info_about_label"
android:textIsSelectable="false" />
<TextView
android:id="@+id/manga_summary"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:ellipsize="end"
android:focusable="true"
android:maxLines="3"
android:textIsSelectable="false" />
<FrameLayout
android:id="@+id/manga_genres_tags_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp">
<com.google.android.material.chip.ChipGroup
android:id="@+id/manga_genres_tags_full_chips"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="gone"
app:chipSpacingHorizontal="4dp" />
<HorizontalScrollView
android:id="@+id/manga_genres_tags_compact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="horizontal">
<com.google.android.material.chip.ChipGroup
android:id="@+id/manga_genres_tags_compact_chips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:chipSpacingHorizontal="4dp"
app:singleLine="true" />
</HorizontalScrollView>
</FrameLayout>
<Button
android:id="@+id/manga_info_toggle"
style="@style/Theme.Widget.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:text="@string/manga_info_expand"
android:textSize="12sp" />
<Button
android:id="@+id/merge_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:text="@string/eh_merge_with_another_source"
android:visibility="gone"
tools:visibility="visible" />
<Button
android:id="@+id/recommend_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:text="@string/az_recommends"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp"
tools:context=".ui.browse.source.browse.BrowseSourceController">
<TextView
android:id="@+id/chapters_label"
style="@style/TextAppearance.Regular.SubHeading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/chapters"
android:textIsSelectable="false" />
</RelativeLayout>

View File

@ -1,373 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.browse.source.browse.BrowseSourceController">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.38" />
<ImageView
android:id="@+id/backdrop"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0.2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@color/material_grey_700" />
<ImageView
android:id="@+id/manga_cover"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="16dp"
android:background="@drawable/rounded_rectangle"
android:contentDescription="@string/description_cover"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="h,2:3"
app:layout_constraintEnd_toStartOf="@+id/guideline2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/manga_info_section"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/manga_full_title"
style="@style/TextAppearance.Medium.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:text="@string/manga_info_full_title_label"
android:textIsSelectable="false"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="12sp"
app:autoSizeStepGranularity="2sp"
app:autoSizeTextType="uniform"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/manga_author"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textIsSelectable="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_full_title"
tools:text="Author" />
<TextView
android:id="@+id/manga_chapters_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="@string/manga_info_last_chapter_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_author" />
<TextView
android:id="@+id/manga_chapters"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_chapters_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_chapters_label" />
<TextView
android:id="@+id/manga_last_update_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_latest_data_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_chapters_label" />
<TextView
android:id="@+id/manga_last_update"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_last_update_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_last_update_label" />
<TextView
android:id="@+id/manga_status_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_status_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_last_update_label" />
<TextView
android:id="@+id/manga_status"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_status_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_status_label" />
<TextView
android:id="@+id/manga_source_label"
style="@style/TextAppearance.Medium.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/manga_info_source_label"
android:textIsSelectable="false"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/manga_status_label" />
<TextView
android:id="@+id/manga_source"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="4"
android:textIsSelectable="false"
app:layout_constraintBaseline_toBaselineOf="@+id/manga_source_label"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_source_label" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<HorizontalScrollView
android:id="@+id/actions_bar_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="horizontal">
<LinearLayout
android:id="@+id/actions_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingTop="8dp"
android:paddingEnd="16dp"
android:paddingBottom="8dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_favorite"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_to_library"
app:icon="@drawable/ic_favorite_border_24dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_categories"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_edit_categories"
android:visibility="gone"
app:icon="@drawable/ic_label_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_share"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_share"
android:visibility="gone"
app:icon="@drawable/ic_share_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_webview"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_open_in_web_view"
android:visibility="gone"
app:icon="@drawable/ic_public_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_migrate"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/migrate"
android:visibility="gone"
app:icon="@drawable/baseline_swap_calls_24"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_smart_search"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/eh_merge_with_another_source"
android:visibility="gone"
app:icon="@drawable/eh_ic_find_replace_white_24dp"
tools:visibility="visible" />
</LinearLayout>
</HorizontalScrollView>
<TextView
android:id="@+id/manga_summary_label"
style="@style/TextAppearance.Regular.SubHeading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp"
android:text="@string/manga_info_about_label"
android:textIsSelectable="false" />
<TextView
android:id="@+id/manga_summary"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:ellipsize="end"
android:focusable="true"
android:maxLines="3"
android:textIsSelectable="false" />
<FrameLayout
android:id="@+id/manga_genres_tags_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp">
<com.google.android.material.chip.ChipGroup
android:id="@+id/manga_genres_tags_full_chips"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="gone"
app:chipSpacingHorizontal="4dp" />
<HorizontalScrollView
android:id="@+id/manga_genres_tags_compact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="horizontal">
<com.google.android.material.chip.ChipGroup
android:id="@+id/manga_genres_tags_compact_chips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:chipSpacingHorizontal="4dp"
app:singleLine="true" />
</HorizontalScrollView>
</FrameLayout>
<Button
android:id="@+id/manga_info_toggle"
style="@style/Theme.Widget.Button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="@string/manga_info_expand"
android:textSize="12sp" />
<Button
android:id="@+id/merge_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:text="@string/eh_merge_with_another_source"
android:visibility="gone"
tools:visibility="visible"/>
<Button
android:id="@+id/recommend_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:text="@string/az_recommends"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout>

View File

@ -0,0 +1,310 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:context=".ui.browse.source.browse.BrowseSourceController">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/backdrop"
android:layout_width="0dp"
android:layout_height="0dp"
android:alpha="0.2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:background="@mipmap/ic_launcher" />
<View
android:layout_width="match_parent"
android:layout_height="88dp"
android:background="@drawable/manga_info_gradient"
app:layout_constraintBottom_toBottomOf="parent" />
<LinearLayout
android:id="@+id/manga_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="16dp"
app:layout_constraintTop_toTopOf="parent">
<eu.kanade.tachiyomi.ui.manga.info.MangaCoverImageView
android:id="@+id/manga_cover"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rounded_rectangle"
android:contentDescription="@string/description_cover"
android:maxWidth="220dp"
tools:src="@mipmap/ic_launcher" />
<LinearLayout
android:id="@+id/manga_info_section"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/manga_cover"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/manga_full_title"
style="@style/TextAppearance.Medium.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="3"
android:text="@string/manga_info_full_title_label"
android:textIsSelectable="false"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="12sp"
app:autoSizeStepGranularity="2sp"
app:autoSizeTextType="uniform" />
<TextView
android:id="@+id/manga_author"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="false"
tools:text="Author" />
<TextView
android:id="@+id/manga_artist"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textIsSelectable="false"
tools:text="Artist" />
<TextView
android:id="@+id/manga_status"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textIsSelectable="false"
tools:text="Status" />
<TextView
android:id="@+id/manga_source"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
android:textIsSelectable="false"
tools:text="Source" />
</LinearLayout>
</LinearLayout>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="horizontal"
app:layout_constraintTop_toBottomOf="@+id/manga_info">
<LinearLayout
android:id="@+id/actions_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_favorite"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_to_library"
app:icon="@drawable/ic_favorite_border_24dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_tracking"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/manga_tracking_tab"
android:visibility="gone"
app:icon="@drawable/ic_sync_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_categories"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_edit_categories"
android:visibility="gone"
app:icon="@drawable/ic_label_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_share"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_share"
android:visibility="gone"
app:icon="@drawable/ic_share_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_webview"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/action_open_in_web_view"
android:visibility="gone"
app:icon="@drawable/ic_public_24dp"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_migrate"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/migrate"
android:visibility="gone"
app:icon="@drawable/baseline_swap_calls_24"
tools:visibility="visible" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_smart_search"
style="@style/Theme.Widget.Button.Icon.Textless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:contentDescription="@string/eh_merge_with_another_source"
android:visibility="gone"
app:icon="@drawable/eh_ic_find_replace_white_24dp"
tools:visibility="visible" />
</LinearLayout>
</HorizontalScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<TextView
android:id="@+id/manga_summary_label"
style="@style/TextAppearance.Regular.SubHeading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/manga_info_about_label"
android:textIsSelectable="false" />
<com.google.android.material.button.MaterialButton
android:id="@+id/manga_info_toggle"
style="@style/Theme.Widget.Button.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:text="@string/manga_info_expand"
app:icon="@drawable/ic_baseline_expand_more_24dp"
app:iconTint="?attr/colorOnPrimary" />
</RelativeLayout>
<TextView
android:id="@+id/manga_summary"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:ellipsize="end"
android:focusable="true"
android:maxLines="2"
android:textIsSelectable="false"
tools:text="Summary" />
<FrameLayout
android:id="@+id/manga_genres_tags_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp">
<com.google.android.material.chip.ChipGroup
android:id="@+id/manga_genres_tags_full_chips"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:visibility="gone"
app:chipSpacingHorizontal="4dp" />
<HorizontalScrollView
android:id="@+id/manga_genres_tags_compact"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:requiresFadingEdge="horizontal">
<com.google.android.material.chip.ChipGroup
android:id="@+id/manga_genres_tags_compact_chips"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:chipSpacingHorizontal="4dp"
app:singleLine="true" />
</HorizontalScrollView>
</FrameLayout>
<Button
android:id="@+id/merge_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:text="@string/eh_merge_with_another_source"
android:visibility="gone"
tools:visibility="visible" />
<Button
android:id="@+id/recommend_btn"
style="@style/Theme.Widget.Button.FilledAccent"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginStart="16dp"
android:layout_marginTop="4dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="4dp"
android:text="@string/az_recommends"
android:visibility="gone"
tools:visibility="visible" />
</LinearLayout>

View File

@ -32,11 +32,29 @@
</item>
<item
android:id="@+id/action_sort"
android:icon="@drawable/ic_sort_by_numeric_white_24dp"
android:title="@string/action_sort"
android:icon="@drawable/ic_sort_24dp"
android:title="@string/sorting_mode"
app:iconTint="?attr/colorOnPrimary"
app:showAsAction="ifRoom" />
app:showAsAction="ifRoom">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/sort_by_source"
android:title="@string/sort_by_source" />
<item
android:id="@+id/sort_by_number"
android:title="@string/sort_by_number" />
<item
android:id="@+id/sort_by_upload_date"
android:title="@string/sort_by_upload_date" />
</group>
<item
android:id="@+id/action_sort_descending"
android:checkable="true"
android:title="@string/action_sort_descending" />
</menu>
</item>
<item
android:title="@string/action_display_mode"
@ -54,24 +72,7 @@
</item>
<item
android:title="@string/sorting_mode"
app:showAsAction="never">
<menu>
<group android:checkableBehavior="single">
<item
android:id="@+id/sort_by_source"
android:title="@string/sort_by_source" />
<item
android:id="@+id/sort_by_number"
android:title="@string/sort_by_number" />
<item
android:id="@+id/sort_by_upload_date"
android:title="@string/sort_by_upload_date" />
</group>
</menu>
</item>
<item
android:id="@+id/download_group"
android:title="@string/manga_download"
app:showAsAction="never">
<menu>
@ -96,6 +97,16 @@
</menu>
</item>
<item
android:id="@+id/action_edit_cover"
android:title="@string/action_edit_cover"
app:showAsAction="never" />
<!--<item
android:id="@+id/action_migrate"
android:title="@string/action_migrate"
app:showAsAction="never" />-->
<item
android:id="@+id/action_edit"
android:icon="@drawable/ic_edit_24dp"

View File

@ -11,4 +11,9 @@
android:title="@string/action_disable_all"
app:showAsAction="never" />
<item
android:id="@+id/action_open_in_settings"
android:title="@string/action_open_in_settings"
app:showAsAction="never" />
</menu>

View File

@ -231,10 +231,6 @@
<string name="unknown">غير معروف</string>
<string name="licensed">مُرخّصة</string>
<string name="remove_from_library">إزالة من المكتبة</string>
<string name="manga_info_chapters_label">الفصول</string>
<string name="manga_info_status_label">الحالة</string>
<string name="manga_info_source_label">المصدر</string>
<string name="manga_info_genres_label">التصنيفات</string>
<string name="delete_downloads_for_manga">حذف الفصول المحمله؟</string>
<string name="manga_chapters_tab">الفصول</string>
<string name="manga_chapter_no_title">بدون عنوان</string>
@ -308,7 +304,6 @@
<string name="update_check_notification_download_complete">اكتمل التحميل</string>
<string name="update_check_notification_download_error">حدث خطأ فى التحميل</string>
<string name="update_check_notification_update_available">يتوفّر تحديث جديد!</string>
<string name="description_backdrop">خلفية المانجا</string>
<string name="description_cover">غلاف المانجا</string>
<string name="information_no_downloads">لا توجد تحميلات</string>
<string name="information_no_recent">لا توجد تحديثات جديدة</string>
@ -367,7 +362,6 @@
<string name="track_start_date">تم البدء فيها</string>
<string name="track_type">النوع</string>
<string name="track_author">المؤلف</string>
<string name="url_not_set">لم يتم تعيين رابط المانجا، يُرجى الضغط على العنوان وتحديد مانجا مرة أخرى</string>
<string name="migration_info">اضغط لتحديد مصدر للترحيل منه</string>
<string name="migration_dialog_what_to_include">اختر بيانات لتضمينها</string>
<string name="migrate">ترحيل</string>
@ -408,7 +402,6 @@
<string name="action_newest">الأجدد</string>
<string name="action_reorganize_by">تسجيل</string>
<string name="action_cancel_all">إلغاء الكل</string>
<string name="action_hide">إخفاء</string>
<string name="action_view_chapters">عرض الفصول</string>
<string name="action_sort_latest_chapter">أحدث الفصول</string>
<string name="action_sort_last_checked">المحدثة أخيرًا</string>
@ -529,8 +522,6 @@
<string name="manga_info_collapse">إظهار معلومات أقل</string>
<string name="manga_info_expand">إظهار المزيد من المعلومات</string>
<string name="manga_info_about_label">حول</string>
<string name="manga_info_latest_data_label">تم التحديث</string>
<string name="manga_info_last_chapter_label">آخر فصل</string>
<string name="in_library">في المكتبة</string>
<string name="add_to_library">إضافة إلى المكتبة</string>
<string name="local_source_help_guide">دليل استخدام ميزة المصدر المحلي</string>
@ -615,5 +606,4 @@
<string name="action_disable_all">تعطيل الكل</string>
<string name="pref_show_reading_mode_summary">إظهار الوضع الحالي لفترةٍ وجيزةٍ عند فتح القارئ</string>
<string name="pref_show_reading_mode">إظهار وضع القراءة</string>
<string name="enabled">مُفعّل</string>
</resources>

View File

@ -70,4 +70,4 @@
<string name="label_download_queue">Cola de descarga</string>
<string name="label_more">Más</string>
<string name="track">Siguiendo</string>
</resources>
</resources>

View File

@ -197,10 +197,6 @@
<string name="unknown">Неизвестно</string>
<string name="licensed">Лицензирана</string>
<string name="remove_from_library">Премахни от библиотеката</string>
<string name="manga_info_chapters_label">Глави</string>
<string name="manga_info_status_label">Статус</string>
<string name="manga_info_source_label">Източник</string>
<string name="manga_info_genres_label">Жанрове</string>
<!-- Manga chapters fragment -->
<string name="manga_chapters_tab">Глави</string>
<string name="manga_chapter_no_title">Неозаглавена</string>
@ -289,7 +285,6 @@
<string name="update_check_notification_download_error">Грешка при изтегляне</string>
<string name="update_check_notification_update_available">Има нова версия!</string>
<!--Content Description-->
<string name="description_backdrop">Фон на мангата</string>
<string name="description_cover">Корица</string>
<!-- Information Text -->
<string name="information_no_downloads">Нямате изтегляния</string>
@ -406,7 +401,6 @@
<string name="track_start_date">Начало</string>
<string name="track_type">Тип</string>
<string name="track_author">Автор</string>
<string name="url_not_set">Мангата няма връзка с акаунта, молим отново да натиснете заглавието и да изберете мангата</string>
<string name="transition_finished">Завършени:</string>
<string name="transition_current">Текущи:</string>
<string name="transition_next">Следва:</string>
@ -484,8 +478,6 @@
<string name="manga_info_collapse">Покажи по-малко</string>
<string name="manga_info_expand">Покажи повече</string>
<string name="manga_info_about_label">Инфо</string>
<string name="manga_info_latest_data_label">Обновени</string>
<string name="manga_info_last_chapter_label">Последна глава</string>
<string name="in_library">В библиотеката</string>
<string name="add_to_library">Добави към библиотеката</string>
<string name="local_source_help_guide">Помощ за локални източници</string>
@ -560,7 +552,6 @@
<string name="action_cancel_all">Отмени всичко</string>
<string name="action_unpin">Откачи</string>
<string name="action_pin">Закачи</string>
<string name="action_hide">Скрий</string>
<string name="action_view_chapters">Преглед на глави</string>
<string name="action_select_inverse">Избери обратно</string>
<string name="action_sort_latest_chapter">Последна глава</string>

View File

@ -231,10 +231,6 @@
<string name="unknown">অজানা</string>
<string name="licensed">লাইসেন্সকৃত</string>
<string name="remove_from_library">মাংগাশালা থেকে সরিয়ে ফেলুন</string>
<string name="manga_info_chapters_label">অধ্যায়গুলো</string>
<string name="manga_info_status_label">স্ট্যাটাস</string>
<string name="manga_info_source_label">উৎস</string>
<string name="manga_info_genres_label">প্রকারভেদ</string>
<string name="delete_downloads_for_manga">ডাউনলোডকৃত অধ্যায়গুলো মুছে ফেলতে চান?</string>
<string name="manga_chapters_tab">অধ্যায়গুলো</string>
<string name="manga_chapter_no_title">শিরোনামহীন</string>
@ -308,7 +304,6 @@
<string name="update_check_notification_download_complete">ডাউনলোড হয়ে গেছে</string>
<string name="update_check_notification_download_error">ডাউনলোড ত্রুটি দেখা গিয়েছে</string>
<string name="update_check_notification_update_available">আপডেট এসেছে!</string>
<string name="description_backdrop">মাংগার ব্যাকড্রপ ছবি</string>
<string name="description_cover">মাংগার কভার</string>
<string name="information_no_downloads">ডাউনলোড নেই</string>
<string name="information_no_recent">কোন সাম্প্রতিক অধ্যায় নেই</string>
@ -377,7 +372,6 @@
<string name="transition_no_previous">কোন পূর্ববর্তী অধ্যায় আছে</string>
<string name="transition_pages_loading">পৃষ্ঠা লোড হচ্ছে …</string>
<string name="transition_pages_error">পৃষ্ঠা লোড করতে ব্যর্থ হয়েছে: %1$s</string>
<string name="url_not_set">মাংগা ইউআরএল সেট না শিরোনাম ক্লিক করুন এবং আবার মাংগা নির্বাচন করুন</string>
<string name="pref_read_with_long_tap">দীর্ঘক্ষণ ট্যাপ ডায়ালগ</string>
<string name="action_open_in_web_view">ওয়েব দর্শনে খুলুন</string>
<string name="pref_true_color">৩২-বিট রঙ</string>

View File

@ -263,10 +263,6 @@
<string name="manga_info_full_title_label">Títol</string>
<string name="manga_added_library">S\'ha afegit a la biblioteca</string>
<string name="manga_removed_library">S\'ha eliminat de la biblioteca</string>
<string name="manga_info_chapters_label">Capítols</string>
<string name="manga_info_status_label">Estat</string>
<string name="manga_info_source_label">Font</string>
<string name="manga_info_genres_label">Gèneres</string>
<string name="delete_downloads_for_manga">Voleu suprimir els capítols baixats\?</string>
<string name="copied_to_clipboard">S\'ha copiat al porta-retalls:
\n%1$s</string>
@ -309,7 +305,6 @@
<string name="track_start_date">Començat</string>
<string name="track_type">Tipus</string>
<string name="track_author">Autor</string>
<string name="url_not_set">La URL del manga no està definida, feu clic al títol i torneu a seleccionar un manga</string>
<string name="error_category_exists">Ja hi ha una categoria amb aquest nom!</string>
<string name="snack_categories_deleted">S\'han suprimit les categories</string>
<string name="dialog_with_checkbox_remove_description">S\'eliminarà la data de lectura d\'aquest capítol. N\'esteu segur\?</string>
@ -363,7 +358,6 @@
<string name="update_check_notification_download_complete">Baixada completada</string>
<string name="update_check_notification_download_error">Error de baixada</string>
<string name="update_check_notification_update_available">Nova versió disponible!</string>
<string name="description_backdrop">Imatge de fons del manga</string>
<string name="description_cover">Portada del manga</string>
<string name="information_no_downloads">No hi ha baixades</string>
<string name="information_no_recent">No hi ha actualitzacions recents</string>
@ -454,7 +448,6 @@
<item quantity="one">Capítols %1$s i 1 més</item>
<item quantity="other">Capítols %1$s i %2$d més</item>
</plurals>
<string name="action_hide">Amaga</string>
<string name="hide_notification_content">Amaga el contingut de les notificacions</string>
<string name="notification_check_updates">S\'està comprovant si hi ha capítols nous</string>
<string name="pref_disable_battery_optimization">Desactiva l\'optimització de la bateria</string>
@ -526,8 +519,6 @@
<string name="last_used_source">Utilitzada per darrer cop</string>
<string name="check_for_updates">Comprova si hi ha actualitzacions</string>
<string name="restore_duration">%02d min i %02d s</string>
<string name="manga_info_latest_data_label">Actualitzat</string>
<string name="manga_info_last_chapter_label">Darrer capítol</string>
<string name="downloaded_only_summary">Filtra tot el manga de la vostra biblioteca</string>
<plurals name="download_queue_summary">
<item quantity="one">En resta 1</item>
@ -577,5 +568,4 @@
<string name="page_list_empty_error">No s\'ha trobat cap pàgina</string>
<string name="action_disable_all">Desactiva-ho tot</string>
<string name="action_enable_all">Activa-ho tot</string>
<string name="enabled">Activat</string>
</resources>

View File

@ -129,9 +129,6 @@
<string name="manga_info_full_title_label">Název</string>
<string name="manga_added_library">Přidáno do knihovny</string>
<string name="manga_removed_library">Odstraněno z knihovny</string>
<string name="manga_info_chapters_label">Kapitoly</string>
<string name="manga_info_source_label">Zdroj</string>
<string name="manga_info_genres_label">Žánry</string>
<string name="delete_downloads_for_manga">Smazat stažené kapitoly\?</string>
<string name="source_not_installed">Zdroj není nainstalován: %1$s</string>
<string name="manga_chapters_tab">Kapitoly</string>
@ -314,7 +311,6 @@
<string name="browse">Procházet</string>
<string name="unknown">Neznámý</string>
<string name="licensed">Licencovaný</string>
<string name="manga_info_status_label">Stav</string>
<string name="copied_to_clipboard">%1$s zkopírováno do schránky</string>
<string name="chapter_queued">Ve frontě</string>
<string name="fetch_chapters_error">Chyba při načítání kapitol.</string>
@ -360,7 +356,6 @@
<string name="invalid_combination">Výchozí nemůže být vybrána s ostatními kategoriemi</string>
<string name="show_title">Zobrazit název</string>
<string name="show_chapter_number">Zobrazit číslo kapitoly</string>
<string name="url_not_set">Url mangy není nastaveno, klikněte prosím na název a vyberte mangu znovu.</string>
<string name="error_category_exists">Kategorie s tímto jménem již existuje!</string>
<string name="dialog_with_checkbox_remove_description">Toto odstraní datum přečtení této kapitoly. Jste si jistý\?</string>
<string name="confirm_set_image_as_cover">Chcete tento obrázek nastavit jako obal\?</string>
@ -371,7 +366,6 @@
<string name="notification_first_add_to_library">Přidejte si prosím mangu do knihovny před tím, než toto uděláte</string>
<string name="file_select_cover">Vybrat obrázek obalu</string>
<string name="update_check_notification_download_in_progress">Probíhá stahování…</string>
<string name="description_backdrop">Obrázek pozadí manga</string>
<string name="description_cover">Obal mangy</string>
<string name="information_no_downloads">Žádné stahování</string>
<string name="information_empty_library">Vaše knihovna je prázdná, můžete si sem přidat něco z Katalogů.</string>

View File

@ -0,0 +1,574 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="description_cover">عکس روی جلد مانگا</string>
<plurals name="update_check_notification_ext_updates">
<item quantity="one">بروزرسانی برای یک افرونه موجود است</item>
<item quantity="other">بروزرسانی برای %d افرونه موجود است</item>
</plurals>
<string name="update_check_notification_update_available">نسخه جدید موجود است!</string>
<string name="update_check_notification_download_error">خطا در دانلود</string>
<string name="update_check_notification_download_complete">دانلود تکمیل شد</string>
<string name="update_check_notification_download_in_progress">در حال دانلود…</string>
<string name="update_check_notification_file_download">دانلود بروزرسانی</string>
<string name="update_check_look_for_updates">در حال جستجوی به روزرسانی ها</string>
<string name="update_check_no_new_updates">هیچ بروزرسانی جدیدی موجود نیست</string>
<string name="update_check_ignore">نادیده گرفتن</string>
<string name="update_check_confirm">دانلود</string>
<string name="file_select_icon">آیکون میانبر را انتخاب کنید</string>
<string name="file_select_backup">فایل پشتیبان را انتخاب کنید</string>
<string name="file_select_cover">عکس روی جلد را انتخاب کنید</string>
<string name="notification_no_connection_body">اتصال در دسترس نیست</string>
<string name="notification_no_connection_title">هم گام سازی کنسل شد</string>
<string name="notification_not_connected_to_ac_body">دستگاه به منبع شارژ وصل نیست</string>
<string name="notification_not_connected_to_ac_title">هم گام سازی کنسل شد</string>
<string name="notification_first_add_to_library">لطفا قبل این کار مانگای موردنظر را به کتابخانه اضافه کنید</string>
<string name="notification_cover_update_failed">آپدیت عکس روی جلد با خطا مواجه شد</string>
<plurals name="notification_update_error">
<item quantity="one">۱ آپدیت با خطا مواجه شد</item>
<item quantity="other">%1$d آپدیت با خطا مواجه شد</item>
</plurals>
<plurals name="notification_chapters_multiple_and_more">
<item quantity="one">فصل های %1$s و ۱ فصل دیگر‬</item>
<item quantity="other">فصل های %1$s و %2$d فصل دیگر‬</item>
</plurals>
<string name="notification_chapters_multiple">فصل های %1$s</string>
<string name="notification_chapters_single_and_more">فصل %1$s و %2$d فصل دیگر</string>
<string name="notification_chapters_single">فصل %1$s</string>
<string name="notification_new_chapters">فصل‌های جدید پیدا شدند</string>
<string name="notification_update_progress">روند بروزرسانی: %1$d/%2$d</string>
<string name="notification_check_updates">درحال بررسی برای فصل‌های جدید</string>
<string name="download_queue_error">دانلود فصل‌ها با خطا مواجه شد. با مراجعه به قسمت دانلودها می‌توانید دوباره تلاش کنید</string>
<string name="copy">کپی</string>
<string name="migrate">تغییر منبع</string>
<string name="migration_selection_prompt">منبعی که میخواهید به آن منتقل شود را انتخاب کنید</string>
<string name="migration_dialog_what_to_include">اطلاعاتی که می‌خواهید منتقل شود را انتخاب کنید</string>
<string name="migration_info">منبعی که میخواهید از آن تغییر دهید را انتخاب کنید</string>
<string name="recent_manga_time">فصل %1$s - %2$s</string>
<string name="updating_library">درحال آپدیت کتابخانه</string>
<string name="page_list_empty_error">صفحه ای یافت نشد</string>
<string name="transition_pages_error">لود صفحات با خطا مواجه شد: %1$s</string>
<string name="transition_pages_loading">درحال لود صفحات…</string>
<string name="transition_no_previous">فصل قبلی وجود ندارد</string>
<string name="transition_no_next">فصل بعدی وجود ندارد</string>
<string name="transition_previous">قبلی:</string>
<string name="transition_next">بعدی:</string>
<string name="transition_current">فعلی:</string>
<string name="transition_finished">تمام شده:</string>
<string name="viewer">حالت خواندن</string>
<string name="confirm_set_image_as_cover">این عکس، به عنوان عکس روی جلد تنظیم شود؟</string>
<string name="no_previous_chapter">فصل قبلی یافت نشد</string>
<string name="no_next_chapter">فصل بعدی یافت نشد</string>
<string name="chapter_subtitle">فصل %1$s</string>
<string name="chapter_progress">صفحه: %1$d</string>
<string name="download_progress">%1$d%% دانلود شده</string>
<string name="downloading">درحال دانلود…</string>
<string name="page_downloaded">صفحه به %1$s کپی شد</string>
<string name="cover_updated">عکس روی جلد بروزرسانی شد</string>
<string name="set_as_cover">تنظیم به عنوان عکس روی جلد</string>
<string name="custom_filter">فیلتر سفارشی</string>
<string name="saving_picture">درحال ذخیره عکس</string>
<string name="picture_saved">عکس ذخیره شد</string>
<string name="dialog_with_checkbox_reset">ریست اطلاعات همه فصل های این مانگا</string>
<string name="dialog_with_checkbox_remove_description">با این کار تاریخ خواندن این فصل حذف می شود. مطمئنید؟</string>
<string name="snack_categories_deleted">دسته بندی ها حذف شدند</string>
<string name="error_category_exists">دسته بندی با این نام قبلا ساخته شده است!</string>
<string name="error_invalid_date_supplied">تاریخ نامعتبر وارد شد</string>
<string name="track_author">نویسنده</string>
<string name="track_type">نوع</string>
<string name="track_finished_reading_date">تاریخ اتمام خواندن</string>
<string name="track_started_reading_date">تاریخ شروع خواندن</string>
<string name="track_start_date">شروع شده</string>
<string name="track_status">وضعیت خوانده شده</string>
<string name="status">وضعیت خوانده شده</string>
<string name="title">عنوان</string>
<string name="score">امتیاز</string>
<string name="repeating">بازخوانی</string>
<string name="paused">متوقف شده</string>
<string name="on_hold">نگه داشته شده(On hold)</string>
<string name="dropped">نیمه رها شده</string>
<string name="completed">تکمیل شده</string>
<string name="currently_reading">درحال خواندن</string>
<string name="reading">درحال خواندن</string>
<string name="add_tracking">اضافه کردن ردیابی</string>
<string name="manga_tracking_tab">ردیابی</string>
<string name="invalid_download_dir">مکان دانلود نامعتبر است</string>
<string name="confirm_delete_chapters">آیا مطمئنید که می خواهید فصل های انتخاب شده را حذف کنید؟</string>
<string name="download_unread">خوانده نشده</string>
<string name="download_all">همه</string>
<string name="download_10">۱۰ فصل بعدی</string>
<string name="download_5">۵ فصل بعدی</string>
<string name="download_1">فصل بعدی</string>
<string name="custom_download">تعداد خاصی دانلود شوند</string>
<string name="manga_download">دانلود شود</string>
<string name="sort_by_upload_date">بر اساس تاریخ آپلود</string>
<string name="pref_cutout_short">Show content in cutout area</string>
<string name="track">ردیابی</string>
<string name="sort_by_source">بر اساس عنوان منبع</string>
<string name="sort_by_number">بر اساس شماره فصل</string>
<string name="sorting_mode">حالت مرتب سازی</string>
<string name="show_chapter_number">شماره فصل</string>
<string name="show_title">عنوان منبع</string>
<string name="fetch_chapters_error">واکشی فصل‌ها با خطا مواجه شد</string>
<string name="chapter_paused">متوقف</string>
<string name="chapter_error">خطا</string>
<string name="chapter_downloading_progress">درحال بارگیری (%1$d/%2$d)</string>
<string name="chapter_downloading">در حال بارگیری</string>
<string name="chapter_queued">در صف</string>
<string name="chapter_downloaded">بارگیری شده</string>
<string name="display_mode_chapter">فصل %1$s</string>
<string name="manga_chapter_no_title">بدون عنوان</string>
<string name="manga_chapters_tab">فصل‌ها</string>
<string name="snack_add_to_library">مانگا به کتابخانه اضافه شود؟</string>
<string name="source_not_installed">منبع نصب نشده است: %1$s</string>
<string name="copied_to_clipboard">کپی شد:
\n%1$s</string>
<string name="delete_downloads_for_manga">فصلهای بارگیری شده را نیز حذف شوند؟</string>
<string name="manga_info_collapse">نمایش اطلاعات کمتر</string>
<string name="manga_info_expand">نمایش اطلاعات بیشتر</string>
<string name="manga_info_about_label">درباره</string>
<string name="manga_removed_library">از کتابخانه حذف شد</string>
<string name="manga_added_library">به کتابخانه اضافه شد</string>
<string name="manga_info_full_title_label">عنوان</string>
<string name="remove_from_library">حذف از کتابخانه</string>
<string name="in_library">در کتابخانه</string>
<string name="pref_refresh_library_tracking_summary">وضعیت خواندن، امتیاز و آخرین فصل خوانده شده را از سایت خدمات ردیابی بروزرسانی می‌کند</string>
<string name="pref_refresh_library_tracking">تازه کردن ردیابی</string>
<string name="tracking_info">همگام سازی یک طرفه برای به روز رسانی اطلاعات خواندن فصل در سایت‌های خدمات ردیابی. برای ردیابی هر مانگا از تب ردیابی اقدام کنید.</string>
<string name="webtoon_viewer">وب تون(Webtoon)</string>
<string name="filter_mode_darken">Burn / Darken</string>
<string name="filter_mode_lighten">Dodge / Lighten</string>
<string name="update_12hour">هر ۱۲ ساعت یکبار</string>
<string name="action_move_category">انتخاب دسته بندی</string>
<string name="unlock_app">بازکردن تاچی یومی</string>
<string name="information_empty_category">هیچ دسته بندی وجود ندارد. با زدن دکمه به علاوه دسته بندی جدیدی بسازید.</string>
<string name="information_empty_library">کتابخانه خالی است, از قسمت مرور مانگاای اضافه کنید.</string>
<string name="information_no_recent_manga">هیچ چیز مانگاای به تازگی خوانده شده</string>
<string name="information_no_recent">هیچ فصل جدیدی یافت نشد</string>
<string name="information_no_downloads">بارگیری فعالی وجود ندارد</string>
<string name="label_help">راهنما</string>
<string name="label_extension_info">اطلاعات افزونه</string>
<string name="label_extensions">افزونه‌ها</string>
<string name="label_migration">تغییر منبع</string>
<string name="label_backup">پشتیبان گیری</string>
<string name="label_categories">دسته بندی ها</string>
<string name="label_sources">منابع</string>
<string name="label_recent_manga">تاریخچه</string>
<string name="label_recent_updates">بروزرسانی‌ها</string>
<string name="label_library">کتابخانه</string>
<string name="label_download_queue">صف بارگیری</string>
<string name="label_settings">تنضیمات</string>
<string name="label_more">بیشتر</string>
<string name="name">نام</string>
<string name="add_to_library">افزودن به کتابخانه</string>
<string name="licensed">مجوزدار</string>
<string name="unknown">نامعلوم</string>
<string name="ongoing">تمام نشده</string>
<string name="description">شرح</string>
<string name="manga_detail_tab">اطلاعات</string>
<string name="manga_not_in_db">این مانگا از پایگاه داده حذف شده است.</string>
<string name="local_source_help_guide">راهنمای منبع محلی</string>
<string name="browse">مرور</string>
<string name="latest">جدیدترین</string>
<string name="action_global_search_hint">جست و جو در تمام منابع…</string>
<string name="added_to_library">مانگا به کتابخانه اضافه شد</string>
<string name="invalid_combination">حالت پیش فرض نمی‌تواند با بقیه دسته بندی ها همزمان انتخاب شود</string>
<string name="pinned_sources">منابع پین شده</string>
<string name="last_used_source">آخرین استفاده</string>
<string name="other_source">دیگر</string>
<string name="local_source">منبع محلی</string>
<string name="http_error_hint">وب سایت را در WebView بررسی کنید</string>
<string name="no_results_found">هیچ نتیجه ای یافت نشد</string>
<string name="no_more_results">نتیجه بیشتری یافت نشد</string>
<string name="no_valid_sources">لطفا حداقل یک منبع معتبر را فعال کنید</string>
<string name="select_source">یک منبع را انتخاب کنید</string>
<string name="source_requires_login">این منبع نیاز به ورود به سیستم دارد</string>
<string name="tabs_header">تب ها</string>
<string name="badges_header">تعداد</string>
<string name="also_delete_chapters">حذف فصل های دانلود شده نیز پاک شوند</string>
<string name="confirm_delete_manga">آیا مطمئنید که می خواهید مانگای انتخاب شده را حذف کنید ؟</string>
<string name="tracker_not_logged_in">وارد سیستم نشده اید: %1$s</string>
<string name="source_not_found_name">منبع یافت نشد: %1$s</string>
<string name="source_not_found">منبع یافت نشد</string>
<string name="pref_backup_slots">حداکثر تعداد نسخه‌های پشتیبان</string>
<string name="pref_backup_interval">فرکانس پشتیبان گیری</string>
<string name="pref_backup_service_category">پشتیبان گیری خودکار</string>
<string name="pref_backup_directory">محل پشتیبان گیری</string>
<string name="pref_restore_backup_summ">بازگرداندن کتابخانه از فایل پشتیبان</string>
<string name="pref_restore_backup">برگرداندن نسخه پشتیبان</string>
<string name="pref_create_backup_summ">می تواند برای بازگرداندن کتابخانه فعلی استفاده شود</string>
<string name="pref_create_backup">تهیه نسخه پشتیبان</string>
<string name="backup">پشتیبان گیری</string>
<string name="pref_search_pinned_sources_only">فقط افزونه‌های پین شده شامل شوند</string>
<string name="pref_enable_automatic_extension_updates">بررسی برای به افزونه‌های بروزرسانی شده</string>
<string name="services">خدمات</string>
<string name="pref_auto_update_manga_sync">بعد از خوانده شدن وضعیت فصل بروزرسانی شود</string>
<string name="pref_download_new_categories">دسته بندی هایی که در دانلود شامل می شوند</string>
<string name="pref_download_new">دانلود فصل‌های جدید</string>
<string name="fifth_to_last">پنجم به آخرین فصل خوانده شده</string>
<string name="fourth_to_last">چهارم به آخرین فصل خوانده شده</string>
<string name="third_to_last">سوم به آخرین فصل خوانده شده</string>
<string name="second_to_last">دوم به آخرین فصل خوانده شده</string>
<string name="last_read_chapter">آخرین فصل خوانده شده</string>
<string name="disabled">غیرفعال شده</string>
<string name="pref_remove_after_read">وقتی خوانده شد پاک شود</string>
<string name="pref_remove_after_marked_as_read">وقتی خوانده شده علامت گذاری شد، پاک شود</string>
<string name="pref_download_only_over_wifi">فقط از طریق Wi-Fi بارگیری شود</string>
<string name="webtoon_side_padding_25">۲۵٪</string>
<string name="webtoon_side_padding_20">۲۰٪</string>
<string name="webtoon_side_padding_15">۱۵٪</string>
<string name="webtoon_side_padding_10">۱۰٪</string>
<string name="webtoon_side_padding_0">هیچ</string>
<string name="pref_webtoon_side_padding">فاصله از کنار صفحه</string>
<string name="pref_category_reading">درحال خواندن</string>
<string name="pref_always_show_chapter_transition">همیشه انتقال فصل را نشان داده شود</string>
<string name="channel_backup_restore_progress">فرآیند</string>
<plurals name="notification_chapters_generic">
<item quantity="one">۱ فصل جدید پیدا شد</item>
<item quantity="other">%1$d فصل جدید پیدا شد</item>
</plurals>
<plurals name="notification_new_chapters_summary">
<item quantity="one">برای ۱ مانگا</item>
<item quantity="other">برای %d مانگا</item>
</plurals>
<string name="pref_category_for_this_series">برای این مجموعه</string>
<string name="decode_image_error">بازکردن عکس با خطا مواجه شد</string>
<string name="options">گزینه ها</string>
<string name="want_to_read">Want to read</string>
<string name="plan_to_read">Plan to read</string>
<string name="download_custom">سفارشی</string>
<string name="custom_dir">مکان سفارشی</string>
<string name="pref_download_directory">مکان دانلود</string>
<string name="scale_type_smart_fit">هم اندازه حالت هوشمند</string>
<string name="filter_mode_multiply">Multiply</string>
<string name="filter_mode_overlay">Overlay</string>
<string name="pref_true_color_summary">Reduces banding, but impacts performance</string>
<string name="pref_double_tap_anim_speed">Double tap animation speed</string>
<string name="ext_install">نصب</string>
<plurals name="num_categories">
<item quantity="one">%d دسته</item>
<item quantity="other">%d دسته</item>
</plurals>
<string name="action_bookmark">بوک مارک</string>
<string name="channel_backup_restore_complete">تکمیل شده</string>
<string name="channel_backup_restore">پشتیبان گیری و بازیابی</string>
<string name="channel_ext_updates">آپدیت افزونه‌ها</string>
<string name="channel_new_chapters">آپدیت فصل ها</string>
<string name="channel_downloader">سیستم دانلود</string>
<string name="channel_library">کتابخانه</string>
<string name="channel_common">عمومی</string>
<string name="download_notifier_download_paused">دانلود متوقف شد</string>
<string name="download_notifier_no_network">هیچ اتصال به شبکه در دسترس نیست</string>
<string name="download_notifier_text_only_wifi">اتصال Wi-Fi در دسترس نیست</string>
<string name="download_notifier_page_ready_error">یک صفحه لود نشده است</string>
<string name="download_notifier_page_error">یک صفحه در پوشه های سیستم وجود ندارد</string>
<string name="download_notifier_unknown_error">به دلیل خطای غیرمنتظره فصل دانلود نشد</string>
<string name="download_notifier_title_error">خطا</string>
<string name="download_notifier_downloader_title">سیستم دانلود</string>
<string name="information_webview_outdated">لطفا برنامه WebView را برای سازگاری بهتر بروز کنید</string>
<string name="information_webview_required">WebView برای Tachiyomi مورد نیاز است</string>
<string name="information_cloudflare_bypass_failure">عدم موفقیت در دور زدن Cloudflare</string>
<string name="color_filter_a_value">opacity</string>
<string name="color_filter_b_value">آبی</string>
<string name="color_filter_g_value">سبز</string>
<string name="color_filter_r_value">قرمز</string>
<string name="rotation_force_landscape">تحمیل حالت افقی</string>
<string name="rotation_force_portrait">تحمیل حالت عمودی</string>
<string name="rotation_lock">قفل</string>
<string name="rotation_free">آزاد</string>
<string name="pref_rotation_type">چرخش</string>
<string name="double_tap_anim_speed_fast">سریع</string>
<string name="double_tap_anim_speed_normal">عادی</string>
<string name="double_tap_anim_speed_0">بدون انیمیشن</string>
<string name="zoom_start_center">مرکز</string>
<string name="zoom_start_right">راست</string>
<string name="zoom_start_left">چپ</string>
<string name="zoom_start_automatic">خودکار</string>
<string name="pref_zoom_start">موقعیت شروع بزرگنمایی</string>
<string name="scale_type_original_size">اندازه اصلی</string>
<string name="scale_type_fit_height">هم اندازه با طول</string>
<string name="scale_type_fit_width">هم اندازه با عرض</string>
<string name="scale_type_stretch">کشیده</string>
<string name="scale_type_fit_screen">هم اندازه با صفحه نمایش</string>
<string name="pref_image_scale_type">نوع مقیاس</string>
<string name="pref_image_decoder">رمزگشای تصاویر</string>
<string name="pager_viewer">صفحه به صفحه</string>
<string name="vertical_plus_viewer">عمودی پیوسته</string>
<string name="vertical_viewer">عمودی</string>
<string name="right_to_left_viewer">راست به چپ</string>
<string name="left_to_right_viewer">چپ به راست</string>
<string name="default_viewer">پیش‌فرض</string>
<string name="pref_viewer_type">حالت پیش فرض خواندن</string>
<string name="black_background">سیاه</string>
<string name="gray_background">خاکستری</string>
<string name="white_background">سفید</string>
<string name="pref_reader_theme">رنگ پس زمینه</string>
<string name="pref_read_with_long_tap">منوی ضربه طولانی</string>
<string name="pref_read_with_tapping">ضربه زدن</string>
<string name="pref_read_with_volume_keys">دکمه‌های صدا</string>
<string name="pref_read_with_volume_keys_inverted">معکوس کردن دکمه‌های صدا</string>
<string name="pref_reader_navigation">نوار ناوبری Navigation</string>
<string name="pref_skip_filtered_chapters">پرش ازز فصل‌های فیلتر شده</string>
<string name="pref_skip_read_chapters">پرش از فصل‌هایی که خوانده شده اند</string>
<string name="pref_keep_screen_on">روشن نگه داشتن صفحه نمایش</string>
<string name="filter_mode_screen">صفحه نمایش</string>
<string name="filter_mode_default">پیش‌فرض</string>
<string name="pref_color_filter_mode">حالت blend فیلتر رنگ</string>
<string name="pref_custom_color_filter">استفاده از فیلتر رنگ اختصاصی</string>
<string name="pref_custom_brightness">استفاده از سیستم روشنایی اخصاصی</string>
<string name="pref_crop_borders">بریدن حاشیه‌ها</string>
<string name="pref_true_color">رنگ ۳۲ بیتی</string>
<string name="pref_show_reading_mode_summary">هنگام باز شدن کتابخوان ، حالت فعلی را به طور خلاصه نشان داده شود</string>
<string name="pref_show_reading_mode">نمایش حالت خواندن</string>
<string name="pref_show_page_number">نمایش شماره صفحه</string>
<string name="pref_page_transitions">انمیشن‌های تغییر صفحه</string>
<string name="pref_lock_orientation">جهت قفل</string>
<string name="pref_fullscreen">تمام صفحه</string>
<string name="ext_language_info">زبان: %1$s</string>
<string name="ext_version_info">نگارش %1$s</string>
<string name="unofficial_extension_message">این افزونه در لیست افزونه‌های رسمی تاچیومی نیست.</string>
<string name="obsolete_extension_message">این افزونه دیگر موجود نیست.</string>
<string name="untrusted_extension_message">این افزونه با یک گواهی نامطمئن امضا شده است و فعال نشده است.
\n
\nیک افزونه مخرب می تواند هر گونه طلاعات ورود به سیستم ذخیره شده تاچیومی را بخواند یا کدهای دلخواه(احتمالا خطرناک) را اجرا کند.
\n
\nبا اعتماد به این گواهی شما این خطرات را می‌پذیرید.</string>
<string name="untrusted_extension">افزونه نامطمئن</string>
<string name="ext_available">موجود</string>
<string name="ext_uninstall">حذف نصب</string>
<string name="ext_untrusted">نامطمئن</string>
<string name="ext_unofficial">غیررسمی</string>
<string name="ext_trust">مورد اعتماد</string>
<string name="ext_installed">نصب شده</string>
<string name="ext_installing">در حال نصب</string>
<string name="ext_downloading">در حال بارگیری</string>
<string name="ext_pending">در انتظار</string>
<string name="ext_obsolete">منسوخ شده</string>
<string name="ext_update">بروزرسانی</string>
<string name="ext_updates_pending">بروزرسانی‌های در انتظار</string>
<string name="all_lang">همه</string>
<string name="all">همه</string>
<string name="pref_library_update_categories">دسته بندی هایی که در به روزرسانی سراسری شامل می شوند</string>
<string name="default_category_summary">همیشه بپرس</string>
<string name="default_category">کاتالوگ پیشفرض</string>
<string name="pref_category_library_categories">کاتالوگ‌ها</string>
<string name="pref_library_update_error_notification">خطاهای بروزرسانی در اعلان ها نمایش داده شود</string>
<string name="pref_library_update_refresh_metadata_summary">وقتی کتابخانه بروزآوری می‌شود عکس روی جلد و اطلاعات مانگاها را نیز بروزآوری شود</string>
<string name="pref_library_update_refresh_metadata">فراداده فقط به صورت خودکار بروز شود</string>
<string name="pref_update_only_non_completed">فقط مانگاهایی آپدیت شوند که تمام نشده</string>
<string name="charging">درحال شارژ</string>
<string name="wifi">وای‌فای</string>
<string name="pref_library_update_restriction_summary">فقط وقتی بروز شود که شرایط برآورده می‌شوند</string>
<string name="pref_library_update_restriction">محدودیت‌های بروزرسانی کتابخانه</string>
<string name="pref_library_update_prioritization">ترتیب بروزرسانی کتابخانه</string>
<string name="update_monthly">ماهانه</string>
<string name="update_weekly">هفتگی</string>
<string name="update_48hour">هر ۴۸ ساعت یکبار</string>
<string name="update_3hour">هر ۳ ساعت یکبار</string>
<string name="update_2hour">هر ۲ ساعت یکبار</string>
<string name="update_24hour">هر ۲۴ ساعت یکبار</string>
<string name="update_6hour">هر ۶ ساعت یکبار</string>
<string name="update_1hour">یک ساعت یکبار</string>
<string name="update_never">فقط دستی</string>
<string name="pref_library_update_interval">بروزرسانی کتابخانه چند وقت یکبار</string>
<string name="pref_category_library_update">بروزرسانی‌ها</string>
<string name="default_columns">پیش‌فرض</string>
<string name="landscape">افقی</string>
<string name="portrait">عمودی</string>
<string name="pref_library_columns">موارد در هر ردیف</string>
<string name="pref_category_display">نمایش</string>
<string name="hide_notification_content">مخفی کردن ناتیفیکشن‌ها</string>
<string name="secure_screen_summary">محتویات برنامه هنگام تعویض برنامه ها مخفی و از عکس گرفتن از صفحه برنامه جلوگیری شود</string>
<string name="secure_screen">صفحه نمایش امن</string>
<plurals name="lock_after_mins">
<item quantity="one">بعد از 1 دقیقه</item>
<item quantity="other">بعد از %1$s دقیقه</item>
</plurals>
<string name="lock_never">هرگز</string>
<string name="lock_always">همیشه</string>
<string name="lock_when_idle">قفل صفحه هنگام بیکاری</string>
<string name="lock_with_biometrics">با بیومتریک قفل کنید</string>
<string name="pref_category_security">امنیت</string>
<string name="pref_manage_notifications">مدیریت ناتیفیکیشن‌ها</string>
<string name="pref_confirm_exit">هنگام خروج از برنامه تایید گرفته شود</string>
<string name="pref_date_format">فرمت تاریخ</string>
<string name="system_default">پیش‌فرض سیستم</string>
<string name="pref_language">انتخاب زبان</string>
<string name="pref_start_screen">صفحه اغازین</string>
<string name="theme_dark_amoled">AMOLED سیاه</string>
<string name="theme_dark_blue">تم آبی تیره</string>
<string name="theme_dark_default">پیش‌فرض</string>
<string name="pref_theme_dark">تم تیره</string>
<string name="theme_light_blue">تم آبی کمرنگ</string>
<string name="theme_light_default">پیش‌فرض</string>
<string name="pref_theme_light">تم روشن</string>
<string name="theme_dark">روشن</string>
<string name="theme_light">خاموش</string>
<string name="theme_system">تم پیش‌فرض سیستم</string>
<string name="pref_theme_mode">تم تیره</string>
<string name="pref_category_about">درباره</string>
<string name="pref_category_advanced">پیشرفته</string>
<string name="pref_category_tracking">ردیابی</string>
<string name="pref_category_downloads">بارگیری ها</string>
<string name="pref_category_reader">کتابخوان</string>
<string name="pref_category_library">کتابخانه</string>
<string name="pref_category_general">عمومی</string>
<string name="short_recent_updates">بروزرسانی‌ها</string>
<string name="app_not_available">برنامه موجود نیست</string>
<string name="loading">درحال بارگیری…</string>
<string name="action_webview_refresh">بازآوری‌</string>
<string name="action_webview_forward">رو به جلو</string>
<string name="action_webview_back">بازگشت</string>
<string name="action_login">وارد شدن</string>
<string name="action_open">بازکردن</string>
<string name="action_restore">بازیابی</string>
<string name="action_create">ایجاد کردن</string>
<string name="action_open_log">بازکردن log</string>
<string name="action_undo">خنثی کردن (Undo)</string>
<string name="action_reset">از اول</string>
<string name="action_save">ذخيره</string>
<string name="action_share">اشتراک گذاری</string>
<string name="action_install">نصب</string>
<string name="action_move_to_bottom">جابه‌جایی به پایین</string>
<string name="action_move_to_top">جابه‌جایی به بالا</string>
<string name="action_oldest">قدیمی ترین</string>
<string name="action_newest">جدیدترین</string>
<string name="action_reorganize_by">دوباره مرتب کردن</string>
<string name="action_sort">مرتب سازی</string>
<string name="action_cancel_all">لغو همه</string>
<string name="action_cancel">لغو</string>
<string name="action_unpin">برداشتن پین</string>
<string name="action_pin">پین</string>
<string name="action_display_show_tabs">نمایش برگه های دسته بندی</string>
<string name="action_display_unread_badge">تعداد خوانده نشده</string>
<string name="action_display_download_badge">تعداد دانلود شده</string>
<string name="action_display_comfortable_grid">چشم نواز</string>
<string name="action_display_list">لیست</string>
<string name="action_display_grid">فشرده</string>
<string name="action_display">نمایش</string>
<string name="action_display_mode">حالت نمایش</string>
<string name="action_migrate">تغییر منبع</string>
<string name="action_open_in_web_view">در WebView باز کنید</string>
<string name="action_open_in_browser">باز کردن در مرورگر</string>
<string name="action_move">انتقال</string>
<string name="action_resume">ازسرگیری</string>
<string name="action_start">آغاز</string>
<string name="action_remove">حذف</string>
<string name="action_retry">تلاش دوباره</string>
<string name="action_next_chapter">فصل بعد</string>
<string name="action_previous_chapter">فصل قبل</string>
<string name="action_close">بستن</string>
<string name="action_pause">مکث</string>
<string name="action_stop">متوقف کردن</string>
<string name="action_view_chapters">مشاهده فصل ها</string>
<string name="action_next_unread">خوانده نشده بعدی</string>
<string name="action_show_downloaded">دانلود شده</string>
<string name="local_source_badge">محلی</string>
<string name="updating_category">درحال به روز رسانی دسته بندی ها</string>
<string name="library_search_hint">عنوان یا نویسنده…</string>
<string name="unknown_error">خطای ناشناخته</string>
<string name="invalid_login">ورود به سیستم ناموفق بود</string>
<string name="logout_success">شما خارج شدید</string>
<string name="logout">خروج</string>
<string name="logout_title">از%1$s خارج می شوید؟</string>
<string name="login_success">وارد شدید</string>
<string name="login">ورود</string>
<string name="show_password">نمایش گذرواژه</string>
<string name="password">گذرواژه</string>
<string name="email">آدرس ایمیل</string>
<string name="username">نام کاربری</string>
<string name="login_title">ورود به %1$s</string>
<plurals name="download_queue_summary">
<item quantity="one">۱ عدد باقی مانده</item>
<item quantity="other">%1$s عدد باقی مانده</item>
</plurals>
<string name="downloaded_only_summary">همه مانگاهای موجود در کتابخانه را فیلتر می کند</string>
<string name="label_downloaded_only">فقط دانلود شده ها</string>
<string name="pref_acra_summary">به رفع اشکالات برنامه کمک می کند. هیچ گونه داده حساسی ارسال نخواهد شد</string>
<string name="pref_enable_acra">ارسال گزارش های خرابی</string>
<string name="check_for_updates">بررسی برای به روز رسانی</string>
<string name="licenses">مجوزهای منبع باز</string>
<string name="notices">اطلاعیه های نسخه پیش نمایش</string>
<string name="changelog">لیست تغییرات برنامه</string>
<string name="build_time">زمان ساخت</string>
<string name="version">نسخهٔ</string>
<string name="website">وب سایت</string>
<string name="battery_optimization_setting_activity_not_found">تنظیمات دستگاه باز نشد</string>
<string name="battery_optimization_disabled">بهینه سازی باتری از قبل غیرفعال شده است</string>
<string name="pref_disable_battery_optimization_summary">به بروزرسانی ها و پشتیبان گیری از کتابخانه در حالت پس زمینه کمک می کند</string>
<string name="pref_disable_battery_optimization">غیر فعال کردن بهینه سازی باتری</string>
<string name="pref_refresh_library_covers">تازه کردن عکس روی جلد مانگاها</string>
<string name="clear_database_completed">اطلاعات حذف شدند</string>
<string name="clear_database_confirmation">آیا مطمئن هستید؟ اطلاعات فصل های خوانده شده برای مانگاهایی که در کتابخانه اضافه نشده از دست خواهد رفت</string>
<string name="pref_clear_database_summary">حذف سابقه برای مانگاهایی که در کتابخانه ذخیره نشده اند</string>
<string name="pref_clear_database">پاک کردن پایگاه داده</string>
<string name="choices_reset">تنظیمات دیالوگ تنظیم مجدد شود</string>
<string name="cookies_cleared">کوکی ها پاک شدند</string>
<string name="pref_clear_cookies">پاک کردن کوکی ها</string>
<string name="cache_delete_error">هنگام پاک کردن حافظه نهان خطایی روی داد</string>
<string name="cache_deleted">حافظه نهان پاک شد. %1$d فایل پاک شدند</string>
<string name="used_cache">مصرف شده: %1$s</string>
<string name="pref_clear_chapter_cache">پاک کردن فصل حافظه پنهان فصل‌ها</string>
<string name="label_data">اطلاعات</string>
<string name="restoring_backup_canceled">بازیابی لغو شده</string>
<string name="restoring_backup_error">بازیابی نسخه پشتیبان ناموفق بود</string>
<string name="restoring_backup">درحال بازگرداندن نسخه پشتیبان</string>
<string name="restore_in_progress">بازیابی هنوز در حال انجام است</string>
<string name="creating_backup_error">پشتیبان گیری ناموفق بود</string>
<string name="creating_backup">درحال ایجاد نسخه پشتیبان</string>
<string name="backup_choice">چه چیزی را می خواهید پشتیبان گیری کنید ؟</string>
<string name="backup_in_progress">پشتیبان گیری هنوز در حال انجام است</string>
<plurals name="restore_completed_message">
<item quantity="one">در %1$s با %2$s خطا انجام شد</item>
<item quantity="other">در %1$s با %2$s خطا انجام شد</item>
</plurals>
<string name="restore_duration">%02d دقیقه, %02d ثانیه</string>
<string name="restore_completed">بازیابی کامل شد</string>
<string name="backup_restore_content">بازیابی برای واکشی داده ها از منابع نصب شده استفاده می‌کند، ممکن است از حجم اینتنرت مصرف شود.
\n
\nاطمینان حاصل کنید که تمام افزونه‌های لازم را نصب کرده اید و قبل از بازگردانی به منابع نصب شده و خدمات ردیابی وارد شده اید.</string>
<string name="backup_restore_missing_sources">منابع ناموجود:</string>
<string name="invalid_backup_file_missing_manga">فایل پشتیبان شامل هیچ مانگا ای نیست.</string>
<string name="invalid_backup_file_missing_data">فایل شامل اطلاعات ناموجود است.</string>
<string name="invalid_backup_file">فایل پشتیبان نامعتبر است</string>
<string name="backup_created">نسخه پشتیبان ایجاد شد</string>
<string name="action_sort_down">مرتب کردن</string>
<string name="action_sort_up">مرتب کردن</string>
<string name="action_edit_cover">ویرایش عکس روی جلد</string>
<string name="action_rename_category">تغییر نام دسته</string>
<string name="action_edit_categories">ویرایش دسته ها</string>
<string name="action_add_category">افزودن دسته</string>
<string name="action_add">افزودن</string>
<string name="action_edit">ویرایش</string>
<string name="action_disable_all">غیر فعال کردن همه</string>
<string name="action_enable_all">فعال کردن همه</string>
<string name="action_update_library">به روز رسانی کتابخانه</string>
<string name="action_update">بروز رسانی</string>
<string name="action_delete">پاک کردن</string>
<string name="action_remove_bookmark">حذف نشان</string>
<string name="action_download">دانلود شود</string>
<string name="action_mark_previous_as_read">علامت گذاری قبلی‌ها به عنوان خوانده شده</string>
<string name="action_mark_as_unread">علامت گذاری به عنوان خوانده نشده</string>
<string name="action_mark_as_read">علامت گذاری به عنوان خوانده شده</string>
<string name="action_select_inverse">معکوس کردن انتخاب شده</string>
<string name="action_select_all">انتخاب همه</string>
<string name="action_global_search">جست و جوی کامل</string>
<string name="action_search">جست و جو</string>
<string name="action_sort_latest_chapter">اخرین فصل</string>
<string name="action_sort_last_checked">اخیرا دیده شده</string>
<string name="action_sort_last_read">آخرین خوانده ‌شده</string>
<string name="action_sort_total">کل فصل‌ها</string>
<string name="action_sort_alpha">الفبا</string>
<string name="action_filter_empty">حذف فیلتر</string>
<string name="action_filter_read">خوانده شده</string>
<string name="action_filter_unread">خوانده ‌نشده</string>
<string name="action_filter_bookmarked">بوک مارک شده</string>
<string name="action_filter_downloaded">دانلود شده</string>
<string name="action_filter">فیلتر</string>
<string name="action_menu">منو</string>
<string name="action_settings">تنضیمات</string>
<string name="confirm_exit">برای بستن دکمه بازگشت را یک بار دیگر بزنید</string>
<string name="history">تاریخچه</string>
<string name="chapters">فصل‌ها</string>
<string name="manga">مانگا</string>
<string name="categories">کاتالوگ‌ها</string>
</resources>

View File

@ -98,7 +98,6 @@
<string name="dialog_with_checkbox_remove_description">פעולה זו תסיר את התאריך הקריאה של פרק זה. האם אתה בטוח\?</string>
<string name="snack_categories_deleted">הקטגוריות נמחקו</string>
<string name="error_category_exists">קטגוריה עם שם זה כבר קיימת!</string>
<string name="url_not_set">כתובת האתר של המנגה לא הוגדרה, אנא לחץ על כותרת ובחר שוב מאנגה</string>
<string name="track_author">מחבר</string>
<string name="track_type">סוג</string>
<string name="track_start_date">הותחל</string>
@ -139,9 +138,6 @@
<string name="snack_add_to_library">להוסיף מאנגה לספרייה\?</string>
<string name="source_not_installed">המקור לא הותקן: %1$s</string>
<string name="delete_downloads_for_manga">למחוק פרקים שהורדו\?</string>
<string name="manga_info_genres_label">סגנון</string>
<string name="manga_info_source_label">מקור</string>
<string name="manga_info_chapters_label">פרקים</string>
<string name="manga_added_library">נוסף לספרייה</string>
<string name="remove_from_library">הסר מהספרייה</string>
<string name="manga_removed_library">הוסר מהספרייה</string>
@ -279,7 +275,6 @@
<string name="track">מעקב</string>
<string name="label_more">עוד</string>
<string name="action_cancel">ביטול</string>
<string name="action_hide">להסתיר</string>
<string name="action_display_download_badge">להוריד תגים</string>
<string name="action_display_list">רשימה</string>
<string name="action_display_grid">רשת</string>
@ -456,4 +451,4 @@
<string name="manga">מנגה</string>
<string name="categories">קטגוריות</string>
<string name="name">שם</string>
</resources>
</resources>

View File

@ -181,6 +181,38 @@
<string name="action_webview_back">Vissza</string>
<string name="action_oldest">Legrégebbi</string>
<string name="action_newest">Legújabb</string>
<string name="action_hide">Elrejt</string>
<string name="action_sort_latest_chapter">Legújabb fejezet</string>
<string name="action_display_download_badge">Letöltésszámláló</string>
<string name="action_migrate">Áttelepítés</string>
<string name="action_start">Kezdés</string>
<string name="action_view_chapters">Fejezetek megtekintése</string>
<string name="action_disable_all">Összes kikapcsolása</string>
<string name="action_enable_all">Összes engedélyezése</string>
<string name="action_select_inverse">Kiválasztás megfordítása</string>
<string name="action_sort_last_checked">Utoljára ellenőrizve</string>
<string name="action_menu">Menü</string>
<string name="confirm_exit">A befejezéshez nyomd meg újfent a Vissza gombot!</string>
<string name="unlock_app">Tachiyomi feloldása</string>
<string name="information_empty_library">A könyvtárad üres. Adj hozzá új sorozatokat a Böngészőből.</string>
<string name="information_no_recent">Nincs új frissítés</string>
<string name="information_no_downloads">Nincs letöltés folyamatban</string>
<string name="channel_backup_restore_complete">Teljesítve</string>
<string name="channel_backup_restore">Biztonsági mentés és visszaállítás</string>
<string name="channel_ext_updates">Bővítményfrissítések</string>
<string name="channel_new_chapters">Fejezetfrissítések</string>
<string name="channel_library">Könyvtár</string>
<string name="channel_common">Gyakori</string>
<string name="download_notifier_download_paused">Letöltés szüneteltetve</string>
<string name="download_notifier_text_only_wifi">Nincs Wi-Fi kapcsolat</string>
<string name="download_notifier_page_error">Egy oldal hiányzik a könyvtárból</string>
<string name="download_notifier_unknown_error">Egy váratlan hiba miatt nem sikerült letölteni a fejezetet</string>
<string name="download_notifier_title_error">Hiba</string>
<string name="information_webview_outdated">A jobb kompatibilitás érdekében kérlek, frissítsd a WebView alkalmazást!</string>
<plurals name="update_check_notification_ext_updates">
<item quantity="one">Egy új bővítményfrissítés érhető el</item>
<item quantity="other">%d bővítményfrissítés érhető el</item>
</plurals>
<string name="update_check_notification_update_available">Új verzió érhető el!</string>
<string name="update_check_notification_download_error">Letöltési hiba</string>
<string name="update_check_notification_download_complete">Letöltés kész</string>
</resources>

View File

@ -191,10 +191,6 @@
<string name="unknown">Sconosciuto</string>
<string name="licensed">Concesso in licenza</string>
<string name="remove_from_library">Rimuovi dalla libreria</string>
<string name="manga_info_chapters_label">Capitoli</string>
<string name="manga_info_status_label">Stato</string>
<string name="manga_info_source_label">Fonte</string>
<string name="manga_info_genres_label">Generi</string>
<!-- Manga chapters fragment -->
<string name="manga_chapters_tab">Capitoli</string>
<string name="manga_chapter_no_title">Senza titolo</string>
@ -279,7 +275,6 @@
<string name="update_check_notification_download_error">Errore di download</string>
<string name="update_check_notification_update_available">Aggiornamento disponibile!</string>
<!--Content Description-->
<string name="description_backdrop">Immagine di sfondo del manga</string>
<string name="description_cover">Copertina del manga</string>
<!-- Information Text -->
<string name="information_no_downloads">Nessun download</string>
@ -396,7 +391,6 @@
<string name="track_start_date">Iniziato</string>
<string name="track_type">Tipo</string>
<string name="track_author">Autore</string>
<string name="url_not_set">L\'URL del manga non è impostato, clicca sul titolo e selezionalo ancora</string>
<string name="snack_categories_deleted">Categorie rimosse</string>
<string name="migration_info">Tocca per selezionare la sorgente dalla quale migrare</string>
<string name="migration_dialog_what_to_include">Seleziona i dati da includere</string>
@ -477,7 +471,6 @@
<string name="secure_screen_summary">Nascondi i contenuti dell\'app quando cambi applicazione e blocca gli screenshot</string>
<string name="pref_category_display">Visualizzazione</string>
<string name="action_reorganize_by">Riordina</string>
<string name="action_hide">Nascondi</string>
<string name="channel_ext_updates">Aggiornamenti estensione</string>
<plurals name="update_check_notification_ext_updates">
<item quantity="one">Aggiornamento estensione disponibile</item>
@ -559,8 +552,6 @@
<string name="information_webview_required">È necessaria l\'app WebView per Tachiyomi</string>
<string name="viewer">Modalità di lettura</string>
<string name="pref_category_for_this_series">Per questa serie</string>
<string name="manga_info_latest_data_label">Aggiornato</string>
<string name="manga_info_last_chapter_label">Ultimo capitolo</string>
<string name="local_source_help_guide">Guida alle fonti locali</string>
<string name="last_used_source">Ultimo usato</string>
<plurals name="download_queue_summary">
@ -610,5 +601,4 @@
<string name="action_migrate">Trasferire</string>
<string name="action_disable_all">Disabilita tutto</string>
<string name="action_enable_all">Abilita tutto</string>
<string name="enabled">Abilitato</string>
</resources>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Tachiyomi</string>
<string name="name">სახელი</string>
<string name="categories">კატეგორიები</string>
<string name="manga">მანგა</string>
@ -65,7 +66,6 @@
<string name="action_display_download_badge">გადმოწერის ნიშნაკი</string>
<string name="action_display_unread_badge">წაუკითხავების ნიშნაკი</string>
<string name="action_display_show_tabs">კატეგორიის ჩანართების ჩვენება</string>
<string name="action_hide">დამალვა</string>
<string name="action_cancel">გააუქმე</string>
<string name="action_cancel_all">გააუქმე ყველა</string>
<string name="action_sort">დალაგება</string>
@ -150,7 +150,6 @@
<string name="all">ყველა</string>
<string name="all_lang">ყველა</string>
<string name="pref_library_update_categories">კატეგორიები გლობალურ განახლებაში შესაყვანად</string>
<string name="ext_details">დეტალები</string>
<string name="ext_update">განაცლება</string>
<string name="ext_install">დაინსტალირება</string>
<string name="ext_installing">ინსტალირდება</string>
@ -163,7 +162,6 @@
<string name="ext_version_info">ვერსია: %1$s</string>
<string name="ext_language_info">ენა: %1$s</string>
<string name="pref_fullscreen">მთლიან ეკრანზე</string>
<string name="enabled">ჩართული</string>
<string name="pref_cutout_short">კონტენტის ჩვენება ჩამოჭრილ ადგილზე</string>
<string name="pref_lock_orientation">ორიენტაციის დაფიქსირება</string>
<string name="pref_page_transitions">გვერდის გადასვლების ანიმირება</string>
@ -339,14 +337,6 @@
<string name="manga_info_full_title_label">სათაური</string>
<string name="manga_added_library">დამატებულია ბიბლიოთეკაში</string>
<string name="manga_removed_library">წაშლილია ბიბლიოთეკიდან</string>
<string name="manga_info_author_label">ავტორი</string>
<string name="manga_info_artist_label">არტისტი</string>
<string name="manga_info_chapters_label">თავები</string>
<string name="manga_info_last_chapter_label">ბოლო თავი</string>
<string name="manga_info_latest_data_label">განახლებული</string>
<string name="manga_info_status_label">სტატუსი</string>
<string name="manga_info_source_label">წყარო</string>
<string name="manga_info_genres_label">ჟანრები</string>
<string name="manga_info_expand">მეტი ინფორმაციის ჩვენება</string>
<string name="manga_info_collapse">ნაკლები ინფორმაციის ჩვენება</string>
<string name="delete_downloads_for_manga">გადმოწერილი თავების წაშლა</string>
@ -400,7 +390,6 @@
<string name="track_type">ტიპი</string>
<string name="track_author">ავტორი</string>
<string name="error_invalid_date_supplied">არასწორი თარიღი</string>
<string name="url_not_set">მანგას URL არ არის დასეტილი, გთხოვთ შეამოწმოთ სათაური და აირჩიეთ მანგა ისევ</string>
<string name="error_category_exists">კატეგორია მოცემული სახელით უკვე არსებობს!</string>
<string name="snack_categories_deleted">კატეგორიები წაიშალა</string>
<string name="dialog_with_checkbox_remove_description">ეს წაშლის ამ თავის წაკითხვის თარიღს. დარწმუნებული ხართ?</string>
@ -536,10 +525,6 @@
<string name="backup_restore_content">აღდგენა იყენებს წყაროებს მონაცემების გადმოსაწერად.\\n\\nდარწმუნდი რომ გაქვს დაინსტალირებული ყველა საჭირო დამატება და ხარ დალოგინებული წყაროებში და თვალყურის სადევნებელ სერვისებში ააღდგენამდე.</string>
<string name="pref_refresh_library_tracking_summary">ანახლებს სტატუსს, შეფასებას და ბოლო თავს წაკითხულს თვალყურის სადევნებელ სერვისებიდან</string>
<string name="download_notifier_page_ready_error">გვერდი ვერ ჩაიტვირთა</string>
<string name="select">ამორჩევა</string>
<string name="ext_empty_preferences">ამ გაფართოებას არ აქვს პარამეტრები</string>
<string name="ext_preferences">პარამეტრები</string>
<string name="filter_mode_overlay">გადაფარება</string>
<string name="description_backdrop">მანგის ფონური ილუსტრაცია</string>
<string name="information_cloudflare_bypass_failure">ვერ მოხერხდა CloudFlare-ს შემოვლა</string>
</resources>

View File

@ -219,9 +219,6 @@
<string name="manga_info_full_title_label">제목</string>
<string name="manga_added_library">서제에 추가됨</string>
<string name="manga_removed_library">서재에서 제거됨</string>
<string name="manga_info_chapters_label">챕터</string>
<string name="manga_info_status_label">상태</string>
<string name="manga_info_genres_label">장르</string>
<string name="delete_downloads_for_manga">다운로드된 챕터를 삭제합니까\?</string>
<string name="source_not_installed">소스 미설치: %1$s</string>
<string name="manga_chapters_tab">챕터</string>
@ -346,7 +343,6 @@
<string name="browse">찾아보기</string>
<string name="manga_not_in_db">이 만화는 데이터베이스에서 제거되었습니다!</string>
<string name="licensed">판권작</string>
<string name="manga_info_source_label">소스</string>
<string name="copied_to_clipboard">클립보드에 %1$s 복사됨</string>
<string name="manga_chapter_no_title">제목 없음</string>
<string name="chapter_queued">대기중</string>
@ -362,7 +358,6 @@
<string name="repeating">다시 읽는 중</string>
<string name="track_start_date">시작 일자</string>
<string name="track_type">종류</string>
<string name="url_not_set">만화 URL이 설정 되지 않았습니다. 제목을 클릭하고 만화를 다시 선택해주세요</string>
<string name="error_category_exists">같은 이름을 가진 카테고리가 이미 존재합니다!</string>
<string name="page_downloaded">페이지가 %1$s로 복사됨</string>
<string name="migration_info">탭하여 이동할 소스 선택</string>
@ -371,7 +366,6 @@
<string name="copy">복사</string>
<string name="download_queue_error">챕터 다운로드 도중 오류가 발생했습니다. 다운로드 섹션에서 다시시도 할 수 있습니다</string>
<string name="notification_first_add_to_library">이 행동을 하기 전 서재에 만화를 추가해주세요</string>
<string name="description_backdrop">만화의 배경 이미지</string>
<string name="information_empty_library">서재가 비어있습니다. 카탈로그에서 만화 시리즈를 서재에 추가 할 수 있습니다.</string>
<string name="information_empty_category">카테고리가 없습니다. 서재 구성을 위해 + 버튼을 눌러 생성하세요.</string>
<string name="download_notifier_page_error">페이지가 폴더에서 누락됨</string>

View File

@ -67,7 +67,6 @@
<string name="action_cancel">रद्द करा</string>
<string name="action_unpin">पिन काढा</string>
<string name="action_pin">पिन</string>
<string name="action_hide">लपवा</string>
<string name="action_display_unread_badge">न वाचलेले बॅज</string>
<string name="action_display_download_badge">Download बॅज</string>
<string name="action_display_list">यादी</string>

View File

@ -255,10 +255,6 @@
<string name="manga_info_full_title_label">Tittel</string>
<string name="manga_added_library">Lagt til i biblioteket</string>
<string name="manga_removed_library">Fjernet fra bibliotek</string>
<string name="manga_info_chapters_label">Kapittel</string>
<string name="manga_info_status_label">Status</string>
<string name="manga_info_source_label">Kilde</string>
<string name="manga_info_genres_label">Sjangere</string>
<string name="delete_downloads_for_manga">Slett nedlastede kapittel\?</string>
<string name="copied_to_clipboard">%1$s kopiert til utklippstavle</string>
<string name="source_not_installed">Kilde ikke installert: %1$s</string>
@ -300,7 +296,6 @@
<string name="track_start_date">Startet</string>
<string name="track_type">Type</string>
<string name="track_author">Forfatter</string>
<string name="url_not_set">Manga-nettadresse ikke satt, klikk tittel og velg manga igjen</string>
<string name="error_category_exists">En kategori ved dette navnet finnes allerede.</string>
<string name="snack_categories_deleted">Kategorier slettet</string>
<string name="dialog_with_checkbox_remove_description">Lesningsdato for dette kapittelet vil bli fjernet. Er du sikker\?</string>
@ -372,7 +367,6 @@
<string name="update_check_notification_download_complete">Nedlasting ferdig</string>
<string name="update_check_notification_download_error">Feil ved nedlasting</string>
<string name="update_check_notification_update_available">Ny versjon tilgjengelig!</string>
<string name="description_backdrop">Bakgrunnsbilde av manga</string>
<string name="description_cover">Omslag til manga</string>
<string name="information_no_downloads">Ingen nedlastinger</string>
<string name="information_no_recent">Ingen nylige oppdateringer</string>
@ -436,7 +430,6 @@
<string name="channel_new_chapters">Kapitteloppdateringer</string>
<string name="unlock_app">Lås opp Tachiyomi</string>
<string name="notification_chapters_single">Kapittel %1$s</string>
<string name="action_hide">Skjul</string>
<string name="email">E-postadresse</string>
<string name="in_library">I bibliotek</string>
<string name="add_to_library">Legg til i bibliotek</string>

View File

@ -282,10 +282,6 @@
<string name="manga_info_full_title_label">Názov</string>
<string name="manga_added_library">Pridané do knižnice</string>
<string name="manga_removed_library">Odstránené z knižnice</string>
<string name="manga_info_chapters_label">Kapitoly</string>
<string name="manga_info_status_label">Stav</string>
<string name="manga_info_source_label">Zdroj</string>
<string name="manga_info_genres_label">Žánre</string>
<string name="delete_downloads_for_manga">Odstrániť stiahnuté kapitoly\?</string>
<string name="copied_to_clipboard">%1$s bol skopírovaný do schránky</string>
<string name="source_not_installed">Zdroj nie je nainštalovaný</string>
@ -327,7 +323,6 @@
<string name="track_start_date">Zahájeno</string>
<string name="track_type">Typ</string>
<string name="track_author">Autor</string>
<string name="url_not_set">URL mangy nie je nastavené, kliknite na názov a znova vyberte mangu</string>
<string name="error_category_exists">Kategória s týmto názvom už existuje!</string>
<string name="snack_categories_deleted">Kategórie boli odstránené</string>
<string name="dialog_with_checkbox_remove_description">Týmto sa odstráni dátum prečítania tejto kapitoly. Si si istý\?</string>
@ -381,7 +376,6 @@
<string name="update_check_notification_download_complete">Sťahovanie dokončené</string>
<string name="update_check_notification_download_error">Chyba sťahovania</string>
<string name="update_check_notification_update_available">Dostupná aktualizácia</string>
<string name="description_backdrop">Obrázok pozadí mangy</string>
<string name="description_cover">Obal mangy</string>
<string name="information_no_downloads">Žiadne sťahovanie</string>
<string name="information_no_recent">Žiadne nedávne kapitoly</string>
@ -406,7 +400,6 @@
<string name="action_view_chapters">Zobraziť kapitoly</string>
<string name="action_cancel_all">Zrušiť všetky</string>
<string name="unlock_app">Odomknúť Tachiyomi</string>
<string name="action_hide">Skryť</string>
<string name="action_menu">Menu</string>
<string name="action_reorganize_by">Zmeniť poradie</string>
<string name="action_newest">Najnovšie</string>

View File

@ -263,10 +263,6 @@
<string name="manga_info_full_title_label">Ime</string>
<string name="manga_added_library">Dodato u biblioteku</string>
<string name="manga_removed_library">Uklonjeno iz biblioteke</string>
<string name="manga_info_chapters_label">Poglavlja</string>
<string name="manga_info_status_label">Status</string>
<string name="manga_info_source_label">Izvor</string>
<string name="manga_info_genres_label">Žanr</string>
<string name="delete_downloads_for_manga">Obriši preuzeta poglavlja\?</string>
<string name="copied_to_clipboard">Kopirano
\n%1$s</string>
@ -341,7 +337,6 @@
<string name="ext_updates_pending">Apdejti koji čekaju</string>
<string name="unlock_app">Otključaj Tachiyomi</string>
<string name="pref_category_display">Prikaži</string>
<string name="action_hide">Sakri</string>
<string name="hide_notification_content">Sakri notifikacije</string>
<string name="pref_disable_battery_optimization">Isključi optimizaciju korišćenja baterije</string>
<string name="battery_optimization_disabled">Optimizacija potročnje baterije je već isključena</string>
@ -422,7 +417,6 @@
<string name="dialog_with_checkbox_remove_description">Ovo će ukloniti datum čitanja ovog poglavlja. Da li ste sigurni\?</string>
<string name="snack_categories_deleted">Kategorija obrisana</string>
<string name="error_category_exists">Kategorija sa ovim imenom već postoji!</string>
<string name="url_not_set">Manga URL nije postavljen, kliknite na naslov i ponovo odaberite mangu</string>
<string name="error_invalid_date_supplied">Netačni datum je unet</string>
<string name="track_author">Autor</string>
<string name="track_type">Tip</string>
@ -448,8 +442,6 @@
<string name="manga_info_collapse">Prikazi manje informacija</string>
<string name="manga_info_expand">Prikazi više informacija</string>
<string name="manga_info_about_label">Info</string>
<string name="manga_info_latest_data_label">Ažurirano</string>
<string name="manga_info_last_chapter_label">Zadnje poglavlje</string>
<string name="in_library">U biblioteci</string>
<string name="add_to_library">Dodaj u biblioteku</string>
<string name="local_source_help_guide">Vodič za lokalni izvor</string>
@ -486,7 +478,6 @@
<string name="information_webview_required">WebView je potreban za Tachiyomi</string>
<string name="information_cloudflare_bypass_failure">Neuspelo zaobilaženje Cloudflare</string>
<string name="description_cover">Naslovna slika mange</string>
<string name="description_backdrop">Pozadinska slika mange</string>
<plurals name="update_check_notification_ext_updates">
<item quantity="one">Dostupno je ažuriranje proširenja</item>
<item quantity="few">%d dostupnih ispravki za proširenje</item>

View File

@ -262,10 +262,6 @@
<string name="manga_info_full_title_label">ชื่อเรื่อง</string>
<string name="manga_added_library">เพิ่มไปยังห้องสมุด</string>
<string name="manga_removed_library">ลบออกจากห้องสมุด</string>
<string name="manga_info_chapters_label">บท</string>
<string name="manga_info_status_label">สถานะ</string>
<string name="manga_info_source_label">แหล่ง</string>
<string name="manga_info_genres_label">ชนิด</string>
<string name="delete_downloads_for_manga">ลบบทที่ดาวน์โหลดมาหรือไม่\?</string>
<string name="copied_to_clipboard">%1$s ถูกคัดลอกไปยังคลิปบอร์ด</string>
<string name="source_not_installed">ไม่ได้ติดตั้ง: %1$s</string>
@ -319,7 +315,6 @@
<string name="track_start_date">เริ่มต้นแล้ว</string>
<string name="track_type">ชนิด</string>
<string name="track_author">ผู้เขียน</string>
<string name="url_not_set">ไม่ได้ตั้งค่า URL ของมังงะ โปรดคลิกที่ชื่อและเลือกมังงะอีกครั้ง</string>
<string name="error_category_exists">หมวดหมู่ที่มีชื่อนี้มีอยู่แล้ว!</string>
<string name="snack_categories_deleted">หมวดหมู่ถูกลบ</string>
<string name="dialog_with_checkbox_remove_description">สิ่งนี้จะลบวันที่อ่านของบทนี้ คุณแน่ใจไหม\?</string>
@ -373,7 +368,6 @@
<string name="update_check_notification_download_complete">ดาวน์โหลดเสร็จสิ้น</string>
<string name="update_check_notification_download_error">ดาวน์โหลดเกิดข้อผิดพลาด</string>
<string name="update_check_notification_update_available">อัปเดตที่มีอยู่</string>
<string name="description_backdrop">ภาพฉากหลังของมังงะ</string>
<string name="description_cover">ปกของมังงะ</string>
<string name="information_no_downloads">ไม่มีการดาวน์โหลด</string>
<string name="information_no_recent">ไม่มีบทล่าสุด</string>

View File

@ -8,47 +8,47 @@
<string name="label_settings">Pagsasaayos</string>
<string name="label_download_queue">Toka sa Download</string>
<string name="label_library">Aklatan</string>
<string name="label_recent_manga">Kakabasa</string>
<string name="label_recent_updates">Mga update sa aklatan</string>
<string name="label_recent_manga">Kamakailang Binása</string>
<string name="label_recent_updates">Mga Update</string>
<string name="label_categories">Mga Kategorya</string>
<string name="label_backup">Backup</string>
<string name="label_migration">Paglipat ng Source</string>
<string name="label_extensions">Mga Karugtong (Extension)</string>
<string name="label_extension_info">Tungkol sa Karugtong (Extension)</string>
<string name="label_backup">Pag-backup</string>
<string name="label_migration">Ilipat</string>
<string name="label_extensions">Mga Dugtong</string>
<string name="label_extension_info">Tungkol sa Dugtong</string>
<string name="action_settings">Pagsasaayos</string>
<string name="action_filter">Salain</string>
<string name="action_filter_downloaded">Na-download</string>
<string name="action_filter_bookmarked">Na-bookmark</string>
<string name="action_filter_unread">Hindi Nabasa</string>
<string name="action_filter_read">Nabasa</string>
<string name="action_filter_bookmarked">Tinandaan</string>
<string name="action_filter_unread">Babasáhin</string>
<string name="action_filter_read">Nabása</string>
<string name="action_filter_empty">Tanggalin ang pagsala</string>
<string name="action_sort_alpha">Pa-alpabeto</string>
<string name="action_sort_total">Kabuuan</string>
<string name="action_sort_last_read">Huling nabasa</string>
<string name="action_sort_total">Dami ng kabanata</string>
<string name="action_sort_last_read">Huling binása</string>
<string name="action_search">Maghanap</string>
<string name="action_global_search">Panlahatang paghahanap</string>
<string name="action_select_all">Piliin lahat</string>
<string name="name">Pangalan</string>
<string name="action_mark_as_read">Markahang nabasa</string>
<string name="action_mark_as_unread">Markahang hindi nabasa</string>
<string name="action_mark_previous_as_read">Markahang nabasa ang huli</string>
<string name="action_mark_as_read">Markahang nabása</string>
<string name="action_mark_as_unread">Markahang babasáhin</string>
<string name="action_mark_previous_as_read">Markahang nabása ang huli</string>
<string name="action_download">I-download</string>
<string name="action_bookmark">I-bookmark</string>
<string name="action_remove_bookmark">Tanggalin ang bookmark</string>
<string name="action_bookmark">Tandaan</string>
<string name="action_remove_bookmark">Tanggalin ang pananda</string>
<string name="action_delete">Burahin</string>
<string name="action_update">I-update</string>
<string name="action_update_library">I-update ang aklatan</string>
<string name="action_edit">I-edit</string>
<string name="action_add">Idagdag</string>
<string name="action_add">Magdagdag</string>
<string name="action_add_category">Magdagdag ng kategorya</string>
<string name="action_edit_categories">I-edit ang mga kategorya</string>
<string name="action_rename_category">Baguhin ang pangalan ng kategorya</string>
<string name="action_move_category">Ilipat sa (mga) kategorya</string>
<string name="action_rename_category">Baguhin ang pangalan</string>
<string name="action_move_category">I-kategorya</string>
<string name="action_edit_cover">I-edit ang pabalat</string>
<string name="action_sort_up">Ayusin pataas</string>
<string name="action_sort_down">Ayusin pababa</string>
<string name="action_show_downloaded">Mga na-download</string>
<string name="action_next_unread">Susunod na hindi pa nababasa</string>
<string name="action_next_unread">Susunod na babasáhin</string>
<string name="action_stop">Itigil</string>
<string name="action_pause">Ihinto</string>
<string name="action_close">Isara</string>
@ -60,11 +60,11 @@
<string name="action_move">Ilipat</string>
<string name="action_open_in_browser">Buksan sa browser</string>
<string name="action_open_in_web_view">Buksan sa WebView</string>
<string name="action_display_mode">Palitan ang display mode</string>
<string name="action_display">Display</string>
<string name="action_display_grid">Pa-grid</string>
<string name="action_display_list">Pa-listahan</string>
<string name="action_display_download_badge">Mga badge ng download</string>
<string name="action_display_mode">Kaparaanan</string>
<string name="action_display">Pagpapakita</string>
<string name="action_display_grid">Siksik</string>
<string name="action_display_list">Listahan</string>
<string name="action_display_download_badge">Bilang ng na-download</string>
<string name="action_cancel">Ikansela</string>
<string name="action_sort">Ibukod</string>
<string name="action_install">I-install</string>
@ -74,23 +74,23 @@
<string name="action_undo">I-undo</string>
<string name="action_open_log">Buksan ang log</string>
<string name="action_create">Gumawa</string>
<string name="action_restore">I-restore</string>
<string name="action_restore">Ipanumbalik</string>
<string name="action_open">Buksan</string>
<string name="action_login">Mag-log in</string>
<string name="loading">Nagkakarga…</string>
<string name="app_not_available">Hindi available ang app</string>
<string name="short_recent_updates">Mga Update</string>
<string name="pref_category_general">Pangkalahatan</string>
<string name="pref_category_reader">Mambabasa</string>
<string name="pref_category_reader">Pagbása</string>
<string name="pref_category_downloads">Mga Download</string>
<string name="pref_category_tracking">Sinusundan</string>
<string name="pref_category_advanced">Advanced</string>
<string name="pref_category_advanced">Higit pa</string>
<string name="pref_category_about">Tungkol</string>
<string name="pref_library_columns">Manga sa aklatan kada hilera</string>
<string name="pref_library_columns">Kada hilera</string>
<string name="portrait">Patayo</string>
<string name="landscape">Pahiga</string>
<string name="default_columns">Default</string>
<string name="pref_library_update_interval">Dalas ng pag-a-update ng aklatan</string>
<string name="default_columns">Itinakda</string>
<string name="pref_library_update_interval">Dalas ng pag-update</string>
<string name="update_never">Mano-mano</string>
<string name="update_1hour">Oras-oras</string>
<string name="update_2hour">Kada 2 oras</string>
@ -103,15 +103,15 @@
<string name="update_monthly">Buwan-buwan</string>
<string name="pref_library_update_categories">Mga Kategoryang isasama sa pangkalahatang update</string>
<string name="all">Lahat</string>
<string name="pref_library_update_restriction">Mga restriksyon sa pag-a-update ng aklatan</string>
<string name="pref_library_update_restriction_summary">I-update lamang kung nakamit ang mga kondisyon</string>
<string name="wifi">Wi-Fi</string>
<string name="charging">Habang nagtsa-charge</string>
<string name="pref_library_update_restriction">Kondisyon sa pag-update</string>
<string name="pref_library_update_restriction_summary">I-update lamang kapag nakamit ang (mga) kondisyon</string>
<string name="wifi">Naka-wifi</string>
<string name="charging">Naka-charge</string>
<string name="pref_update_only_non_completed">I-update lamang ang (mga) nagpapatuloy na manga</string>
<string name="pref_auto_update_manga_sync">I-sync ang mga kabanata pagkatapos basahin</string>
<string name="pref_start_screen">Start screen</string>
<string name="pref_start_screen">Pambungad na iskrin</string>
<string name="pref_language">Wika</string>
<string name="system_default">Default ng sistema</string>
<string name="system_default">Bahala ang sistema</string>
<string name="default_category">Default na kategorya</string>
<string name="default_category_summary">Palaging tanungin</string>
<string name="all_lang">Lahat</string>
@ -220,7 +220,7 @@
<string name="filter_mode_lighten">Iwas / Liwanag (Dodge / Lighten)</string>
<string name="filter_mode_darken">Sunog / Dilim (Burn / Darken)</string>
<string name="label_help">Tulong</string>
<string name="pref_library_update_prioritization">Ayos ng pag-update ng Aklatan</string>
<string name="pref_library_update_prioritization">Ayos ng pag-update</string>
<string name="backup_choice">Ano ang gusto mong i-backup\?</string>
<string name="restoring_backup">Nire-restore ang backup</string>
<string name="creating_backup">Ginagawa ang backup</string>
@ -275,10 +275,6 @@
<string name="manga_info_full_title_label">Pamagat</string>
<string name="manga_added_library">Nadagdag sa aklatan</string>
<string name="manga_removed_library">Natanggal sa aklatan</string>
<string name="manga_info_chapters_label">Mga Kabanata</string>
<string name="manga_info_status_label">Estado</string>
<string name="manga_info_source_label">Galing sa</string>
<string name="manga_info_genres_label">Mga Dyanra</string>
<string name="delete_downloads_for_manga">Burahin ang mga na-download na kabanata\?</string>
<string name="copied_to_clipboard">Nakopya sa clipboard ang %1$s</string>
<string name="source_not_installed">Hindi na-install ang source: %1$s</string>
@ -320,7 +316,6 @@
<string name="track_start_date">Nagsimula</string>
<string name="track_type">Uri</string>
<string name="track_author">Gumawa</string>
<string name="url_not_set">Hindi na-set ang URL ng manga. Mangyaring pindutin ang pamagat at pumili ng manga muli.</string>
<string name="error_category_exists">Mayroon nang kategoryang kasing-pangalan nito!</string>
<string name="snack_categories_deleted">Binura ang mga kategorya</string>
<string name="dialog_with_checkbox_remove_description">Tatanggalin nito ang petsa ng pagbasa ng kabanatang ito. Sigurado ka ba\?</string>
@ -354,8 +349,8 @@
<string name="migrate">Lumipat</string>
<string name="copy">Kopyahin</string>
<string name="download_queue_error">Nagkaproblema habang nagda-download ng mga kabanata. Maaring mong ulitin ito sa Downloads section.</string>
<string name="notification_update_progress">Progreso ng update: %1$d/%2$d</string>
<string name="notification_new_chapters">May nakitang bagong (mga) kabanata</string>
<string name="notification_update_progress">Usad: %1$d/%2$d</string>
<string name="notification_new_chapters">May (mga) bagong kabanatang nakita</string>
<string name="notification_cover_update_failed">Pumalya sa pag-update ng pabalat</string>
<string name="notification_first_add_to_library">Mangyaring idagdag muna ang manga sa iyong aklatan bago gawin ito</string>
<string name="notification_not_connected_to_ac_title">Kinansela ang pag-sync</string>
@ -374,13 +369,12 @@
<string name="update_check_notification_download_complete">Nakumpleto ang pag-download</string>
<string name="update_check_notification_download_error">Error sa pag-download</string>
<string name="update_check_notification_update_available">Mayroong update</string>
<string name="description_backdrop">Larawang sa likod ng manga</string>
<string name="description_cover">Pabalat ng manga</string>
<string name="information_no_downloads">Walang mga download</string>
<string name="information_no_recent">Walang mga kamakailang kabanata</string>
<string name="information_no_recent_manga">Walang mga kakabasang kabanata</string>
<string name="information_empty_library">Walang laman ang iyong aklatan, magdagdag ng mga serye sa iyong aklatan mula sa mga katalogo.</string>
<string name="information_empty_category">Wala kang mga kategorya. Pindutin ang plus button para makagawa ng isa para ma-organisa ang iyong aklatan.</string>
<string name="information_no_recent">Walang bago kamakailan</string>
<string name="information_no_recent_manga">Walang binása kamakailan</string>
<string name="information_empty_library">Walang laman ang Aklatan mo, magdagdag ng serye sa iyong Aklatan mula sa Mangalugad.</string>
<string name="information_empty_category">Wala ka pang kategorya. Pindutin lang ang plus button para makagawa ng isa para maorganisa mo na ang Aklatan mo.</string>
<string name="download_notifier_downloader_title">Tagapag-download</string>
<string name="download_notifier_title_error">Error</string>
<string name="download_notifier_unknown_error">Nangyari ang isang hindi inaasahang error habang dina-download ang kabanata</string>
@ -396,10 +390,9 @@
<string name="action_webview_back">Bumalik</string>
<string name="action_webview_forward">Susunod</string>
<string name="pref_category_library">Aklatan</string>
<string name="action_sort_latest_chapter">Bagong Kabanata</string>
<string name="action_sort_latest_chapter">Pinakabagong kabanata</string>
<string name="action_view_chapters">Mga kabanata</string>
<string name="action_cancel_all">Ikansela lahat</string>
<string name="action_hide">Itago</string>
<string name="action_cancel_all">Kanselahin lahat</string>
<string name="notification_chapters_single">Kabanata %1$s</string>
<string name="track_finished_reading_date">Petsa na natapos basahin</string>
<string name="track_started_reading_date">Petsa nagsimulang basahin</string>
@ -407,11 +400,67 @@
<string name="manga_info_about_label">Tungkol</string>
<string name="pref_category_reading">Binabasa</string>
<string name="pref_category_security">Seguridad</string>
<string name="action_move_to_bottom">Ilagay Sa baba</string>
<string name="action_move_to_top">Ilagay sa Taas</string>
<string name="action_move_to_bottom">I-baba</string>
<string name="action_move_to_top">I-taas</string>
<string name="action_oldest">Pinakaluma</string>
<string name="action_newest">Pinakabago</string>
<string name="label_sources">Mga Sources</string>
<string name="action_sort_last_checked">Huling nakita</string>
<string name="unlock_app">I-unlock ang Tachiyomi</string>
<string name="label_sources">Mga Pinagkunan</string>
<string name="action_sort_last_checked">Huling sinilip</string>
<string name="unlock_app">Buksan ang Tachiyomi</string>
<string name="action_pin">I-pin</string>
<string name="action_display_show_tabs">Ipakita ang mga kategorya</string>
<string name="action_display_unread_badge">Bilang ng babasáhin</string>
<string name="action_display_comfortable_grid">Maginhawa</string>
<string name="action_migrate">Lumipat</string>
<string name="action_start">Magsimula</string>
<string name="action_disable_all">Isara lahat</string>
<string name="action_enable_all">Buksan lahat</string>
<string name="action_select_inverse">Baligtarin</string>
<string name="action_menu">Pagpipilian</string>
<string name="confirm_exit">Pindutin muli upang makaalis</string>
<string name="label_more">Karagdagan</string>
<string name="notification_chapters_single_and_more">Kabanata %q$s at %2$d pa</string>
<plurals name="notification_chapters_generic">
<item quantity="one">Isang bagong kabanata</item>
<item quantity="other">%1$d (na) bagong kabanata</item>
</plurals>
<plurals name="notification_new_chapters_summary">
<item quantity="one">Para sa isang serye</item>
<item quantity="other">Para sa %d (na) serye</item>
</plurals>
<string name="notification_check_updates">Naghahanap ng mga bagong kabanata</string>
<string name="information_webview_outdated">Mangyaring i-update po ang inyong WebView para sa mas maayos na pagpapakita</string>
<string name="information_cloudflare_bypass_failure"/>
<string name="pref_library_update_refresh_metadata">Awtomatikong i-refresh ang metadata</string>
<string name="pref_category_library_update">Mga Update</string>
<string name="pref_category_display">Pagpapakita</string>
<string name="hide_notification_content">Itago ang nilalaman ng abiso</string>
<string name="secure_screen_summary">Itago ang mga laman ng app sa tuwing lumilipat sa ibang app at harangin ang mga screenshot</string>
<string name="secure_screen">Bantayán ang iskrin</string>
<plurals name="lock_after_mins">
<item quantity="one">Matapos ng 1 minuto</item>
<item quantity="other">Matapos ng %1$s (na) minuto</item>
</plurals>
<string name="lock_never">Di kailanman</string>
<string name="lock_always">Palagi</string>
<string name="lock_when_idle">I-lock kapag di ginagamit</string>
<string name="lock_with_biometrics">I-lock gamit ang biometrics</string>
<string name="pref_manage_notifications">Mga abiso</string>
<string name="pref_confirm_exit">Kumpirmahing aalis</string>
<string name="pref_date_format">Pagsulat ng petsa</string>
<string name="theme_dark_amoled">AMOLED na itim</string>
<string name="theme_dark_blue">May bughaw</string>
<string name="theme_dark_default">Orihinal</string>
<string name="pref_theme_light">Maliwanag na tema</string>
<string name="pref_theme_dark">Madilim na tema</string>
<string name="theme_light_blue">May bughaw</string>
<string name="theme_light_default">Orihinal</string>
<string name="theme_light">Nakasara</string>
<string name="theme_dark">Nakabukás</string>
<string name="theme_system">Sundan ang sistema</string>
<string name="pref_theme_mode">Madilim</string>
<string name="action_webview_refresh">Sariwain</string>
<string name="action_reorganize_by">I-reorder</string>
<string name="action_unpin">I-unpin</string>
<string name="action_disable">Isara</string>
</resources>

View File

@ -197,10 +197,6 @@
<string name="unknown">Không rõ</string>
<string name="licensed">Đã được mua bản quyền</string>
<string name="remove_from_library">Gỡ bỏ khỏi thư viện</string>
<string name="manga_info_chapters_label">Số chương</string>
<string name="manga_info_status_label">Trạng thái</string>
<string name="manga_info_source_label">Nguồn truyện</string>
<string name="manga_info_genres_label">Thể loại</string>
<!-- Manga chapters fragment -->
<string name="manga_chapters_tab">Danh sách chương</string>
<string name="manga_chapter_no_title">Không có tiêu đề</string>
@ -290,7 +286,6 @@
<string name="update_check_notification_download_error">Lỗi khi tải xuống</string>
<string name="update_check_notification_update_available">Có cập nhật mới!</string>
<!--Content Description-->
<string name="description_backdrop">Ảnh nền của truyện</string>
<string name="description_cover">Ảnh bìa của truyện</string>
<!-- Information Text -->
<string name="information_no_downloads">Không có tải xuống</string>
@ -397,7 +392,6 @@
<string name="track_start_date">Đã bắt đầu</string>
<string name="track_type">Loại</string>
<string name="track_author">Tác giả</string>
<string name="url_not_set">Đường dẫn truyện chưa được thiết lập, vui lòng nhấn vào tiêu đề và chọn lại truyện một lần nữa</string>
<string name="transition_finished">Đã đọc xong:</string>
<string name="transition_current">Đang đọc:</string>
<string name="transition_next">Chương kế:</string>
@ -501,7 +495,6 @@
<string name="action_newest">Mới nhất</string>
<string name="action_reorganize_by">Sắp xếp lại</string>
<string name="action_cancel_all">Huỷ bỏ tất cả</string>
<string name="action_hide">Ẩn</string>
<string name="action_view_chapters">Xem các chương</string>
<string name="action_select_inverse">Đảo ngược lựa chọn</string>
<string name="action_sort_latest_chapter">Chương mới nhất</string>

View File

@ -278,10 +278,6 @@
<string name="licensed">已授權</string>
<string name="remove_from_library">移出書櫃</string>
<string name="manga_info_full_title_label">標題</string>
<string name="manga_info_chapters_label">章節</string>
<string name="manga_info_status_label">狀態</string>
<string name="manga_info_source_label">來源</string>
<string name="manga_info_genres_label">類型</string>
<string name="delete_downloads_for_manga">刪除下載的章節?</string>
<string name="manga_chapters_tab">章節</string>
<string name="manga_chapter_no_title">無標題</string>

View File

@ -20,7 +20,7 @@
<string name="label_sources">Sources</string>
<string name="label_categories">Categories</string>
<string name="label_backup">Backup</string>
<string name="label_migration">Migration</string>
<string name="label_migration">Migrate</string>
<string name="label_extensions">Extensions</string>
<string name="label_extension_info">Extension info</string>
<string name="label_help">Help</string>
@ -83,6 +83,7 @@
<string name="action_move">Move</string>
<string name="action_open_in_browser">Open in browser</string>
<string name="action_open_in_web_view">Open in WebView</string>
<string name="action_open_in_settings">Open in Settings</string>
<string name="action_migrate">Migrate</string>
<string name="action_display_mode">Display mode</string>
<string name="action_display">Display</string>
@ -98,6 +99,7 @@
<string name="action_cancel">Cancel</string>
<string name="action_cancel_all">Cancel all</string>
<string name="action_sort">Sort</string>
<string name="action_sort_descending">Descending</string>
<string name="action_reorganize_by">Reorder</string>
<string name="action_newest">Newest</string>
<string name="action_oldest">Oldest</string>
@ -174,6 +176,7 @@
<string name="portrait">Portrait</string>
<string name="landscape">Landscape</string>
<string name="default_columns">Default</string>
<string name="pref_jump_to_chapters">Jump to chapters on open</string>
<string name="pref_category_library_update">Updates</string>
<string name="pref_library_update_interval">Library update frequency</string>
@ -391,7 +394,6 @@
<string name="website">Website</string>
<string name="version">Version</string>
<string name="build_time">Build time</string>
<string name="changelog">Changelog</string>
<string name="whats_new">What\'s new</string>
<string name="notices">Preview build notices</string>
<string name="licenses">Open source licenses</string>
@ -462,6 +464,8 @@
<string name="description">Description</string>
<string name="ongoing">Ongoing</string>
<string name="unknown">Unknown</string>
<string name="unknown_author">Unknown author</string>
<string name="unknown_status">Unknown status</string>
<string name="licensed">Licensed</string>
<string name="add_to_library">Add to library</string>
<string name="in_library">In library</string>
@ -469,15 +473,13 @@
<string name="manga_info_full_title_label">Title</string>
<string name="manga_added_library">Added to library</string>
<string name="manga_removed_library">Removed from library</string>
<string name="manga_info_chapters_label">Chapters</string>
<string name="manga_info_last_chapter_label">Last chapter</string>
<string name="manga_info_latest_data_label">Updated</string>
<string name="manga_info_status_label">Status</string>
<string name="manga_info_source_label">Source</string>
<string name="manga_info_genres_label">Genres</string>
<string name="manga_info_about_label">About</string>
<string name="manga_info_expand">Show more info</string>
<string name="manga_info_collapse">Show less info</string>
<string name="manga_info_expand">More</string>
<string name="manga_info_collapse">Less</string>
<plurals name="manga_num_chapters">
<item quantity="one">1 chapter</item>
<item quantity="other">%1$s chapters</item>
</plurals>
<string name="delete_downloads_for_manga">Delete downloaded chapters?</string>
<string name="copied_to_clipboard">Copied to clipboard:\n%1$s</string>
<string name="source_not_installed">Source not installed: %1$s</string>

View File

@ -33,6 +33,10 @@
<string name="ext_redundant">Redundant</string>
<string name="redundant_extension_message">This extension is redundant and will not be used inside this version of Tachiyomi.</string>
<!-- About Section -->
<string name="changelog">Changelog</string>
<!-- Library update service notifications -->
<string name="notification_new_chapters_text_old">For %1$d titles</string>