diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index 37a1963443..231c5b00ad 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -77,7 +77,6 @@ import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.base.controller.DialogController import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder -import eu.kanade.tachiyomi.ui.source.SourceController import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog import eu.kanade.tachiyomi.ui.library.LibraryController import eu.kanade.tachiyomi.ui.main.MainActivity @@ -90,6 +89,7 @@ import eu.kanade.tachiyomi.ui.manga.track.TrackingBottomSheet import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate +import eu.kanade.tachiyomi.ui.source.SourceController import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.system.ThemeUtil @@ -169,30 +169,16 @@ class MangaDetailsController : BaseController, var refreshTracker: Int? = null private var textAnim: ViewPropertyAnimator? = null private var scrollAnim: ViewPropertyAnimator? = null - var isTablet = false var chapterPopupMenu: Pair? = null // Tablet Layout + var isTablet = false var tabletRecycler: RecyclerView? = null - - /** - * Adapter containing a list of chapters. - */ private var tabletAdapter: MangaDetailsAdapter? = null - /** - * Library search query. - */ private var query = "" - - /** - * Adapter containing a list of chapters. - */ private var adapter: MangaDetailsAdapter? = null - /** - * Action mode for selections. - */ private var actionMode: ActionMode? = null // Hold a reference to the current animator, so that it can be canceled mid-way. @@ -201,10 +187,6 @@ class MangaDetailsController : BaseController, var showScroll = false var headerHeight = 0 - init { - setHasOptionsMenu(true) - } - override fun getTitle(): String? { return if (toolbarIsColored && !isTablet) manga?.title else null } @@ -213,12 +195,33 @@ class MangaDetailsController : BaseController, return inflater.inflate(R.layout.manga_details_controller, container, false) } + //region UI Methods override fun onViewCreated(view: View) { super.onViewCreated(view) coverColor = null - // Init RecyclerView and adapter - adapter = MangaDetailsAdapter(this, view.context) + presenter.onCreate() + setTabletMode(view) + setRecycler(view) + setPaletteColor() + setFastScroller() + + swipe_refresh.isRefreshing = presenter.isLoading + if (manga?.initialized != true) + swipe_refresh.post { swipe_refresh.isRefreshing = true } + swipe_refresh.setOnRefreshListener { presenter.refreshAll() } + } + + override fun onDestroyView(view: View) { + snack?.dismiss() + presenter.onDestroy() + adapter = null + trackingBottomSheet = null + super.onDestroyView(view) + } + + /** Check if device is tablet, and create a second recycler to hold the details header if so */ + private fun setTabletMode(view: View) { isTablet = isTabletSize() if (isTablet) { tabletRecycler = RecyclerView(view.context) @@ -240,6 +243,11 @@ class MangaDetailsController : BaseController, width = 1.dpToPx } } + } + + /** Set adapter, insets, and scroll listener for recycler view */ + private fun setRecycler(view: View) { + adapter = MangaDetailsAdapter(this, view.context) recycler.adapter = adapter adapter?.isSwipeEnabled = true @@ -273,8 +281,6 @@ class MangaDetailsController : BaseController, v.updatePaddingRelative(bottom = insets.systemWindowInsetBottom) } - presenter.onCreate() - fast_scroller.translationX = if (showScroll || isTablet) 0f else 25f.dpToPx recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) @@ -349,8 +355,10 @@ class MangaDetailsController : BaseController, } } }) - setPaletteColor() + } + private fun setFastScroller() { + fast_scroller.translationX = if (showScroll || isTablet) 0f else 25f.dpToPx fast_scroller.setupWithRecyclerView(recycler, { position -> val letter = adapter?.getSectionText(position) when { @@ -382,14 +390,9 @@ class MangaDetailsController : BaseController, colorToolbar(itemPosition > 0, false) } } - - swipe_refresh.isRefreshing = presenter.isLoading - if (manga?.initialized != true) - swipe_refresh.post { swipe_refresh.isRefreshing = true } - - swipe_refresh.setOnRefreshListener { presenter.refreshAll() } } + /** Set the toolbar to fully transparent or colored and translucent */ fun colorToolbar(isColor: Boolean, animate: Boolean = true) { if (isColor == toolbarIsColored) return toolbarIsColored = isColor @@ -428,6 +431,7 @@ class MangaDetailsController : BaseController, } } + /** Get the color of the manga cover based on the current theme */ fun setPaletteColor() { val view = view ?: return GlideApp.with(view.context).load(manga) @@ -466,31 +470,49 @@ class MangaDetailsController : BaseController, }) } - override fun onActivityResumed(activity: Activity) { - super.onActivityResumed(activity) - presenter.isLockedFromSearch = SecureActivityDelegate.shouldBeLocked() - presenter.headerItem.isLocked = presenter.isLockedFromSearch - presenter.fetchChapters(refreshTracker == null) - if (refreshTracker != null) { - trackingBottomSheet?.refreshItem(refreshTracker ?: 0) - presenter.refreshTrackers() - refreshTracker = null - } - val isCurrentController = router?.backstack?.lastOrNull()?.controller() == - this - if (isCurrentController) { - setStatusBarAndToolbar() + /** Set toolbar theme for themes that are inverted (ie. light blue theme) */ + private fun setActionBar(forThis: Boolean) { + val currentNightMode = + activity!!.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + // if the theme is using inverted toolbar color + if (currentNightMode == Configuration.UI_MODE_NIGHT_NO && ThemeUtil.isBlueTheme( + presenter.preferences.theme() + ) + ) { + if (forThis) (activity as MainActivity).appbar.context.setTheme( + R.style.ThemeOverlay_AppCompat_DayNight_ActionBar + ) + else (activity as MainActivity).appbar.context.setTheme( + R.style.Theme_ActionBar_Dark_DayNight + ) + + val iconPrimary = view?.context?.getResourceColor( + if (forThis) android.R.attr.textColorPrimary + else R.attr.actionBarTintColor + ) ?: Color.BLACK + (activity as MainActivity).toolbar.setTitleTextColor(iconPrimary) + (activity as MainActivity).drawerArrow?.color = iconPrimary + (activity as MainActivity).toolbar.overflowIcon?.setTint(iconPrimary) + if (forThis) activity!!.main_content.systemUiVisibility = + activity!!.main_content.systemUiVisibility.or( + View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + ) + else activity!!.main_content.systemUiVisibility = + activity!!.main_content.systemUiVisibility.rem( + View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + ) } } - fun showError(message: String) { - swipe_refresh?.isRefreshing = presenter.isLoading - view?.snack(message) - } - - fun updateChapterDownload(download: Download) { - getHolder(download.chapter)?.notifyStatus(download.status, presenter.isLockedFromSearch, - download.progress) + private fun setStatusBarAndToolbar() { + activity?.window?.statusBarColor = if (toolbarIsColored) { + val translucentColor = ColorUtils.setAlphaComponent(coverColor ?: Color.TRANSPARENT, 175) + (activity as MainActivity).toolbar.setBackgroundColor(translucentColor) + translucentColor + } else Color.TRANSPARENT + (activity as MainActivity).appbar.setBackgroundColor(Color.TRANSPARENT) + (activity as MainActivity).toolbar.setBackgroundColor(activity?.window?.statusBarColor + ?: Color.TRANSPARENT) } private fun isTabletSize(): Boolean { @@ -511,15 +533,26 @@ class MangaDetailsController : BaseController, activity.windowManager?.defaultDisplay?.getMetrics(displayMetrics) return displayMetrics.heightPixels.pxToDp >= 720 } + //endregion - private fun getHolder(chapter: Chapter): ChapterHolder? { - return recycler?.findViewHolderForItemId(chapter.id!!) as? ChapterHolder + //region Lifecycle methods + override fun onActivityResumed(activity: Activity) { + super.onActivityResumed(activity) + presenter.isLockedFromSearch = SecureActivityDelegate.shouldBeLocked() + presenter.headerItem.isLocked = presenter.isLockedFromSearch + presenter.fetchChapters(refreshTracker == null) + if (refreshTracker != null) { + trackingBottomSheet?.refreshItem(refreshTracker ?: 0) + presenter.refreshTrackers() + refreshTracker = null + } + val isCurrentController = router?.backstack?.lastOrNull()?.controller() == + this + if (isCurrentController) { + setStatusBarAndToolbar() + } } - private fun getHeader(): MangaHeaderHolder? { - return if (isTablet) tabletRecycler?.findViewHolderForAdapterPosition(0) as? MangaHeaderHolder - else recycler.findViewHolderForAdapterPosition(0) as? MangaHeaderHolder - } override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { super.onChangeStarted(handler, type) @@ -546,43 +579,39 @@ class MangaDetailsController : BaseController, } } - private fun setActionBar(forThis: Boolean) { - val currentNightMode = - activity!!.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK - // if the theme is using inverted toolbar color - if (currentNightMode == Configuration.UI_MODE_NIGHT_NO && ThemeUtil.isBlueTheme( - presenter.preferences.theme() - ) - ) { - if (forThis) - (activity as MainActivity).appbar.context.setTheme(R.style - .ThemeOverlay_AppCompat_DayNight_ActionBar) - else - (activity as MainActivity).appbar.context.setTheme(R.style - .Theme_ActionBar_Dark_DayNight) - - val iconPrimary = view?.context?.getResourceColor( - if (forThis) android.R.attr.textColorPrimary - else R.attr.actionBarTintColor - ) ?: Color.BLACK - (activity as MainActivity).toolbar.setTitleTextColor(iconPrimary) - (activity as MainActivity).drawerArrow?.color = iconPrimary - (activity as MainActivity).toolbar.overflowIcon?.setTint(iconPrimary) - if (forThis) activity!!.main_content.systemUiVisibility = - activity!!.main_content.systemUiVisibility.or( - View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - ) - else activity!!.main_content.systemUiVisibility = - activity!!.main_content.systemUiVisibility.rem( - View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - ) + override fun handleBack(): Boolean { + if (manga_cover_full?.visibility == View.VISIBLE) { + manga_cover_full?.performClick() + return true } + return super.handleBack() + } + //endregion + + fun showError(message: String) { + swipe_refresh?.isRefreshing = presenter.isLoading + view?.snack(message) } fun setRefresh(enabled: Boolean) { swipe_refresh.isRefreshing = enabled } + //region Recycler methods + fun updateChapterDownload(download: Download) { + getHolder(download.chapter)?.notifyStatus(download.status, presenter.isLockedFromSearch, + download.progress) + } + + private fun getHolder(chapter: Chapter): ChapterHolder? { + return recycler?.findViewHolderForItemId(chapter.id!!) as? ChapterHolder + } + + private fun getHeader(): MangaHeaderHolder? { + return if (isTablet) tabletRecycler?.findViewHolderForAdapterPosition(0) as? MangaHeaderHolder + else recycler.findViewHolderForAdapterPosition(0) as? MangaHeaderHolder + } + fun updateHeader() { swipe_refresh?.isRefreshing = presenter.isLoading adapter?.setChapters(presenter.chapters) @@ -601,6 +630,18 @@ class MangaDetailsController : BaseController, activity?.invalidateOptionsMenu() } + private fun addMangaHeader() { + if (tabletAdapter?.scrollableHeaders?.isEmpty() == true) { + tabletAdapter?.removeAllScrollableHeaders() + tabletAdapter?.addScrollableHeader(presenter.headerItem) + adapter?.removeAllScrollableHeaders() + adapter?.addScrollableHeader(presenter.tabletChapterHeaderItem!!) + } else if (!isTablet && adapter?.scrollableHeaders?.isEmpty() == true) { + adapter?.removeAllScrollableHeaders() + adapter?.addScrollableHeader(presenter.headerItem) + } + } + fun refreshAdapter() = adapter?.notifyDataSetChanged() override fun onItemClick(view: View?, position: Int): Boolean { @@ -659,6 +700,18 @@ class MangaDetailsController : BaseController, popup.show() } + override fun onActionStateChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { + swipe_refresh.isEnabled = actionState != ItemTouchHelper.ACTION_STATE_SWIPE + } + + override fun onItemMove(fromPosition: Int, toPosition: Int) { + } + + override fun shouldMoveItem(fromPosition: Int, toPosition: Int): Boolean { + return true + } + //endregion + fun dismissPopup(position: Int) { if (chapterPopupMenu != null && chapterPopupMenu?.first == position) { chapterPopupMenu?.second?.dismiss() @@ -739,14 +792,7 @@ class MangaDetailsController : BaseController, startActivity(intent) } - override fun onDestroyView(view: View) { - snack?.dismiss() - presenter.onDestroy() - adapter = null - trackingBottomSheet = null - super.onDestroyView(view) - } - + //region action bar menu methods override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { inflater.inflate(R.menu.manga_details, menu) val editItem = menu.findItem(R.id.action_edit) @@ -847,10 +893,8 @@ class MangaDetailsController : BaseController, } return true } + //endregion - /** - * Called to run Intent with [Intent.ACTION_SEND], which show share dialog. - */ override fun prepareToShareManga() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && coverDrawable != null) GlideApp.with(activity!!).asBitmap().load(presenter.manga).into(object : @@ -867,9 +911,6 @@ class MangaDetailsController : BaseController, else shareManga() } - /** - * Called to run Intent with [Intent.ACTION_SEND], which show share dialog. - */ fun shareManga(cover: File? = null) { val context = view?.context ?: return @@ -947,6 +988,18 @@ class MangaDetailsController : BaseController, } } + private fun isLocked(): Boolean { + if (presenter.isLockedFromSearch) { + SecureActivityDelegate.promptLockIfNeeded(activity) + return true + } + return false + } + + //region Interface methods + override fun coverColor(): Int? = coverColor + override fun topCoverHeight(): Int = headerHeight + override fun startDownloadNow(position: Int) { val chapter = (adapter?.getItem(position) as? ChapterItem) ?: return presenter.startDownloadingNow(chapter) @@ -979,9 +1032,6 @@ class MangaDetailsController : BaseController, onItemClick(null, position) } - override fun coverColor(): Int? = coverColor - override fun topCoverHeight(): Int = headerHeight - override fun readNextChapter() { if (activity is SearchActivity && presenter.isLockedFromSearch) { SecureActivityDelegate.promptLockIfNeeded(activity) @@ -1032,14 +1082,6 @@ class MangaDetailsController : BaseController, ChaptersSortBottomSheet(this).show() } - private fun isLocked(): Boolean { - if (presenter.isLockedFromSearch) { - SecureActivityDelegate.promptLockIfNeeded(activity) - return true - } - return false - } - override fun favoriteManga(longPress: Boolean) { if (isLocked()) return val manga = presenter.manga @@ -1158,21 +1200,15 @@ class MangaDetailsController : BaseController, snack = view.snack(view.context.getString(R.string._copied_to_clipboard, contentType)) } - override fun handleBack(): Boolean { - if (manga_cover_full?.visibility == View.VISIBLE) { - manga_cover_full?.performClick() - return true - } - return super.handleBack() - } - override fun showTrackingSheet() { if (isLocked()) return trackingBottomSheet = TrackingBottomSheet(this) trackingBottomSheet?.show() } + //endregion + //region Tracking methods fun refreshTracking(trackings: List) { trackingBottomSheet?.onNextTrackings(trackings) } @@ -1198,10 +1234,9 @@ class MangaDetailsController : BaseController, Timber.e(error) trackingBottomSheet?.onSearchResultsError(error) } + //endregion - /** - * Creates the action mode if it's not created already. - */ + //region Action mode methods private fun createActionModeIfNeeded() { if (actionMode == null) { actionMode = (activity as AppCompatActivity).startSupportActionMode(this) @@ -1231,6 +1266,12 @@ class MangaDetailsController : BaseController, return true } + override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean { + mode?.title = view?.context?.getString(if (startingDLChapterPos == null) + R.string.select_starting_chapter else R.string.select_ending_chapter) + return false + } + override fun onDestroyActionMode(mode: ActionMode?) { actionMode = null setStatusBarAndToolbar() @@ -1245,38 +1286,7 @@ class MangaDetailsController : BaseController, adapter?.clearSelection() return } - - /** - * Called to set the last used catalogue at the top of the view. - */ - private fun addMangaHeader() { - if (tabletAdapter?.scrollableHeaders?.isEmpty() == true) { - tabletAdapter?.removeAllScrollableHeaders() - tabletAdapter?.addScrollableHeader(presenter.headerItem) - adapter?.removeAllScrollableHeaders() - adapter?.addScrollableHeader(presenter.tabletChapterHeaderItem!!) - } else if (!isTablet && adapter?.scrollableHeaders?.isEmpty() == true) { - adapter?.removeAllScrollableHeaders() - adapter?.addScrollableHeader(presenter.headerItem) - } - } - - private fun setStatusBarAndToolbar() { - activity?.window?.statusBarColor = if (toolbarIsColored) { - val translucentColor = ColorUtils.setAlphaComponent(coverColor ?: Color.TRANSPARENT, 175) - (activity as MainActivity).toolbar.setBackgroundColor(translucentColor) - translucentColor - } else Color.TRANSPARENT - (activity as MainActivity).appbar.setBackgroundColor(Color.TRANSPARENT) - (activity as MainActivity).toolbar.setBackgroundColor(activity?.window?.statusBarColor - ?: Color.TRANSPARENT) - } - - override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean { - mode?.title = view?.context?.getString(if (startingDLChapterPos == null) - R.string.select_starting_chapter else R.string.select_ending_chapter) - return false - } + //endregion fun changeCover() { if (manga?.favorite == true) { @@ -1310,17 +1320,6 @@ class MangaDetailsController : BaseController, } } - override fun onActionStateChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { - swipe_refresh.isEnabled = actionState != ItemTouchHelper.ACTION_STATE_SWIPE - } - - override fun onItemMove(fromPosition: Int, toPosition: Int) { - } - - override fun shouldMoveItem(fromPosition: Int, toPosition: Int): Boolean { - return true - } - override fun zoomImageFromThumb(thumbView: View) { // If there's an animation in progress, cancel it immediately and proceed with this one. currentAnimator?.cancel()