diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index f1ca6e4cf..5fec069c7 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -73,7 +73,7 @@ object PreferenceKeys { const val lastUsedCategory = "last_used_category" - const val catalogueAsList = "pref_display_catalogue_as_list" + const val catalogueViewSetting = "pref_display_catalogue_view_setting" const val enabledLanguages = "source_languages" @@ -137,7 +137,7 @@ object PreferenceKeys { const val downloadNewCategories = "download_new_categories" - const val libraryAsList = "pref_display_library_as_list" + const val libraryViewSetting = "pref_display_library_view_setting" const val lang = "app_language" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 8cd43302f..45b3073fc 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -143,7 +143,7 @@ class PreferencesHelper(val context: Context) { fun lastVersionCode() = flowPrefs.getInt("last_version_code", 0) - fun catalogueAsList() = rxPrefs.getBoolean(Keys.catalogueAsList, false) + fun catalogueViewSetting() = flowPrefs.getInt(Keys.catalogueViewSetting, 0) fun enabledLanguages() = flowPrefs.getStringSet(Keys.enabledLanguages, setOf("all", "en", Locale.getDefault().language)) @@ -191,7 +191,7 @@ class PreferencesHelper(val context: Context) { fun libraryUpdatePrioritization() = flowPrefs.getInt(Keys.libraryUpdatePrioritization, 0) - fun libraryAsList() = flowPrefs.getBoolean(Keys.libraryAsList, false) + fun libraryViewSetting() = flowPrefs.getInt(Keys.libraryViewSetting, 0) fun downloadBadge() = flowPrefs.getBoolean(Keys.downloadBadge, false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt index 7f69c989f..5222c8211 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt @@ -314,7 +314,7 @@ open class BrowseSourceController(bundle: Bundle) : binding.catalogueView.removeView(oldRecycler) } - val recycler = if (presenter.isListMode) { + val recycler = if (presenter.mode == 1) { RecyclerView(view.context).apply { id = R.id.recycler layoutManager = LinearLayoutManager(context) @@ -332,7 +332,7 @@ open class BrowseSourceController(bundle: Bundle) : (layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { override fun getSpanSize(position: Int): Int { return when (adapter?.getItemViewType(position)) { - R.layout.source_grid_item, null -> 1 + R.layout.source_grid_item, R.layout.source_comfortable_grid_item, null -> 1 else -> spanCount } } @@ -399,10 +399,10 @@ open class BrowseSourceController(bundle: Bundle) : // Show next display mode menu.findItem(R.id.action_display_mode).apply { - val icon = if (presenter.isListMode) { - R.drawable.ic_view_module_24dp - } else { + val icon = if (presenter.mode == 0) { R.drawable.ic_view_list_24dp + } else { + R.drawable.ic_view_module_24dp } setIcon(icon) } @@ -583,7 +583,7 @@ open class BrowseSourceController(bundle: Bundle) : val adapter = adapter ?: return presenter.swapDisplayMode() - val isListMode = presenter.isListMode + val isListMode = presenter.mode == 1 activity?.invalidateOptionsMenu() setupRecycler(view) if (!isListMode || !view.context.connectivityManager.isActiveNetworkMetered) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt index 5a8f0fe90..d41c6b416 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt @@ -37,6 +37,12 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem import eu.kanade.tachiyomi.util.removeCovers import exh.EXHSavedSearch import java.lang.RuntimeException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.subscribe import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers @@ -100,7 +106,7 @@ open class BrowseSourcePresenter( /** * Whether the view is in list mode or not. */ - var isListMode: Boolean = false + var mode: Int = 0 private set /** @@ -118,6 +124,11 @@ open class BrowseSourcePresenter( */ private var initializerSubscription: Subscription? = null + /** + * Scope to watch the view setting + */ + private val scope = CoroutineScope(Job() + Dispatchers.Default) + override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) @@ -129,10 +140,9 @@ open class BrowseSourcePresenter( query = savedState.getString(::query.name, "") } - add( - prefs.catalogueAsList().asObservable() - .subscribe { setDisplayMode(it) } - ) + prefs.catalogueViewSetting().asFlow() + .onEach { setDisplayMode(it) } + .launchIn(scope) restartPager() } @@ -161,7 +171,7 @@ open class BrowseSourcePresenter( val sourceId = source.id - val catalogueAsList = prefs.catalogueAsList() + val catalogueAsList = prefs.catalogueViewSetting() // Prepare the pager. pagerSubscription?.let { remove(it) } @@ -210,10 +220,10 @@ open class BrowseSourcePresenter( /** * Sets the display mode. * - * @param asList whether the current mode is in list or not. + * @param mode whether the current mode is in list or not. */ - private fun setDisplayMode(asList: Boolean) { - isListMode = asList + private fun setDisplayMode(mode: Int) { + this.mode = mode subscribeToMangaInitializer() } @@ -302,7 +312,13 @@ open class BrowseSourcePresenter( * Changes the active display mode. */ fun swapDisplayMode() { - prefs.catalogueAsList().set(!isListMode) + prefs.catalogueViewSetting().set( + when (mode) { + 0 -> 1 + 1 -> 2 + else -> 0 + } + ) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceComfortableGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceComfortableGridHolder.kt new file mode 100644 index 000000000..055061d42 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceComfortableGridHolder.kt @@ -0,0 +1,56 @@ +package eu.kanade.tachiyomi.ui.browse.source.browse + +import android.view.View +import com.bumptech.glide.load.engine.DiskCacheStrategy +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.glide.GlideApp +import eu.kanade.tachiyomi.data.glide.toMangaThumbnail +import eu.kanade.tachiyomi.widget.StateImageViewTarget +import kotlinx.android.synthetic.main.source_comfortable_grid_item.card +import kotlinx.android.synthetic.main.source_comfortable_grid_item.progress +import kotlinx.android.synthetic.main.source_comfortable_grid_item.thumbnail +import kotlinx.android.synthetic.main.source_comfortable_grid_item.title + +/** + * Class used to hold the displayed data of a manga in the catalogue, like the cover or the title. + * All the elements from the layout file "item_source_grid" are available in this class. + * + * @param view the inflated view for this holder. + * @param adapter the adapter handling this holder. + * @constructor creates a new catalogue holder. + */ +class SourceComfortableGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) : + SourceHolder(view, adapter) { + + /** + * Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this + * holder with the given manga. + * + * @param manga the manga to bind. + */ + override fun onSetValues(manga: Manga) { + // Set manga title + title.text = manga.title + + // Set alpha of thumbnail. + thumbnail.alpha = if (manga.favorite) 0.3f else 1.0f + + setImage(manga) + } + + override fun setImage(manga: Manga) { + // Setting this via XML doesn't work + card.clipToOutline = true + + GlideApp.with(view.context).clear(thumbnail) + if (!manga.thumbnail_url.isNullOrEmpty()) { + GlideApp.with(view.context) + .load(manga.toMangaThumbnail()) + .diskCacheStrategy(DiskCacheStrategy.DATA) + .centerCrop() + .placeholder(android.R.color.transparent) + .into(StateImageViewTarget(thumbnail, progress)) + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt index e3f89c896..da443a6e8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/SourceItem.kt @@ -5,25 +5,24 @@ import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.widget.FrameLayout import androidx.recyclerview.widget.RecyclerView -import com.f2prateek.rx.preferences.Preference +import com.tfcporciuncula.flow.Preference import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.widget.AutofitRecyclerView import kotlinx.android.synthetic.main.source_grid_item.view.card import kotlinx.android.synthetic.main.source_grid_item.view.gradient -class SourceItem(val manga: Manga, private val catalogueAsList: Preference) : +class SourceItem(val manga: Manga, private val catalogueAsList: Preference) : AbstractFlexibleItem() { override fun getLayoutRes(): Int { - return if (catalogueAsList.getOrDefault()) { - R.layout.source_list_item - } else { - R.layout.source_grid_item + return when (catalogueAsList.get()) { + 0 -> R.layout.source_grid_item + 1 -> R.layout.source_list_item + else -> R.layout.source_comfortable_grid_item } } @@ -33,15 +32,28 @@ class SourceItem(val manga: Manga, private val catalogueAsList: Preference> +) : LibraryHolder(view, adapter) { + + /** + * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this + * holder with the given manga. + * + * @param item the manga item to bind. + */ + override fun onSetValues(item: LibraryItem) { + // Update the title of the manga. + title.text = item.manga.title + + // Update the unread count and its visibility. + with(unread_text) { + visibleIf { item.unreadCount > 0 } + text = item.unreadCount.toString() + } + // Update the download count and its visibility. + with(download_text) { + visibleIf { item.downloadCount > 0 } + text = item.downloadCount.toString() + } + // set local visibility if its local manga + local_text.visibleIf { item.manga.isLocal() } + + // Setting this via XML doesn't work + card.clipToOutline = true + + // Update the cover. + GlideApp.with(view.context).clear(thumbnail) + GlideApp.with(view.context) + .load(item.manga.toMangaThumbnail()) + .diskCacheStrategy(DiskCacheStrategy.RESOURCE) + .centerCrop() + .dontAnimate() + .into(thumbnail) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt index ab43eee46..725453a43 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt @@ -3,7 +3,10 @@ package eu.kanade.tachiyomi.ui.library import android.view.Gravity import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.FrameLayout +import android.widget.ImageView +import androidx.constraintlayout.widget.ConstraintLayout import androidx.recyclerview.widget.RecyclerView import com.tfcporciuncula.flow.Preference import eu.davidea.flexibleadapter.FlexibleAdapter @@ -16,13 +19,17 @@ import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.tachiyomi.data.database.models.Track import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.widget.AutofitRecyclerView +import exh.util.updateLayoutParams +import kotlinx.android.synthetic.main.source_comfortable_grid_item.view.constraint_layout import kotlinx.android.synthetic.main.source_grid_item.view.card import kotlinx.android.synthetic.main.source_grid_item.view.gradient +import kotlinx.android.synthetic.main.source_grid_item.view.thumbnail import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get -class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference) : +class LibraryItem(val manga: LibraryManga, private val libraryViewSetting: Preference) : AbstractFlexibleItem(), IFilterable { private val sourceManager: SourceManager = Injekt.get() @@ -33,24 +40,58 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference var unreadCount = -1 override fun getLayoutRes(): Int { - return if (libraryAsList.get()) { - R.layout.source_list_item - } else { - R.layout.source_grid_item + return when (libraryViewSetting.get()) { + 0 -> R.layout.source_grid_item + 1 -> R.layout.source_list_item + else -> R.layout.source_comfortable_grid_item } } override fun createViewHolder(view: View, adapter: FlexibleAdapter>): LibraryHolder { val parent = adapter.recyclerView return if (parent is AutofitRecyclerView) { - view.apply { - val coverHeight = parent.itemWidth / 3 * 4 - card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight) - gradient.layoutParams = FrameLayout.LayoutParams( - MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM - ) + if (libraryViewSetting.get() == 0) { + view.apply { + val coverHeight = parent.itemWidth / 3 * 4 + card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight) + gradient.layoutParams = FrameLayout.LayoutParams( + MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM + ) + } + LibraryGridHolder(view, adapter) + } else { + view.apply { + val coverHeight = parent.itemWidth / 3 * 4 + + gradient.layoutParams = FrameLayout.LayoutParams( + MATCH_PARENT, + coverHeight * (66 / 100), + Gravity.BOTTOM + ) + card.updateLayoutParams { + bottomMargin = 6.dpToPx + } + + constraint_layout.layoutParams = FrameLayout.LayoutParams( + MATCH_PARENT, WRAP_CONTENT + ) + thumbnail.maxHeight = Int.MAX_VALUE + thumbnail.minimumHeight = 0 + constraint_layout.minHeight = 0 + thumbnail.scaleType = ImageView.ScaleType.CENTER_CROP + thumbnail.adjustViewBounds = false + thumbnail.layoutParams = FrameLayout.LayoutParams( + MATCH_PARENT, + (parent.itemWidth / 3f * 3.7f).toInt() + ) + // .layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight) + + // gradient.layoutParams = FrameLayout.LayoutParams( + // MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM + // ) + } + LibraryComfortableGridHolder(view, adapter) } - LibraryGridHolder(view, adapter) } else { LibraryListHolder(view, adapter) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index 392e044e3..b9c0e6036 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -301,7 +301,7 @@ class LibraryPresenter( * value. */ private fun getLibraryMangasObservable(): Observable { - val libraryAsList = preferences.libraryAsList() + val libraryAsList = preferences.libraryViewSetting() return db.getLibraryMangas().asRxObservable() .map { list -> list.map { LibraryItem(it, libraryAsList) }.groupBy { it.manga.category } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt index 50a999f0f..1ff5bc9c0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt @@ -220,16 +220,18 @@ class LibrarySettingsSheet( inner class DisplayGroup : Group { private val grid = Item.Radio(R.string.action_display_grid, this) + private val comfortableGrid = Item.Radio(R.string.action_display_comfortable_grid, this) private val list = Item.Radio(R.string.action_display_list, this) override val header = null - override val items = listOf(grid, list) + override val items = listOf(grid, comfortableGrid, list) override val footer = null override fun initModels() { - val asList = preferences.libraryAsList().get() - grid.checked = !asList - list.checked = asList + val mode = preferences.libraryViewSetting().get() + grid.checked = mode == 0 + list.checked = mode == 1 + comfortableGrid.checked = mode == 2 } override fun onItemClicked(item: Item) { @@ -239,7 +241,13 @@ class LibrarySettingsSheet( item.group.items.forEach { (it as Item.Radio).checked = false } item.checked = true - preferences.libraryAsList().set(item == list) + preferences.libraryViewSetting().set( + when (item) { + grid -> 0 + list -> 1 + else -> 2 + } + ) item.group.items.forEach { adapter.notifyItemChanged(it) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaAllInOneController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaAllInOneController.kt index 9b3cb5442..4daa5413b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaAllInOneController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaAllInOneController.kt @@ -5,6 +5,7 @@ import android.animation.AnimatorListenerAdapter import android.app.Activity import android.content.Intent import android.os.Bundle +import android.util.Log import android.view.LayoutInflater import android.view.Menu import android.view.MenuInflater @@ -178,9 +179,11 @@ class MangaAllInOneController : } override fun createPresenter(): MangaAllInOnePresenter { + Log.d("Adapter", "Tester8") return MangaAllInOnePresenter( this, manga!!, source!!, smartSearchConfig ) + Log.d("Adapter", "Tester9") } override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { @@ -198,6 +201,8 @@ class MangaAllInOneController : .onEach { fetchMangaFromSource(manualFetch = true) } .launchIn(scope) + Log.d("Controller", "Tester1") + // Init RecyclerView and adapter adapter = MangaAllInOneAdapter(this, view.context) @@ -206,6 +211,7 @@ class MangaAllInOneController : binding.recycler.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)) binding.recycler.setHasFixedSize(true) adapter?.fastScroller = binding.fastScroller + Log.d("Adapter", "Tester2") binding.fab.clicks() .onEach { @@ -233,22 +239,27 @@ class MangaAllInOneController : binding.actionToolbar.offsetAppbarHeight(activity!!) binding.fab.offsetAppbarHeight(activity!!) + Log.d("Adapter", "Tester3") } private fun getHeader(): MangaAllInOneHolder? { + Log.d("Adapter", "Tester4") return binding.recycler.findViewHolderForAdapterPosition(0) as? MangaAllInOneHolder + Log.d("Adapter", "Tester5") } private fun addMangaHeader() { + Log.d("Adapter", "Tester6") if (adapter?.scrollableHeaders?.isEmpty() == true) { adapter?.removeAllScrollableHeaders() adapter?.addScrollableHeader(presenter.headerItem) } + Log.d("Adapter", "Tester7") } // EXH --> override fun openSmartSearch() { - val smartSearchConfig = SourceController.SmartSearchConfig(presenter.manga.title, presenter.manga.id!!) + val smartSearchConfig = SourceController.SmartSearchConfig(presenter.manga.title, presenter.manga.id) router?.pushController( SourceController( @@ -329,12 +340,16 @@ class MangaAllInOneController : override fun onNextManga(manga: Manga, source: Source, chapters: List, lastUpdateDate: Date, chapterCount: Float) { if (manga.initialized) { // Update view. + Log.d("Controller", "Tester1") setMangaInfo(manga, source, chapters, lastUpdateDate, chapterCount) + Log.d("Controller", "Tester2") if (fromSource && !presenter.hasRequested && chapters.isNullOrEmpty()) { + Log.d("Controller", "Tester3") fetchMangaFromSource(fetchManga = false) } } else { // Initialize manga. + Log.d("Controller", "Tester4") fetchMangaFromSource() } } diff --git a/app/src/main/res/layout/source_comfortable_grid_item.xml b/app/src/main/res/layout/source_comfortable_grid_item.xml new file mode 100644 index 000000000..971e944ca --- /dev/null +++ b/app/src/main/res/layout/source_comfortable_grid_item.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings_extra.xml b/app/src/main/res/values/strings_extra.xml index d2ed473e6..a8d8acacf 100644 --- a/app/src/main/res/values/strings_extra.xml +++ b/app/src/main/res/values/strings_extra.xml @@ -15,6 +15,7 @@ Search manually Migrate now Copy now + Comfortable grid