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:
parent
8ab2a823b5
commit
372e570fac
@ -67,6 +67,8 @@ class AppModule(val app: Application) : InjektModule {
|
||||
|
||||
GlobalScope.launch { get<DownloadManager>() }
|
||||
|
||||
// SY -->
|
||||
GlobalScope.launch { get<CustomMangaManager>() }
|
||||
// SY <--
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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 <--
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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) {
|
||||
|
@ -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!!)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
}
|
@ -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 = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ class TrackSearchAdapter(context: Context) :
|
||||
} else {
|
||||
holder = v.tag as TrackSearchHolder
|
||||
}
|
||||
holder.onSetValues(track!!)
|
||||
holder.onSetValues(track)
|
||||
return v
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ class AboutController : SettingsController() {
|
||||
}
|
||||
}
|
||||
preference {
|
||||
titleRes = R.string.changelog
|
||||
titleRes = R.string.whats_new
|
||||
|
||||
onClick {
|
||||
// SY -->
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -72,6 +72,11 @@ class SettingsLibraryController : SettingsController() {
|
||||
}
|
||||
.launchIn(scope)
|
||||
}
|
||||
switchPreference {
|
||||
key = Keys.jumpToChapters
|
||||
titleRes = R.string.pref_jump_to_chapters
|
||||
defaultValue = false
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
|
@ -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)) {
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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)
|
||||
}
|
||||
|
10
app/src/main/res/drawable/ic_sort_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_sort_24dp.xml
Normal 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>
|
@ -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>
|
11
app/src/main/res/drawable/manga_info_gradient.xml
Normal file
11
app/src/main/res/drawable/manga_info_gradient.xml
Normal 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>
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
20
app/src/main/res/layout/manga_chapters_header.xml
Normal file
20
app/src/main/res/layout/manga_chapters_header.xml
Normal 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>
|
@ -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>
|
310
app/src/main/res/layout/manga_info_header.xml
Normal file
310
app/src/main/res/layout/manga_info_header.xml
Normal 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>
|
@ -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"
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
||||
|
574
app/src/main/res/values-fa/strings.xml
Normal file
574
app/src/main/res/values-fa/strings.xml
Normal 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>
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user