New All In One Manga interface, its optional and can be disabled
This commit is contained in:
parent
92af538b0a
commit
b09f1fff51
@ -230,4 +230,6 @@ object PreferenceKeys {
|
|||||||
const val eh_tag_watching_value = "eh_tag_watching_value"
|
const val eh_tag_watching_value = "eh_tag_watching_value"
|
||||||
|
|
||||||
const val eh_is_hentai_enabled = "eh_is_hentai_enabled"
|
const val eh_is_hentai_enabled = "eh_is_hentai_enabled"
|
||||||
|
|
||||||
|
const val eh_use_new_manga_interface = "eh_use_new_manga_interface"
|
||||||
}
|
}
|
||||||
|
@ -340,4 +340,6 @@ class PreferencesHelper(val context: Context) {
|
|||||||
fun eh_hl_useHighQualityThumbs() = flowPrefs.getBoolean(Keys.eh_hl_useHighQualityThumbs, false)
|
fun eh_hl_useHighQualityThumbs() = flowPrefs.getBoolean(Keys.eh_hl_useHighQualityThumbs, false)
|
||||||
|
|
||||||
fun eh_preload_size() = flowPrefs.getInt(Keys.eh_preload_size, 4)
|
fun eh_preload_size() = flowPrefs.getInt(Keys.eh_preload_size, 4)
|
||||||
|
|
||||||
|
fun eh_useNewMangaInterface() = flowPrefs.getBoolean(Keys.eh_use_new_manga_interface, true)
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ 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.browse.source.browse.SourceFilterSheet.FilterNavigationView.Companion.MAX_SAVED_SEARCHES
|
||||||
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
|
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
|
||||||
import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight
|
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.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||||
import eu.kanade.tachiyomi.util.system.connectivityManager
|
import eu.kanade.tachiyomi.util.system.connectivityManager
|
||||||
@ -648,13 +649,25 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||||||
val item = adapter?.getItem(position) as? SourceItem ?: return false
|
val item = adapter?.getItem(position) as? SourceItem ?: return false
|
||||||
|
|
||||||
when (mode) {
|
when (mode) {
|
||||||
Mode.CATALOGUE -> router.pushController(
|
Mode.CATALOGUE -> {
|
||||||
MangaController(
|
if (preferences.eh_useNewMangaInterface().get()) {
|
||||||
item.manga,
|
router.pushController(
|
||||||
true,
|
MangaAllInOneController(
|
||||||
args.getParcelable(SMART_SEARCH_CONFIG_KEY)
|
item.manga,
|
||||||
).withFadeTransaction()
|
true,
|
||||||
)
|
args.getParcelable(SMART_SEARCH_CONFIG_KEY)
|
||||||
|
).withFadeTransaction()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
router.pushController(
|
||||||
|
MangaController(
|
||||||
|
item.manga,
|
||||||
|
true,
|
||||||
|
args.getParcelable(SMART_SEARCH_CONFIG_KEY)
|
||||||
|
).withFadeTransaction()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Mode.RECOMMENDS -> openSmartSearch(item.manga.title)
|
Mode.RECOMMENDS -> openSmartSearch(item.manga.title)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource
|
|||||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
|
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.ui.manga.MangaController
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
@ -83,7 +84,11 @@ open class GlobalSearchController(
|
|||||||
*/
|
*/
|
||||||
override fun onMangaClick(manga: Manga) {
|
override fun onMangaClick(manga: Manga) {
|
||||||
// Open MangaController.
|
// Open MangaController.
|
||||||
router.pushController(MangaController(manga, true).withFadeTransaction())
|
if (preferences.eh_useNewMangaInterface().get()) {
|
||||||
|
router.pushController(MangaAllInOneController(manga, true).withFadeTransaction())
|
||||||
|
} else {
|
||||||
|
router.pushController(MangaController(manga, true).withFadeTransaction())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,6 +36,7 @@ import eu.kanade.tachiyomi.ui.base.controller.TabbedController
|
|||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight
|
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.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.migration.MigrationController
|
import eu.kanade.tachiyomi.ui.migration.MigrationController
|
||||||
import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController
|
import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController
|
||||||
@ -514,7 +515,11 @@ class LibraryController(
|
|||||||
// Notify the presenter a manga is being opened.
|
// Notify the presenter a manga is being opened.
|
||||||
presenter.onOpenManga()
|
presenter.onOpenManga()
|
||||||
|
|
||||||
router.pushController(MangaController(manga).withFadeTransaction())
|
if (preferences.eh_useNewMangaInterface().get()) {
|
||||||
|
router.pushController(MangaAllInOneController(manga).withFadeTransaction())
|
||||||
|
} else {
|
||||||
|
router.pushController(MangaController(manga).withFadeTransaction())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +31,7 @@ import eu.kanade.tachiyomi.ui.browse.BrowseController
|
|||||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||||
import eu.kanade.tachiyomi.ui.download.DownloadController
|
import eu.kanade.tachiyomi.ui.download.DownloadController
|
||||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
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.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.more.MoreController
|
import eu.kanade.tachiyomi.ui.more.MoreController
|
||||||
import eu.kanade.tachiyomi.ui.recent.history.HistoryController
|
import eu.kanade.tachiyomi.ui.recent.history.HistoryController
|
||||||
@ -296,7 +297,11 @@ class MainActivity : BaseActivity<MainActivityBinding>() {
|
|||||||
router.popToRoot()
|
router.popToRoot()
|
||||||
}
|
}
|
||||||
setSelectedNavItem(R.id.nav_library)
|
setSelectedNavItem(R.id.nav_library)
|
||||||
router.pushController(RouterTransaction.with(MangaController(extras)))
|
if (preferences.eh_useNewMangaInterface().get()) {
|
||||||
|
router.pushController(RouterTransaction.with(MangaAllInOneController(extras)))
|
||||||
|
} else {
|
||||||
|
router.pushController(RouterTransaction.with(MangaController(extras)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SHORTCUT_DOWNLOADS -> {
|
SHORTCUT_DOWNLOADS -> {
|
||||||
if (router.backstackSize > 1) {
|
if (router.backstackSize > 1) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,697 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.manga
|
||||||
|
|
||||||
|
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.Chapter
|
||||||
|
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.data.download.model.Download
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
|
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.ui.manga.chapter.ChapterItem
|
||||||
|
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||||
|
import eu.kanade.tachiyomi.util.lang.isNullOrUnsubscribed
|
||||||
|
import eu.kanade.tachiyomi.util.prepUpdateCover
|
||||||
|
import eu.kanade.tachiyomi.util.removeCovers
|
||||||
|
import exh.EH_SOURCE_ID
|
||||||
|
import exh.EXH_SOURCE_ID
|
||||||
|
import exh.MERGED_SOURCE_ID
|
||||||
|
import exh.debug.DebugToggles
|
||||||
|
import exh.eh.EHentaiUpdateHelper
|
||||||
|
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 timber.log.Timber
|
||||||
|
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 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(),
|
||||||
|
val preferences: PreferencesHelper = Injekt.get()
|
||||||
|
) : BasePresenter<MangaAllInOneController>() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
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 <--
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscription to send the manga to the view.
|
||||||
|
*/
|
||||||
|
private var viewMangaSubscription: Subscription? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(MangaAllInOneController::setChapterCount)
|
||||||
|
|
||||||
|
// Prepare the relay.
|
||||||
|
chaptersRelay.flatMap { applyChapterFilters(it) }
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribeLatestCache(MangaAllInOneController::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(
|
||||||
|
MangaAllInOnePresenter.EXHRedirect(
|
||||||
|
acceptedChain.manga,
|
||||||
|
update
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// EXH <--
|
||||||
|
}
|
||||||
|
.subscribe { chaptersRelay.call(it) }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Update favorite status
|
||||||
|
mangaFavoriteRelay.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { setFavorite(it) }
|
||||||
|
.apply { add(this) }
|
||||||
|
|
||||||
|
// update last update date
|
||||||
|
lastUpdateRelay.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribeLatestCache(MangaAllInOneController::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 ->
|
||||||
|
if (manualFetch || manga.thumbnail_url != networkManga.thumbnail_url) {
|
||||||
|
manga.prepUpdateCover(coverCache)
|
||||||
|
}
|
||||||
|
manga.copyFrom(networkManga)
|
||||||
|
manga.initialized = true
|
||||||
|
db.insertManga(manga).executeAsBlocking()
|
||||||
|
manga
|
||||||
|
}
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribeFirst(
|
||||||
|
{ view, _ ->
|
||||||
|
view.onFetchMangaDone()
|
||||||
|
},
|
||||||
|
MangaAllInOneController::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.title, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
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(MangaAllInOneController::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>) {
|
||||||
|
for (chapter in chapters) {
|
||||||
|
if (downloadManager.isChapterDownloaded(chapter, manga)) {
|
||||||
|
chapter.status = Download.DOWNLOADED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests an updated list of chapters from the source.
|
||||||
|
*/
|
||||||
|
fun fetchChaptersFromSource() {
|
||||||
|
hasRequested = true
|
||||||
|
|
||||||
|
if (!fetchChaptersSubscription.isNullOrUnsubscribed()) return
|
||||||
|
fetchChaptersSubscription = Observable.defer { source.fetchChapterList(manga) }
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.map { syncChaptersWithSource(db, it, manga, source) }
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribeFirst(
|
||||||
|
{ view, _ ->
|
||||||
|
view.onFetchChaptersDone()
|
||||||
|
},
|
||||||
|
MangaAllInOneController::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.source == LocalSource.ID }
|
||||||
|
}
|
||||||
|
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) }
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
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<ChapterItem>) {
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
MangaAllInOneController::onChaptersDeletedError
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ import java.text.DecimalFormatSymbols
|
|||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class ChaptersAdapter(
|
class ChaptersAdapter(
|
||||||
controller: ChaptersController,
|
controller: Any,
|
||||||
context: Context
|
context: Context
|
||||||
) : FlexibleAdapter<ChapterItem>(null, controller, true) {
|
) : FlexibleAdapter<ChapterItem>(null, controller, true) {
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import eu.kanade.tachiyomi.source.SourceManager
|
|||||||
import eu.kanade.tachiyomi.source.model.SChapter
|
import eu.kanade.tachiyomi.source.model.SChapter
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
|
import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.migration.MigrationMangaDialog
|
import eu.kanade.tachiyomi.ui.migration.MigrationMangaDialog
|
||||||
import eu.kanade.tachiyomi.ui.migration.SearchController
|
import eu.kanade.tachiyomi.ui.migration.SearchController
|
||||||
@ -397,7 +398,7 @@ class MigrationListController(bundle: Bundle? = null) :
|
|||||||
private fun navigateOut() {
|
private fun navigateOut() {
|
||||||
if (migratingManga?.size == 1) {
|
if (migratingManga?.size == 1) {
|
||||||
launchUI {
|
launchUI {
|
||||||
val hasDetails = router.backstack.any { it.controller() is MangaController }
|
val hasDetails = router.backstack.any { it.controller() is MangaController } || router.backstack.any { it.controller() is MangaAllInOneController }
|
||||||
if (hasDetails) {
|
if (hasDetails) {
|
||||||
val manga = migratingManga?.firstOrNull()?.searchResult?.get()?.let {
|
val manga = migratingManga?.firstOrNull()?.searchResult?.get()?.let {
|
||||||
db.getManga(it).executeOnIO()
|
db.getManga(it).executeOnIO()
|
||||||
@ -405,9 +406,10 @@ class MigrationListController(bundle: Bundle? = null) :
|
|||||||
if (manga != null) {
|
if (manga != null) {
|
||||||
val newStack = router.backstack.filter {
|
val newStack = router.backstack.filter {
|
||||||
it.controller() !is MangaController &&
|
it.controller() !is MangaController &&
|
||||||
|
it.controller() !is MangaAllInOneController &&
|
||||||
it.controller() !is MigrationListController &&
|
it.controller() !is MigrationListController &&
|
||||||
it.controller() !is PreMigrationController
|
it.controller() !is PreMigrationController
|
||||||
} + MangaController(manga).withFadeTransaction()
|
} + MangaController(manga).withFadeTransaction() // TODO MangaAllInOneController
|
||||||
router.setBackstack(newStack, FadeChangeHandler())
|
router.setBackstack(newStack, FadeChangeHandler())
|
||||||
return@launchUI
|
return@launchUI
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,12 @@ import eu.kanade.tachiyomi.R
|
|||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.glide.GlideApp
|
import eu.kanade.tachiyomi.data.glide.GlideApp
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
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.ui.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||||
@ -23,6 +25,8 @@ import kotlinx.android.synthetic.main.migration_manga_card.view.*
|
|||||||
import kotlinx.android.synthetic.main.migration_process_item.*
|
import kotlinx.android.synthetic.main.migration_process_item.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class MigrationProcessHolder(
|
class MigrationProcessHolder(
|
||||||
@ -67,12 +71,21 @@ class MigrationProcessHolder(
|
|||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
migration_manga_card_from.attachManga(manga, source)
|
migration_manga_card_from.attachManga(manga, source)
|
||||||
migration_manga_card_from.setOnClickListener {
|
migration_manga_card_from.setOnClickListener {
|
||||||
adapter.controller.router.pushController(
|
if (Injekt.get<PreferencesHelper>().eh_useNewMangaInterface().get()) {
|
||||||
MangaController(
|
adapter.controller.router.pushController(
|
||||||
manga,
|
MangaAllInOneController(
|
||||||
true
|
manga,
|
||||||
).withFadeTransaction()
|
true
|
||||||
)
|
).withFadeTransaction()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
adapter.controller.router.pushController(
|
||||||
|
MangaController(
|
||||||
|
manga,
|
||||||
|
true
|
||||||
|
).withFadeTransaction()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,12 +13,14 @@ import eu.kanade.tachiyomi.R
|
|||||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
|
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
|
||||||
import eu.kanade.tachiyomi.data.database.models.History
|
import eu.kanade.tachiyomi.data.database.models.History
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
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.databinding.HistoryControllerBinding
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
|
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.RootController
|
import eu.kanade.tachiyomi.ui.base.controller.RootController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.ProgressItem
|
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.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
@ -26,6 +28,8 @@ import kotlinx.coroutines.flow.filter
|
|||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import reactivecircus.flowbinding.appcompat.queryTextChanges
|
import reactivecircus.flowbinding.appcompat.queryTextChanges
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment that shows recently read manga.
|
* Fragment that shows recently read manga.
|
||||||
@ -158,7 +162,11 @@ class HistoryController :
|
|||||||
|
|
||||||
override fun onItemClick(position: Int) {
|
override fun onItemClick(position: Int) {
|
||||||
val manga = (adapter?.getItem(position) as? HistoryItem)?.mch?.manga ?: return
|
val manga = (adapter?.getItem(position) as? HistoryItem)?.mch?.manga ?: return
|
||||||
router.pushController(MangaController(manga).withFadeTransaction())
|
if (Injekt.get<PreferencesHelper>().eh_useNewMangaInterface().get()) {
|
||||||
|
router.pushController(MangaAllInOneController(manga).withFadeTransaction())
|
||||||
|
} else {
|
||||||
|
router.pushController(MangaController(manga).withFadeTransaction())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeHistory(manga: Manga, history: History, all: Boolean) {
|
override fun removeHistory(manga: Manga, history: History, all: Boolean) {
|
||||||
|
@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.R
|
|||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||||
import eu.kanade.tachiyomi.data.notification.Notifications
|
import eu.kanade.tachiyomi.data.notification.Notifications
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.databinding.UpdatesControllerBinding
|
import eu.kanade.tachiyomi.databinding.UpdatesControllerBinding
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
|
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||||
@ -24,6 +25,7 @@ import eu.kanade.tachiyomi.ui.base.controller.RootController
|
|||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight
|
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.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||||
@ -33,6 +35,8 @@ import kotlinx.coroutines.flow.onEach
|
|||||||
import reactivecircus.flowbinding.recyclerview.scrollStateChanges
|
import reactivecircus.flowbinding.recyclerview.scrollStateChanges
|
||||||
import reactivecircus.flowbinding.swiperefreshlayout.refreshes
|
import reactivecircus.flowbinding.swiperefreshlayout.refreshes
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment that shows recent chapters.
|
* Fragment that shows recent chapters.
|
||||||
@ -282,7 +286,11 @@ class UpdatesController :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun openManga(chapter: UpdatesItem) {
|
private fun openManga(chapter: UpdatesItem) {
|
||||||
router.pushController(MangaController(chapter.manga).withFadeTransaction())
|
if (Injekt.get<PreferencesHelper>().eh_useNewMangaInterface().get()) {
|
||||||
|
router.pushController(MangaAllInOneController(chapter.manga).withFadeTransaction())
|
||||||
|
} else {
|
||||||
|
router.pushController(MangaController(chapter.manga).withFadeTransaction())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,61 +193,31 @@ class SettingsGeneralController : SettingsController() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --> EXH
|
// --> EXH
|
||||||
switchPreference {
|
preferenceCategory {
|
||||||
key = Keys.eh_expandFilters
|
title = "EH Settings"
|
||||||
title = "Expand all search filters by default"
|
|
||||||
defaultValue = false
|
|
||||||
}
|
|
||||||
|
|
||||||
switchPreference {
|
switchPreference {
|
||||||
key = Keys.eh_autoSolveCaptchas
|
key = Keys.eh_expandFilters
|
||||||
title = "Automatically solve captcha"
|
title = "Expand all search filters by default"
|
||||||
summary =
|
defaultValue = false
|
||||||
"Use HIGHLY EXPERIMENTAL automatic ReCAPTCHA solver. Will be grayed out if unsupported by your device."
|
|
||||||
defaultValue = false
|
|
||||||
}
|
|
||||||
|
|
||||||
/*preferenceCategory {
|
|
||||||
title = "Application lock"
|
|
||||||
|
|
||||||
LockPreference(context).apply {
|
|
||||||
key = "pref_app_lock" // Not persistent so use random key
|
|
||||||
isPersistent = false
|
|
||||||
|
|
||||||
addPreference(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
FingerLockPreference(context).apply {
|
|
||||||
key = "pref_lock_finger" // Not persistent so use random key
|
|
||||||
isPersistent = false
|
|
||||||
|
|
||||||
addPreference(this)
|
|
||||||
|
|
||||||
// Call after addPreference
|
|
||||||
dependency = "pref_app_lock"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switchPreference {
|
switchPreference {
|
||||||
key = Keys.eh_lock_manually
|
key = Keys.eh_autoSolveCaptchas
|
||||||
|
title = "Automatically solve captcha"
|
||||||
title = "Lock manually only"
|
|
||||||
summary =
|
summary =
|
||||||
"Disable automatic app locking. The app can still be locked manually by long-pressing the three-lines/back button in the top left corner."
|
"Use HIGHLY EXPERIMENTAL automatic ReCAPTCHA solver. Will be grayed out if unsupported by your device."
|
||||||
defaultValue = false
|
defaultValue = false
|
||||||
}
|
}
|
||||||
|
|
||||||
switchPreference {
|
switchPreference {
|
||||||
key = Keys.secureScreen
|
key = Keys.eh_use_new_manga_interface
|
||||||
title = "Enable Secure Screen"
|
title = "Use New Manga Interface"
|
||||||
defaultValue = false
|
summary = "Use new all in one manga interface"
|
||||||
|
defaultValue = true
|
||||||
}
|
}
|
||||||
switchPreference {
|
}
|
||||||
key = Keys.hideNotificationContent
|
|
||||||
titleRes = R.string.hide_notification_content
|
|
||||||
defaultValue = false
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
// <-- EXH
|
// <-- EXH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import android.os.Bundle
|
|||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.databinding.SmartSearchBinding
|
import eu.kanade.tachiyomi.databinding.SmartSearchBinding
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
@ -11,6 +12,7 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
|||||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.SourceController
|
import eu.kanade.tachiyomi.ui.browse.source.SourceController
|
||||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
|
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.ui.manga.MangaController
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -20,6 +22,8 @@ import kotlinx.coroutines.NonCancellable
|
|||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import uy.kohesive.injekt.Injekt
|
||||||
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
|
||||||
class SmartSearchController(bundle: Bundle? = null) : NucleusController<SmartSearchBinding, SmartSearchPresenter>(), CoroutineScope {
|
class SmartSearchController(bundle: Bundle? = null) : NucleusController<SmartSearchBinding, SmartSearchPresenter>(), CoroutineScope {
|
||||||
@ -59,7 +63,11 @@ class SmartSearchController(bundle: Bundle? = null) : NucleusController<SmartSea
|
|||||||
for (event in presenter.smartSearchChannel) {
|
for (event in presenter.smartSearchChannel) {
|
||||||
withContext(NonCancellable) {
|
withContext(NonCancellable) {
|
||||||
if (event is SmartSearchPresenter.SearchResults.Found) {
|
if (event is SmartSearchPresenter.SearchResults.Found) {
|
||||||
val transaction = MangaController(event.manga, true, smartSearchConfig).withFadeTransaction()
|
val transaction = if (Injekt.get<PreferencesHelper>().eh_useNewMangaInterface().get()) {
|
||||||
|
MangaAllInOneController(event.manga, true, smartSearchConfig).withFadeTransaction()
|
||||||
|
} else {
|
||||||
|
MangaController(event.manga, true, smartSearchConfig).withFadeTransaction()
|
||||||
|
}
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
router.replaceTopController(transaction)
|
router.replaceTopController(transaction)
|
||||||
}
|
}
|
||||||
|
439
app/src/main/res/layout-land/manga_all_in_one_controller.xml
Normal file
439
app/src/main/res/layout-land/manga_all_in_one_controller.xml
Normal file
@ -0,0 +1,439 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<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_label"
|
||||||
|
style="@style/TextAppearance.Medium.Body2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/manga_info_author_label"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_full_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_author"
|
||||||
|
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_author_label"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/manga_author_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_artist_label"
|
||||||
|
style="@style/TextAppearance.Medium.Body2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/manga_info_artist_label"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_author_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_artist"
|
||||||
|
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_artist_label"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/manga_artist_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_chapters_label"
|
||||||
|
style="@style/TextAppearance.Medium.Body2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/manga_info_last_chapter_label"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_artist_label" />
|
||||||
|
|
||||||
|
<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="1"
|
||||||
|
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:layout_marginTop="4dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
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/Widget.MaterialComponents.Button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/merge"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_info_toggle"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/recommend_btn"
|
||||||
|
style="@style/Widget.MaterialComponents.Button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/az_recommends"
|
||||||
|
android:visibility="gone"
|
||||||
|
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"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/recommend_btn">
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:descendantFocusability="blocksDescendants"
|
||||||
|
android:paddingBottom="@dimen/fab_list_padding"
|
||||||
|
tools:listitem="@layout/chapters_item" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout>
|
||||||
|
|
||||||
|
<eu.davidea.fastscroller.FastScroller
|
||||||
|
android:id="@+id/fast_scroller"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layoutDirection="ltr"
|
||||||
|
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_resume"
|
||||||
|
app:icon="@drawable/ic_play_arrow_24dp"
|
||||||
|
app:layout_anchor="@id/recycler" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
453
app/src/main/res/layout/manga_all_in_one_controller.xml
Normal file
453
app/src/main/res/layout/manga_all_in_one_controller.xml
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
<?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">
|
||||||
|
|
||||||
|
<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">
|
||||||
|
|
||||||
|
<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_label"
|
||||||
|
style="@style/TextAppearance.Medium.Body2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/manga_info_author_label"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_full_title" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_author"
|
||||||
|
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_author_label"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/manga_author_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_artist_label"
|
||||||
|
style="@style/TextAppearance.Medium.Body2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/manga_info_artist_label"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_author_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_artist"
|
||||||
|
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_artist_label"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/manga_artist_label" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_chapters_label"
|
||||||
|
style="@style/TextAppearance.Medium.Body2"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/manga_info_last_chapter_label"
|
||||||
|
android:textIsSelectable="false"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/manga_artist_label" />
|
||||||
|
|
||||||
|
<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="1"
|
||||||
|
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_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/Widget.MaterialComponents.Button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/eh_merge_with_another_source"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/recommend_btn"
|
||||||
|
style="@style/Widget.MaterialComponents.Button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="@string/az_recommends"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recycler"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:descendantFocusability="blocksDescendants"
|
||||||
|
android:paddingBottom="@dimen/fab_list_padding"
|
||||||
|
tools:listitem="@layout/chapters_item" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
</eu.kanade.tachiyomi.widget.ThemedSwipeRefreshLayout>
|
||||||
|
|
||||||
|
<eu.davidea.fastscroller.FastScroller
|
||||||
|
android:id="@+id/fast_scroller"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_centerHorizontal="true"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layoutDirection="ltr"
|
||||||
|
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_resume"
|
||||||
|
app:icon="@drawable/ic_play_arrow_24dp"
|
||||||
|
app:layout_anchor="@id/recycler" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
Loading…
x
Reference in New Issue
Block a user