mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-30 22:07:57 +01:00 
			
		
		
		
	New All In One Manga interface, its optional and can be disabled
This commit is contained in:
		| @@ -230,4 +230,6 @@ object PreferenceKeys { | ||||
|     const val eh_tag_watching_value = "eh_tag_watching_value" | ||||
|  | ||||
|     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_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.library.ChangeMangaCategoriesDialog | ||||
| 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.webview.WebViewActivity | ||||
| 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 | ||||
|  | ||||
|         when (mode) { | ||||
|             Mode.CATALOGUE -> router.pushController( | ||||
|                 MangaController( | ||||
|                     item.manga, | ||||
|                     true, | ||||
|                     args.getParcelable(SMART_SEARCH_CONFIG_KEY) | ||||
|                 ).withFadeTransaction() | ||||
|             ) | ||||
|             Mode.CATALOGUE -> { | ||||
|                 if (preferences.eh_useNewMangaInterface().get()) { | ||||
|                     router.pushController( | ||||
|                         MangaAllInOneController( | ||||
|                             item.manga, | ||||
|                             true, | ||||
|                             args.getParcelable(SMART_SEARCH_CONFIG_KEY) | ||||
|                         ).withFadeTransaction() | ||||
|                     ) | ||||
|                 } else { | ||||
|                     router.pushController( | ||||
|                         MangaController( | ||||
|                             item.manga, | ||||
|                             true, | ||||
|                             args.getParcelable(SMART_SEARCH_CONFIG_KEY) | ||||
|                         ).withFadeTransaction() | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
|             Mode.RECOMMENDS -> openSmartSearch(item.manga.title) | ||||
|         } | ||||
|         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.withFadeTransaction | ||||
| import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import kotlinx.coroutines.flow.filter | ||||
| import kotlinx.coroutines.flow.launchIn | ||||
| @@ -83,7 +84,11 @@ open class GlobalSearchController( | ||||
|      */ | ||||
|     override fun onMangaClick(manga: Manga) { | ||||
|         // 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.main.MainActivity | ||||
| import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import eu.kanade.tachiyomi.ui.migration.MigrationController | ||||
| import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController | ||||
| @@ -514,7 +515,11 @@ class LibraryController( | ||||
|         // Notify the presenter a manga is being opened. | ||||
|         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.download.DownloadController | ||||
| import eu.kanade.tachiyomi.ui.library.LibraryController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import eu.kanade.tachiyomi.ui.more.MoreController | ||||
| import eu.kanade.tachiyomi.ui.recent.history.HistoryController | ||||
| @@ -296,7 +297,11 @@ class MainActivity : BaseActivity<MainActivityBinding>() { | ||||
|                     router.popToRoot() | ||||
|                 } | ||||
|                 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 -> { | ||||
|                 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 | ||||
|  | ||||
| class ChaptersAdapter( | ||||
|     controller: ChaptersController, | ||||
|     controller: Any, | ||||
|     context: Context | ||||
| ) : 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.ui.base.controller.BaseController | ||||
| 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.migration.MigrationMangaDialog | ||||
| import eu.kanade.tachiyomi.ui.migration.SearchController | ||||
| @@ -397,7 +398,7 @@ class MigrationListController(bundle: Bundle? = null) : | ||||
|     private fun navigateOut() { | ||||
|         if (migratingManga?.size == 1) { | ||||
|             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) { | ||||
|                     val manga = migratingManga?.firstOrNull()?.searchResult?.get()?.let { | ||||
|                         db.getManga(it).executeOnIO() | ||||
| @@ -405,9 +406,10 @@ class MigrationListController(bundle: Bundle? = null) : | ||||
|                     if (manga != null) { | ||||
|                         val newStack = router.backstack.filter { | ||||
|                             it.controller() !is MangaController && | ||||
|                                 it.controller() !is MangaAllInOneController && | ||||
|                                 it.controller() !is MigrationListController && | ||||
|                                 it.controller() !is PreMigrationController | ||||
|                         } + MangaController(manga).withFadeTransaction() | ||||
|                         } + MangaController(manga).withFadeTransaction() // TODO MangaAllInOneController | ||||
|                         router.setBackstack(newStack, FadeChangeHandler()) | ||||
|                         return@launchUI | ||||
|                     } | ||||
|   | ||||
| @@ -7,10 +7,12 @@ import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.database.DatabaseHelper | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.data.glide.GlideApp | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.source.Source | ||||
| import eu.kanade.tachiyomi.source.SourceManager | ||||
| import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction | ||||
| import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import eu.kanade.tachiyomi.util.lang.launchUI | ||||
| import eu.kanade.tachiyomi.util.system.getResourceColor | ||||
| @@ -23,6 +25,8 @@ import kotlinx.android.synthetic.main.migration_manga_card.view.* | ||||
| import kotlinx.android.synthetic.main.migration_process_item.* | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.withContext | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import uy.kohesive.injekt.injectLazy | ||||
|  | ||||
| class MigrationProcessHolder( | ||||
| @@ -67,12 +71,21 @@ class MigrationProcessHolder( | ||||
|                 withContext(Dispatchers.Main) { | ||||
|                     migration_manga_card_from.attachManga(manga, source) | ||||
|                     migration_manga_card_from.setOnClickListener { | ||||
|                         adapter.controller.router.pushController( | ||||
|                             MangaController( | ||||
|                                 manga, | ||||
|                                 true | ||||
|                             ).withFadeTransaction() | ||||
|                         ) | ||||
|                         if (Injekt.get<PreferencesHelper>().eh_useNewMangaInterface().get()) { | ||||
|                             adapter.controller.router.pushController( | ||||
|                                 MangaAllInOneController( | ||||
|                                     manga, | ||||
|                                     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.database.models.History | ||||
| import eu.kanade.tachiyomi.data.database.models.Manga | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.databinding.HistoryControllerBinding | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NucleusController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.RootController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction | ||||
| import eu.kanade.tachiyomi.ui.browse.source.browse.ProgressItem | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import eu.kanade.tachiyomi.ui.reader.ReaderActivity | ||||
| import eu.kanade.tachiyomi.util.system.toast | ||||
| @@ -26,6 +28,8 @@ import kotlinx.coroutines.flow.filter | ||||
| import kotlinx.coroutines.flow.launchIn | ||||
| import kotlinx.coroutines.flow.onEach | ||||
| import reactivecircus.flowbinding.appcompat.queryTextChanges | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
|  | ||||
| /** | ||||
|  * Fragment that shows recently read manga. | ||||
| @@ -158,7 +162,11 @@ class HistoryController : | ||||
|  | ||||
|     override fun onItemClick(position: Int) { | ||||
|         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) { | ||||
|   | ||||
| @@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.R | ||||
| import eu.kanade.tachiyomi.data.download.model.Download | ||||
| import eu.kanade.tachiyomi.data.library.LibraryUpdateService | ||||
| import eu.kanade.tachiyomi.data.notification.Notifications | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.databinding.UpdatesControllerBinding | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController | ||||
| import eu.kanade.tachiyomi.ui.base.controller.NucleusController | ||||
| @@ -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.main.MainActivity | ||||
| import eu.kanade.tachiyomi.ui.main.offsetAppbarHeight | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import eu.kanade.tachiyomi.ui.reader.ReaderActivity | ||||
| import eu.kanade.tachiyomi.util.system.notificationManager | ||||
| @@ -33,6 +35,8 @@ import kotlinx.coroutines.flow.onEach | ||||
| import reactivecircus.flowbinding.recyclerview.scrollStateChanges | ||||
| import reactivecircus.flowbinding.swiperefreshlayout.refreshes | ||||
| import timber.log.Timber | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
|  | ||||
| /** | ||||
|  * Fragment that shows recent chapters. | ||||
| @@ -282,7 +286,11 @@ class UpdatesController : | ||||
|     } | ||||
|  | ||||
|     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 | ||||
|         switchPreference { | ||||
|             key = Keys.eh_expandFilters | ||||
|             title = "Expand all search filters by default" | ||||
|             defaultValue = false | ||||
|         } | ||||
|         preferenceCategory { | ||||
|             title = "EH Settings" | ||||
|  | ||||
|         switchPreference { | ||||
|             key = Keys.eh_autoSolveCaptchas | ||||
|             title = "Automatically solve captcha" | ||||
|             summary = | ||||
|                 "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 { | ||||
|                 key = Keys.eh_expandFilters | ||||
|                 title = "Expand all search filters by default" | ||||
|                 defaultValue = false | ||||
|             } | ||||
|  | ||||
|             switchPreference { | ||||
|                 key = Keys.eh_lock_manually | ||||
|  | ||||
|                 title = "Lock manually only" | ||||
|                 key = Keys.eh_autoSolveCaptchas | ||||
|                 title = "Automatically solve captcha" | ||||
|                 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 | ||||
|             } | ||||
|  | ||||
|             switchPreference { | ||||
|                 key = Keys.secureScreen | ||||
|                 title = "Enable Secure Screen" | ||||
|                 defaultValue = false | ||||
|                 key = Keys.eh_use_new_manga_interface | ||||
|                 title = "Use New Manga Interface" | ||||
|                 summary = "Use new all in one manga interface" | ||||
|                 defaultValue = true | ||||
|             } | ||||
|             switchPreference { | ||||
|                 key = Keys.hideNotificationContent | ||||
|                 titleRes = R.string.hide_notification_content | ||||
|                 defaultValue = false | ||||
|             } | ||||
|         }*/ | ||||
|         } | ||||
|         // <-- EXH | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import android.os.Bundle | ||||
| import android.view.LayoutInflater | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import eu.kanade.tachiyomi.data.preference.PreferencesHelper | ||||
| import eu.kanade.tachiyomi.databinding.SmartSearchBinding | ||||
| import eu.kanade.tachiyomi.source.CatalogueSource | ||||
| 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.browse.source.SourceController | ||||
| import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaAllInOneController | ||||
| import eu.kanade.tachiyomi.ui.manga.MangaController | ||||
| import eu.kanade.tachiyomi.util.system.toast | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
| @@ -20,6 +22,8 @@ import kotlinx.coroutines.NonCancellable | ||||
| import kotlinx.coroutines.cancel | ||||
| import kotlinx.coroutines.launch | ||||
| import kotlinx.coroutines.withContext | ||||
| import uy.kohesive.injekt.Injekt | ||||
| import uy.kohesive.injekt.api.get | ||||
| import uy.kohesive.injekt.injectLazy | ||||
|  | ||||
| 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) { | ||||
|                 withContext(NonCancellable) { | ||||
|                     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) { | ||||
|                             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> | ||||
		Reference in New Issue
	
	Block a user