Improve explore screen

This commit is contained in:
Koitharu
2022-07-07 17:50:05 +03:00
parent 6df56c2d77
commit 242704f853
17 changed files with 266 additions and 65 deletions

View File

@@ -22,10 +22,12 @@ import org.koitharu.kotatsu.history.ui.HistoryActivity
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.search.ui.MangaListActivity
import org.koitharu.kotatsu.settings.SettingsActivity
import org.koitharu.kotatsu.suggestions.ui.SuggestionsActivity
class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
RecyclerViewOwner,
ExploreListEventListener, OnListItemClickListener<ExploreItem.Source> {
ExploreListEventListener,
OnListItemClickListener<ExploreItem.Source> {
private val viewModel by viewModel<ExploreViewModel>()
private var exploreAdapter: ExploreAdapter? = null
@@ -78,6 +80,7 @@ class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
R.id.button_history -> HistoryActivity.newIntent(v.context)
R.id.button_local -> MangaListActivity.newIntent(v.context, MangaSource.LOCAL)
R.id.button_bookmarks -> BookmarksActivity.newIntent(v.context)
R.id.button_suggestions -> SuggestionsActivity.newIntent(v.context)
else -> return
}
startActivity(intent)
@@ -90,7 +93,7 @@ class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
override fun onRetryClick(error: Throwable) = Unit
override fun onEmptyActionClick() = Unit
override fun onEmptyActionClick() = onManageClick(requireView())
companion object {

View File

@@ -19,7 +19,11 @@ class ExploreViewModel(
) : BaseViewModel() {
val content: LiveData<List<ExploreItem>> = settings.observe()
.filter { it == AppSettings.KEY_SOURCES_HIDDEN || it == AppSettings.KEY_SOURCES_ORDER }
.filter {
it == AppSettings.KEY_SOURCES_HIDDEN ||
it == AppSettings.KEY_SOURCES_ORDER ||
it == AppSettings.KEY_SUGGESTIONS
}
.onStart { emit("") }
.map { settings.getMangaSources(includeHidden = false) }
.distinctUntilChanged()
@@ -27,13 +31,20 @@ class ExploreViewModel(
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
private fun buildList(sources: List<MangaSource>): List<ExploreItem> {
val result = ArrayList<ExploreItem>(sources.size + 2)
result += ExploreItem.Buttons
val result = ArrayList<ExploreItem>(sources.size + 3)
result += ExploreItem.Buttons(
isSuggestionsEnabled = settings.isSuggestionsEnabled,
)
result += ExploreItem.Header(R.string.remote_sources, sources.isNotEmpty())
if (sources.isNotEmpty()) {
result += ExploreItem.Header(R.string.enabled_sources)
sources.mapTo(result) { ExploreItem.Source(it) }
} else {
// TODO
result += ExploreItem.EmptyHint(
icon = R.drawable.ic_empty_search,
textPrimary = R.string.no_manga_sources,
textSecondary = R.string.no_manga_sources_text,
actionStringRes = R.string.manage,
)
}
return result
}

View File

@@ -16,4 +16,5 @@ class ExploreAdapter(
exploreButtonsAD(listener),
exploreSourcesHeaderAD(listener),
exploreSourceItemAD(coil, clickListener, lifecycleOwner),
exploreEmptyHintListAD(listener),
)

View File

@@ -1,19 +1,22 @@
package org.koitharu.kotatsu.explore.ui.adapter
import android.view.View
import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner
import coil.ImageLoader
import coil.request.Disposable
import coil.request.ImageRequest
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.list.AdapterDelegateClickListenerAdapter
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.databinding.ItemEmptyCardBinding
import org.koitharu.kotatsu.databinding.ItemExploreButtonsBinding
import org.koitharu.kotatsu.databinding.ItemExploreHeaderBinding
import org.koitharu.kotatsu.databinding.ItemExploreSourceBinding
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
import org.koitharu.kotatsu.utils.ext.enqueueWith
import org.koitharu.kotatsu.utils.ext.setTextAndVisible
import org.koitharu.kotatsu.utils.image.FaviconFallbackDrawable
fun exploreButtonsAD(
@@ -26,6 +29,10 @@ fun exploreButtonsAD(
binding.buttonHistory.setOnClickListener(clickListener)
binding.buttonLocal.setOnClickListener(clickListener)
binding.buttonSuggestions.setOnClickListener(clickListener)
bind {
binding.buttonSuggestions.isVisible = item.isSuggestionsEnabled
}
}
fun exploreSourcesHeaderAD(
@@ -41,7 +48,8 @@ fun exploreSourcesHeaderAD(
binding.buttonMore.setOnClickListener(listenerAdapter)
bind {
binding.textViewTitle.setText(R.string.remote_sources)
binding.textViewTitle.setText(item.titleResId)
binding.buttonMore.isVisible = item.isButtonVisible
}
}
@@ -77,4 +85,20 @@ fun exploreSourceItemAD(
imageRequest?.dispose()
imageRequest = null
}
}
fun exploreEmptyHintListAD(
listener: ListStateHolderListener,
) = adapterDelegateViewBinding<ExploreItem.EmptyHint, ExploreItem, ItemEmptyCardBinding>(
{ inflater, parent -> ItemEmptyCardBinding.inflate(inflater, parent, false) }
) {
binding.buttonRetry.setOnClickListener { listener.onEmptyActionClick() }
bind {
binding.icon.setImageResource(item.icon)
binding.textPrimary.setText(item.textPrimary)
binding.textSecondary.setTextAndVisible(item.textSecondary)
binding.buttonRetry.setTextAndVisible(item.actionStringRes)
}
}

View File

@@ -1,26 +1,56 @@
package org.koitharu.kotatsu.explore.ui.model
import android.net.Uri
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.parsers.model.MangaSource
sealed interface ExploreItem : ListModel {
object Buttons : ExploreItem
class Header(
@StringRes val titleResId: Int,
class Buttons(
val isSuggestionsEnabled: Boolean
) : ExploreItem {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Header
return titleResId == other.titleResId
other as Buttons
if (isSuggestionsEnabled != other.isSuggestionsEnabled) return false
return true
}
override fun hashCode(): Int = titleResId
override fun hashCode(): Int {
return isSuggestionsEnabled.hashCode()
}
}
class Header(
@StringRes val titleResId: Int,
val isButtonVisible: Boolean,
) : ExploreItem {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Header
if (titleResId != other.titleResId) return false
if (isButtonVisible != other.isButtonVisible) return false
return true
}
override fun hashCode(): Int {
var result = titleResId
result = 31 * result + isButtonVisible.hashCode()
return result
}
}
class Source(
@@ -46,4 +76,10 @@ sealed interface ExploreItem : ListModel {
}
}
class EmptyHint(
@DrawableRes icon: Int,
@StringRes textPrimary: Int,
@StringRes textSecondary: Int,
@StringRes actionStringRes: Int,
) : EmptyState(icon, textPrimary, textSecondary, actionStringRes), ExploreItem
}

View File

@@ -38,7 +38,7 @@ class LibraryMenuProvider(
}
private fun showClearHistoryDialog() {
val selectionListener = RememberSelectionDialogListener(-1)
val selectionListener = RememberSelectionDialogListener(2)
MaterialAlertDialogBuilder(context, materialR.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered)
.setTitle(R.string.clear_history)
.setSingleChoiceItems(

View File

@@ -168,21 +168,20 @@ abstract class MangaListFragment :
}
override fun onWindowInsetsChanged(insets: Insets) {
val headerHeight = (activity as? AppBarOwner)?.appBar?.measureHeight() ?: insets.top
binding.root.updatePadding(
left = insets.left,
right = insets.right,
)
binding.recyclerView.updatePadding(
bottom = insets.bottom,
)
if (activity is MainActivity) {
val headerHeight = (activity as? AppBarOwner)?.appBar?.measureHeight() ?: insets.top
binding.swipeRefreshLayout.setProgressViewOffset(
true,
headerHeight + resources.resolveDp(-72),
headerHeight + resources.resolveDp(10),
)
} else {
binding.recyclerView.updatePadding(
bottom = insets.bottom,
)
}
}

View File

@@ -3,9 +3,32 @@ package org.koitharu.kotatsu.list.ui.model
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
data class EmptyState(
open class EmptyState(
@DrawableRes val icon: Int,
@StringRes val textPrimary: Int,
@StringRes val textSecondary: Int,
@StringRes val actionStringRes: Int,
) : ListModel
) : ListModel {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as EmptyState
if (icon != other.icon) return false
if (textPrimary != other.textPrimary) return false
if (textSecondary != other.textSecondary) return false
if (actionStringRes != other.actionStringRes) return false
return true
}
override fun hashCode(): Int {
var result = icon
result = 31 * result + textPrimary
result = 31 * result + textSecondary
result = 31 * result + actionStringRes
return result
}
}

View File

@@ -30,7 +30,6 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.ActivityMainBinding
import org.koitharu.kotatsu.details.ui.DetailsActivity
import org.koitharu.kotatsu.explore.ui.ExploreFragment
import org.koitharu.kotatsu.favourites.ui.FavouritesContainerFragment
import org.koitharu.kotatsu.library.ui.LibraryFragment
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaTag
@@ -42,6 +41,7 @@ import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionFragment
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionViewModel
import org.koitharu.kotatsu.settings.AppUpdateChecker
import org.koitharu.kotatsu.settings.SettingsHeadersFragment
import org.koitharu.kotatsu.settings.newsources.NewSourcesDialogFragment
import org.koitharu.kotatsu.settings.onboard.OnboardDialogFragment
import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker
@@ -256,6 +256,10 @@ class MainActivity :
setPrimaryFragment(FeedFragment.newInstance())
binding.root.isLiftAppBarOnScroll = true // --//--
}
R.id.nav_tools -> {
setPrimaryFragment(SettingsHeadersFragment())
binding.root.isLiftAppBarOnScroll = true // --//--
}
else -> return false
}
appBar.setExpanded(true)

View File

@@ -55,13 +55,7 @@ class MangaListActivity : BaseActivity<ActivityContainerBinding>() {
left = insets.left,
right = insets.right
)
updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = insets.top
}
}
binding.container.updatePadding(
bottom = insets.bottom
)
}
private class ApplyFilterRunnable(

View File

@@ -5,7 +5,6 @@ import android.os.Bundle
import android.view.View
import androidx.preference.ListPreference
import androidx.preference.Preference
import java.io.File
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.R
@@ -19,6 +18,7 @@ import org.koitharu.kotatsu.settings.utils.SliderPreference
import org.koitharu.kotatsu.utils.ext.getStorageName
import org.koitharu.kotatsu.utils.ext.setDefaultValueCompat
import org.koitharu.kotatsu.utils.ext.viewLifecycleScope
import java.io.File
class ContentSettingsFragment :
BasePreferenceFragment(R.string.content),
@@ -30,9 +30,6 @@ class ContentSettingsFragment :
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_content)
findPreference<Preference>(AppSettings.KEY_SUGGESTIONS)?.setSummary(
if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled
)
findPreference<SliderPreference>(AppSettings.KEY_DOWNLOADS_PARALLELISM)?.run {
summary = value.toString()
setOnPreferenceChangeListener { preference, newValue ->
@@ -54,6 +51,9 @@ class ContentSettingsFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
findPreference<Preference>(AppSettings.KEY_LOCAL_STORAGE)?.bindStorageName()
findPreference<Preference>(AppSettings.KEY_SUGGESTIONS)?.setSummary(
if (settings.isSuggestionsEnabled) R.string.enabled else R.string.disabled
)
bindRemoteSourcesSummary()
settings.subscribe(this)
}

View File

@@ -0,0 +1,45 @@
package org.koitharu.kotatsu.suggestions.ui
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import androidx.fragment.app.commit
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseActivity
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
class SuggestionsActivity : BaseActivity<ActivityContainerBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(ActivityContainerBinding.inflate(layoutInflater))
supportActionBar?.setDisplayHomeAsUpEnabled(true)
val fm = supportFragmentManager
if (fm.findFragmentById(R.id.container) == null) {
fm.commit {
val fragment = SuggestionsFragment.newInstance()
replace(R.id.container, fragment)
}
}
}
override fun onWindowInsetsChanged(insets: Insets) {
binding.toolbar.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = insets.left
rightMargin = insets.right
}
binding.root.updatePadding(
left = insets.left,
right = insets.right,
)
}
companion object {
fun newIntent(context: Context) = Intent(context, SuggestionsActivity::class.java)
}
}