Improve explore fragment
This commit is contained in:
@@ -11,16 +11,19 @@ import org.koin.android.ext.android.get
|
|||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.BaseFragment
|
import org.koitharu.kotatsu.base.ui.BaseFragment
|
||||||
|
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||||
import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner
|
import org.koitharu.kotatsu.base.ui.util.RecyclerViewOwner
|
||||||
import org.koitharu.kotatsu.databinding.FragmentExploreBinding
|
import org.koitharu.kotatsu.databinding.FragmentExploreBinding
|
||||||
import org.koitharu.kotatsu.explore.ui.adapter.ExploreAdapter
|
import org.koitharu.kotatsu.explore.ui.adapter.ExploreAdapter
|
||||||
import org.koitharu.kotatsu.explore.ui.adapter.SourcesHeaderEventListener
|
import org.koitharu.kotatsu.explore.ui.adapter.ExploreListEventListener
|
||||||
import org.koitharu.kotatsu.library.ui.adapter.LibraryAdapter
|
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
|
||||||
|
import org.koitharu.kotatsu.history.ui.HistoryActivity
|
||||||
|
import org.koitharu.kotatsu.search.ui.MangaListActivity
|
||||||
import org.koitharu.kotatsu.settings.SettingsActivity
|
import org.koitharu.kotatsu.settings.SettingsActivity
|
||||||
|
|
||||||
class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
|
class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
|
||||||
RecyclerViewOwner,
|
RecyclerViewOwner,
|
||||||
SourcesHeaderEventListener {
|
ExploreListEventListener, OnListItemClickListener<ExploreItem.Source> {
|
||||||
|
|
||||||
private val viewModel by viewModel<ExploreViewModel>()
|
private val viewModel by viewModel<ExploreViewModel>()
|
||||||
private var exploreAdapter: ExploreAdapter? = null
|
private var exploreAdapter: ExploreAdapter? = null
|
||||||
@@ -35,15 +38,15 @@ class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
|
|||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
exploreAdapter = ExploreAdapter(get(), viewLifecycleOwner, this)
|
exploreAdapter = ExploreAdapter(get(), viewLifecycleOwner, this, this)
|
||||||
with(binding.recyclerView) {
|
with(binding.recyclerView) {
|
||||||
adapter = exploreAdapter
|
adapter = exploreAdapter
|
||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing)
|
val spacing = resources.getDimensionPixelOffset(R.dimen.list_spacing)
|
||||||
paddingHorizontal = spacing
|
paddingHorizontal = spacing
|
||||||
}
|
}
|
||||||
viewModel.items.observe(viewLifecycleOwner) {
|
viewModel.content.observe(viewLifecycleOwner) {
|
||||||
exploreAdapter!!.items = it
|
exploreAdapter?.items = it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +71,19 @@ class ExploreFragment : BaseFragment<FragmentExploreBinding>(),
|
|||||||
startActivity(SettingsActivity.newManageSourcesIntent(view.context))
|
startActivity(SettingsActivity.newManageSourcesIntent(view.context))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View) {
|
||||||
|
val intent = when (v.id) {
|
||||||
|
R.id.button_history -> HistoryActivity.newIntent(v.context)
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemClick(item: ExploreItem.Source, view: View) {
|
||||||
|
val intent = MangaListActivity.newIntent(view.context, item.source)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onRetryClick(error: Throwable) = Unit
|
override fun onRetryClick(error: Throwable) = Unit
|
||||||
|
|
||||||
override fun onEmptyActionClick() = Unit
|
override fun onEmptyActionClick() = Unit
|
||||||
|
|||||||
@@ -1,71 +1,40 @@
|
|||||||
package org.koitharu.kotatsu.explore.ui
|
package org.koitharu.kotatsu.explore.ui
|
||||||
|
|
||||||
import androidx.core.os.LocaleListCompat
|
import androidx.lifecycle.LiveData
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
import org.koitharu.kotatsu.base.ui.BaseViewModel
|
||||||
import org.koitharu.kotatsu.core.model.getLocaleTitle
|
|
||||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||||
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
|
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
|
||||||
import org.koitharu.kotatsu.utils.ext.map
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
import java.util.*
|
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
|
||||||
|
|
||||||
private const val KEY_ENABLED = "!"
|
|
||||||
|
|
||||||
class ExploreViewModel(
|
class ExploreViewModel(
|
||||||
private val settings: AppSettings,
|
private val settings: AppSettings,
|
||||||
) : BaseViewModel() {
|
) : BaseViewModel() {
|
||||||
|
|
||||||
val items = MutableLiveData<List<ExploreItem>>(emptyList())
|
val content: LiveData<List<ExploreItem>> = settings.observe()
|
||||||
|
.filter { it == AppSettings.KEY_SOURCES_HIDDEN || it == AppSettings.KEY_SOURCES_ORDER }
|
||||||
|
.onStart { emit("") }
|
||||||
|
.map { settings.getMangaSources(includeHidden = false) }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.map { buildList(it) }
|
||||||
|
.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, emptyList())
|
||||||
|
|
||||||
init {
|
private fun buildList(sources: List<MangaSource>): List<ExploreItem> {
|
||||||
buildList()
|
val result = ArrayList<ExploreItem>(sources.size + 2)
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildList() {
|
|
||||||
val sources = settings.getMangaSources(includeHidden = true)
|
|
||||||
val hiddenSources = settings.hiddenSources
|
|
||||||
val map = sources.groupByTo(TreeMap(LocaleKeyComparator())) {
|
|
||||||
if (it.name !in hiddenSources) {
|
|
||||||
KEY_ENABLED
|
|
||||||
} else {
|
|
||||||
it.locale
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val result = ArrayList<ExploreItem>(sources.size + map.size + 1)
|
|
||||||
result += ExploreItem.Buttons
|
result += ExploreItem.Buttons
|
||||||
val enabledSources = map.remove(KEY_ENABLED)
|
if (sources.isNotEmpty()) {
|
||||||
if (!enabledSources.isNullOrEmpty()) {
|
|
||||||
result += ExploreItem.Header(R.string.enabled_sources)
|
result += ExploreItem.Header(R.string.enabled_sources)
|
||||||
enabledSources.mapTo(result) {
|
sources.mapTo(result) { ExploreItem.Source(it) }
|
||||||
ExploreItem.Source(
|
} else {
|
||||||
source = it,
|
// TODO
|
||||||
summary = it.getLocaleTitle(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
items.value = result
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LocaleKeyComparator : Comparator<String?> {
|
|
||||||
|
|
||||||
private val deviceLocales = LocaleListCompat.getAdjustedDefault()
|
|
||||||
.map { it.language }
|
|
||||||
|
|
||||||
override fun compare(a: String?, b: String?): Int {
|
|
||||||
when {
|
|
||||||
a == b -> return 0
|
|
||||||
a == null -> return 1
|
|
||||||
b == null -> return -1
|
|
||||||
}
|
|
||||||
val ai = deviceLocales.indexOf(a!!)
|
|
||||||
val bi = deviceLocales.indexOf(b!!)
|
|
||||||
return when {
|
|
||||||
ai < 0 && bi < 0 -> a.compareTo(b)
|
|
||||||
ai < 0 -> 1
|
|
||||||
bi < 0 -> -1
|
|
||||||
else -> ai.compareTo(bi)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,25 +3,17 @@ package org.koitharu.kotatsu.explore.ui.adapter
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||||
|
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
|
||||||
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
|
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
|
||||||
|
|
||||||
class ExploreAdapter(
|
class ExploreAdapter(
|
||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
listener: SourcesHeaderEventListener,
|
listener: ExploreListEventListener,
|
||||||
|
clickListener: OnListItemClickListener<ExploreItem.Source>,
|
||||||
) : AsyncListDifferDelegationAdapter<ExploreItem>(
|
) : AsyncListDifferDelegationAdapter<ExploreItem>(
|
||||||
ExploreDiffCallback(),
|
ExploreDiffCallback(),
|
||||||
exploreButtonsDelegate(),
|
exploreButtonsAD(listener),
|
||||||
sourceHeaderDelegate(listener),
|
exploreSourcesHeaderAD(listener),
|
||||||
sourceItemDelegate(coil, lifecycleOwner),
|
exploreSourceItemAD(coil, clickListener, lifecycleOwner),
|
||||||
) {
|
)
|
||||||
|
|
||||||
init {
|
|
||||||
delegatesManager
|
|
||||||
.addDelegate(exploreButtonsDelegate())
|
|
||||||
.addDelegate(sourceHeaderDelegate(listener = listener))
|
|
||||||
.addDelegate(sourceItemDelegate(coil, lifecycleOwner))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -7,22 +7,29 @@ import coil.request.Disposable
|
|||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||||
import org.koitharu.kotatsu.R
|
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.ItemExploreButtonsBinding
|
import org.koitharu.kotatsu.databinding.ItemExploreButtonsBinding
|
||||||
import org.koitharu.kotatsu.databinding.ItemExploreHeaderBinding
|
import org.koitharu.kotatsu.databinding.ItemExploreHeaderBinding
|
||||||
import org.koitharu.kotatsu.databinding.ItemExploreSourceBinding
|
import org.koitharu.kotatsu.databinding.ItemExploreSourceBinding
|
||||||
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
|
import org.koitharu.kotatsu.explore.ui.model.ExploreItem
|
||||||
import org.koitharu.kotatsu.utils.ext.enqueueWith
|
import org.koitharu.kotatsu.utils.ext.enqueueWith
|
||||||
|
import org.koitharu.kotatsu.utils.image.FaviconFallbackDrawable
|
||||||
|
|
||||||
fun exploreButtonsDelegate() = adapterDelegateViewBinding<ExploreItem.Buttons, ExploreItem, ItemExploreButtonsBinding>(
|
fun exploreButtonsAD(
|
||||||
|
clickListener: View.OnClickListener,
|
||||||
|
) = adapterDelegateViewBinding<ExploreItem.Buttons, ExploreItem, ItemExploreButtonsBinding>(
|
||||||
{ layoutInflater, parent -> ItemExploreButtonsBinding.inflate(layoutInflater, parent, false) }
|
{ layoutInflater, parent -> ItemExploreButtonsBinding.inflate(layoutInflater, parent, false) }
|
||||||
) {
|
) {
|
||||||
|
|
||||||
binding.localStorage.requestFocus() // stub
|
binding.buttonBookmarks.setOnClickListener(clickListener)
|
||||||
|
binding.buttonHistory.setOnClickListener(clickListener)
|
||||||
|
binding.buttonLocal.setOnClickListener(clickListener)
|
||||||
|
binding.buttonSuggestions.setOnClickListener(clickListener)
|
||||||
|
}
|
||||||
|
|
||||||
} // TODO
|
fun exploreSourcesHeaderAD(
|
||||||
|
listener: ExploreListEventListener,
|
||||||
fun sourceHeaderDelegate(
|
|
||||||
listener: SourcesHeaderEventListener,
|
|
||||||
) = adapterDelegateViewBinding<ExploreItem.Header, ExploreItem, ItemExploreHeaderBinding>(
|
) = adapterDelegateViewBinding<ExploreItem.Header, ExploreItem, ItemExploreHeaderBinding>(
|
||||||
{ layoutInflater, parent -> ItemExploreHeaderBinding.inflate(layoutInflater, parent, false) }
|
{ layoutInflater, parent -> ItemExploreHeaderBinding.inflate(layoutInflater, parent, false) }
|
||||||
) {
|
) {
|
||||||
@@ -38,8 +45,9 @@ fun sourceHeaderDelegate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun sourceItemDelegate(
|
fun exploreSourceItemAD(
|
||||||
coil: ImageLoader,
|
coil: ImageLoader,
|
||||||
|
listener: OnListItemClickListener<ExploreItem.Source>,
|
||||||
lifecycleOwner: LifecycleOwner,
|
lifecycleOwner: LifecycleOwner,
|
||||||
) = adapterDelegateViewBinding<ExploreItem.Source, ExploreItem, ItemExploreSourceBinding>(
|
) = adapterDelegateViewBinding<ExploreItem.Source, ExploreItem, ItemExploreSourceBinding>(
|
||||||
{ layoutInflater, parent -> ItemExploreSourceBinding.inflate(layoutInflater, parent, false) },
|
{ layoutInflater, parent -> ItemExploreSourceBinding.inflate(layoutInflater, parent, false) },
|
||||||
@@ -47,12 +55,19 @@ fun sourceItemDelegate(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
var imageRequest: Disposable? = null
|
var imageRequest: Disposable? = null
|
||||||
|
val eventListener = AdapterDelegateClickListenerAdapter(this, listener)
|
||||||
|
|
||||||
|
binding.root.setOnClickListener(eventListener)
|
||||||
|
binding.root.setOnLongClickListener(eventListener)
|
||||||
|
|
||||||
bind {
|
bind {
|
||||||
binding.textViewTitle.text = item.source.title
|
binding.textViewTitle.text = item.source.title
|
||||||
|
val fallbackIcon = FaviconFallbackDrawable(context, item.source.name)
|
||||||
imageRequest = ImageRequest.Builder(context)
|
imageRequest = ImageRequest.Builder(context)
|
||||||
.data(item.faviconUrl)
|
.data(item.faviconUrl)
|
||||||
.error(R.drawable.ic_favicon_fallback)
|
.fallback(fallbackIcon)
|
||||||
|
.placeholder(fallbackIcon)
|
||||||
|
.error(fallbackIcon)
|
||||||
.target(binding.imageViewCover)
|
.target(binding.imageViewCover)
|
||||||
.lifecycle(lifecycleOwner)
|
.lifecycle(lifecycleOwner)
|
||||||
.enqueueWith(coil)
|
.enqueueWith(coil)
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ class ExploreDiffCallback : DiffUtil.ItemCallback<ExploreItem>() {
|
|||||||
override fun areItemsTheSame(oldItem: ExploreItem, newItem: ExploreItem): Boolean {
|
override fun areItemsTheSame(oldItem: ExploreItem, newItem: ExploreItem): Boolean {
|
||||||
return when {
|
return when {
|
||||||
oldItem.javaClass != newItem.javaClass -> false
|
oldItem.javaClass != newItem.javaClass -> false
|
||||||
oldItem is ExploreItem.Buttons && newItem is ExploreItem.Buttons -> {
|
oldItem is ExploreItem.Buttons && newItem is ExploreItem.Buttons -> true
|
||||||
oldItem == newItem
|
|
||||||
}
|
|
||||||
oldItem is ExploreItem.Source && newItem is ExploreItem.Source -> {
|
oldItem is ExploreItem.Source && newItem is ExploreItem.Source -> {
|
||||||
oldItem.source == newItem.source
|
oldItem.source == newItem.source
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ package org.koitharu.kotatsu.explore.ui.adapter
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
|
import org.koitharu.kotatsu.list.ui.adapter.ListStateHolderListener
|
||||||
|
|
||||||
interface SourcesHeaderEventListener : ListStateHolderListener {
|
interface ExploreListEventListener : ListStateHolderListener, View.OnClickListener {
|
||||||
|
|
||||||
fun onManageClick(view: View)
|
fun onManageClick(view: View)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,10 @@ package org.koitharu.kotatsu.explore.ui.model
|
|||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
|
|
||||||
sealed interface ExploreItem {
|
sealed interface ExploreItem : ListModel {
|
||||||
|
|
||||||
object Buttons : ExploreItem
|
object Buttons : ExploreItem
|
||||||
|
|
||||||
@@ -24,7 +25,6 @@ sealed interface ExploreItem {
|
|||||||
|
|
||||||
class Source(
|
class Source(
|
||||||
val source: MangaSource,
|
val source: MangaSource,
|
||||||
val summary: String?,
|
|
||||||
) : ExploreItem {
|
) : ExploreItem {
|
||||||
|
|
||||||
val faviconUrl: Uri
|
val faviconUrl: Uri
|
||||||
@@ -37,15 +37,12 @@ sealed interface ExploreItem {
|
|||||||
other as Source
|
other as Source
|
||||||
|
|
||||||
if (source != other.source) return false
|
if (source != other.source) return false
|
||||||
if (summary != other.summary) return false
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
var result = source.hashCode()
|
return source.hashCode()
|
||||||
result = 31 * result + summary.hashCode()
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.R
|
|||||||
import org.koitharu.kotatsu.base.ui.BaseActivity
|
import org.koitharu.kotatsu.base.ui.BaseActivity
|
||||||
import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaTags
|
import org.koitharu.kotatsu.core.model.parcelable.ParcelableMangaTags
|
||||||
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
import org.koitharu.kotatsu.databinding.ActivityContainerBinding
|
||||||
|
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||||
import org.koitharu.kotatsu.parsers.model.MangaTag
|
import org.koitharu.kotatsu.parsers.model.MangaTag
|
||||||
import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment
|
import org.koitharu.kotatsu.remotelist.ui.RemoteListFragment
|
||||||
import org.koitharu.kotatsu.remotelist.ui.RemoteListViewModel
|
import org.koitharu.kotatsu.remotelist.ui.RemoteListViewModel
|
||||||
@@ -24,17 +25,21 @@ class MangaListActivity : BaseActivity<ActivityContainerBinding>() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(ActivityContainerBinding.inflate(layoutInflater))
|
setContentView(ActivityContainerBinding.inflate(layoutInflater))
|
||||||
val tags = intent.getParcelableExtra<ParcelableMangaTags>(EXTRA_TAGS)?.tags ?: run {
|
val tags = intent.getParcelableExtra<ParcelableMangaTags>(EXTRA_TAGS)?.tags
|
||||||
finishAfterTransition()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
val fm = supportFragmentManager
|
val fm = supportFragmentManager
|
||||||
if (fm.findFragmentById(R.id.container) == null) {
|
if (fm.findFragmentById(R.id.container) == null) {
|
||||||
|
val source = intent.getSerializableExtra(EXTRA_SOURCE) as? MangaSource ?: tags?.firstOrNull()?.source
|
||||||
|
if (source == null) {
|
||||||
|
finishAfterTransition()
|
||||||
|
return
|
||||||
|
}
|
||||||
fm.commit {
|
fm.commit {
|
||||||
val fragment = RemoteListFragment.newInstance(tags.first().source)
|
val fragment = RemoteListFragment.newInstance(source)
|
||||||
replace(R.id.container, fragment)
|
replace(R.id.container, fragment)
|
||||||
runOnCommit(ApplyFilterRunnable(fragment, tags))
|
if (!tags.isNullOrEmpty()) {
|
||||||
|
runOnCommit(ApplyFilterRunnable(fragment, tags))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,9 +75,12 @@ class MangaListActivity : BaseActivity<ActivityContainerBinding>() {
|
|||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val EXTRA_TAGS = "tags"
|
private const val EXTRA_TAGS = "tags"
|
||||||
|
private const val EXTRA_SOURCE = "source"
|
||||||
|
|
||||||
fun newIntent(context: Context, tags: Set<MangaTag>) =
|
fun newIntent(context: Context, tags: Set<MangaTag>) = Intent(context, MangaListActivity::class.java)
|
||||||
Intent(context, MangaListActivity::class.java)
|
.putExtra(EXTRA_TAGS, ParcelableMangaTags(tags))
|
||||||
.putExtra(EXTRA_TAGS, ParcelableMangaTags(tags))
|
|
||||||
|
fun newIntent(context: Context, source: MangaSource) = Intent(context, MangaListActivity::class.java)
|
||||||
|
.putExtra(EXTRA_SOURCE, source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package org.koitharu.kotatsu.utils.image
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.*
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import androidx.core.graphics.ColorUtils
|
||||||
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
class FaviconFallbackDrawable(
|
||||||
|
context: Context,
|
||||||
|
name: String,
|
||||||
|
) : Drawable() {
|
||||||
|
|
||||||
|
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||||
|
private val letter = name.take(1).uppercase()
|
||||||
|
private val color = colorOfString(name)
|
||||||
|
private val textBounds = Rect()
|
||||||
|
private val tempRect = Rect()
|
||||||
|
|
||||||
|
init {
|
||||||
|
paint.style = Paint.Style.FILL
|
||||||
|
paint.textAlign = Paint.Align.CENTER
|
||||||
|
paint.isFakeBoldText = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun draw(canvas: Canvas) {
|
||||||
|
val cx = bounds.exactCenterX()
|
||||||
|
paint.color = color
|
||||||
|
canvas.drawPaint(paint)
|
||||||
|
paint.color = Color.WHITE
|
||||||
|
val ty = bounds.height() / 2f + textBounds.height() / 2f - textBounds.bottom
|
||||||
|
canvas.drawText(letter, cx, ty, paint)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBoundsChange(bounds: Rect) {
|
||||||
|
super.onBoundsChange(bounds)
|
||||||
|
val innerWidth = bounds.width() - (paint.strokeWidth * 2f)
|
||||||
|
paint.textSize = getTextSizeForWidth(innerWidth, "100%")
|
||||||
|
paint.getTextBounds(letter, 0, letter.length, textBounds)
|
||||||
|
invalidateSelf()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setAlpha(alpha: Int) {
|
||||||
|
paint.alpha = alpha
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setColorFilter(colorFilter: ColorFilter?) {
|
||||||
|
paint.colorFilter = colorFilter
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DeprecatedCallableAddReplaceWith")
|
||||||
|
@Deprecated("Deprecated in Java")
|
||||||
|
override fun getOpacity() = PixelFormat.TRANSPARENT
|
||||||
|
|
||||||
|
private fun getTextSizeForWidth(width: Float, text: String): Float {
|
||||||
|
val testTextSize = 48f
|
||||||
|
paint.textSize = testTextSize
|
||||||
|
paint.getTextBounds(text, 0, text.length, tempRect)
|
||||||
|
return testTextSize * width / tempRect.width()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun colorOfString(str: String): Int {
|
||||||
|
val hue = (str.hashCode() % 360).absoluteValue.toFloat()
|
||||||
|
return ColorUtils.HSLToColor(floatArrayOf(hue, 0.5f, 0.5f))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,45 +5,45 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:paddingHorizontal="8dp"
|
android:padding="4dp"
|
||||||
android:paddingVertical="4dp">
|
android:weightSum="4">
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/favourites"
|
android:id="@+id/button_history"
|
||||||
style="@style/Widget.Kotatsu.ExploreButton"
|
style="@style/Widget.Kotatsu.ExploreButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginHorizontal="4dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/history"
|
android:text="@string/history"
|
||||||
app:icon="@drawable/ic_history" />
|
app:icon="@drawable/ic_history" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/local_storage"
|
android:id="@+id/button_local"
|
||||||
style="@style/Widget.Kotatsu.ExploreButton"
|
style="@style/Widget.Kotatsu.ExploreButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginHorizontal="4dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/local_storage"
|
android:text="@string/local_storage"
|
||||||
app:icon="@drawable/ic_storage" />
|
app:icon="@drawable/ic_storage" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/suggestions"
|
android:id="@+id/button_suggestions"
|
||||||
style="@style/Widget.Kotatsu.ExploreButton"
|
style="@style/Widget.Kotatsu.ExploreButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="8dp"
|
android:layout_marginHorizontal="4dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/suggestions"
|
android:text="@string/suggestions"
|
||||||
app:icon="@drawable/ic_suggestion" />
|
app:icon="@drawable/ic_suggestion" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/downloads"
|
android:id="@+id/button_bookmarks"
|
||||||
style="@style/Widget.Kotatsu.ExploreButton"
|
style="@style/Widget.Kotatsu.ExploreButton"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginHorizontal="4dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/bookmarks"
|
android:text="@string/bookmarks"
|
||||||
app:icon="@drawable/ic_bookmark" />
|
app:icon="@drawable/ic_bookmark" />
|
||||||
|
|||||||
@@ -1,62 +1,63 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<GridLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:columnCount="2"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingHorizontal="8dp"
|
android:rowCount="2">
|
||||||
android:paddingVertical="4dp">
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/favourites"
|
android:id="@+id/button_history"
|
||||||
style="@style/Widget.Kotatsu.ExploreButton"
|
style="@style/Widget.Kotatsu.ExploreButton"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_columnWeight="0.5"
|
||||||
|
android:layout_gravity="fill_horizontal"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
android:text="@string/history"
|
android:text="@string/history"
|
||||||
app:icon="@drawable/ic_history"
|
app:icon="@drawable/ic_history" />
|
||||||
app:layout_constraintEnd_toStartOf="@+id/local_storage"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/local_storage"
|
android:id="@+id/button_suggestions"
|
||||||
style="@style/Widget.Kotatsu.ExploreButton"
|
style="@style/Widget.Kotatsu.ExploreButton"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/local_storage"
|
android:layout_columnWeight="0.5"
|
||||||
app:icon="@drawable/ic_storage"
|
android:layout_gravity="fill_horizontal"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:layout_marginStart="8dp"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
android:layout_marginTop="4dp"
|
||||||
app:layout_constraintStart_toEndOf="@+id/favourites"
|
android:layout_marginEnd="8dp"
|
||||||
app:layout_constraintTop_toTopOf="@+id/favourites" />
|
android:layout_marginBottom="4dp"
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
|
||||||
android:id="@+id/suggestions"
|
|
||||||
style="@style/Widget.Kotatsu.ExploreButton"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:text="@string/suggestions"
|
android:text="@string/suggestions"
|
||||||
app:icon="@drawable/ic_suggestion"
|
app:icon="@drawable/ic_suggestion" />
|
||||||
app:layout_constraintEnd_toStartOf="@+id/downloads"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/favourites"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/favourites" />
|
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/downloads"
|
android:id="@+id/button_local"
|
||||||
style="@style/Widget.Kotatsu.ExploreButton"
|
style="@style/Widget.Kotatsu.ExploreButton"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_columnWeight="0.5"
|
||||||
android:text="@string/bookmarks"
|
android:layout_gravity="fill_horizontal"
|
||||||
app:icon="@drawable/ic_bookmark"
|
android:layout_marginStart="8dp"
|
||||||
app:layout_constraintEnd_toEndOf="@+id/local_storage"
|
android:layout_marginTop="4dp"
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
android:layout_marginEnd="8dp"
|
||||||
app:layout_constraintStart_toEndOf="@+id/suggestions"
|
android:layout_marginBottom="4dp"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/local_storage" />
|
android:text="@string/local_storage"
|
||||||
|
app:icon="@drawable/ic_storage" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
<com.google.android.material.button.MaterialButton
|
||||||
|
android:id="@+id/button_bookmarks"
|
||||||
|
style="@style/Widget.Kotatsu.ExploreButton"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_columnWeight="0.5"
|
||||||
|
android:layout_gravity="fill_horizontal"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="4dp"
|
||||||
|
android:text="@string/bookmarks"
|
||||||
|
app:icon="@drawable/ic_bookmark" />
|
||||||
|
|
||||||
|
</GridLayout>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
@@ -7,32 +7,26 @@
|
|||||||
android:layout_height="?attr/listPreferredItemHeightSmall"
|
android:layout_height="?attr/listPreferredItemHeightSmall"
|
||||||
android:background="?selectableItemBackground"
|
android:background="?selectableItemBackground"
|
||||||
android:clipChildren="false"
|
android:clipChildren="false"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
android:padding="@dimen/list_spacing">
|
android:padding="@dimen/list_spacing">
|
||||||
|
|
||||||
<com.google.android.material.imageview.ShapeableImageView
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
android:id="@+id/imageView_cover"
|
android:id="@+id/imageView_cover"
|
||||||
android:layout_width="0dp"
|
android:layout_width="32dp"
|
||||||
android:layout_height="0dp"
|
android:layout_height="32dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:shapeAppearance="?shapeAppearanceCornerSmall"
|
||||||
app:layout_constraintDimensionRatio="h,1:1"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Kotatsu.Cover.Small"
|
|
||||||
tools:src="@tools:sample/backgrounds/scenic" />
|
tools:src="@tools:sample/backgrounds/scenic" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/textView_title"
|
android:id="@+id/textView_title"
|
||||||
android:layout_width="0dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:maxLines="1"
|
android:singleLine="true"
|
||||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
tools:text="@tools:sample/lorem[2]" />
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/imageView_cover"
|
|
||||||
app:layout_constraintTop_toTopOf="@+id/imageView_cover"
|
|
||||||
tools:text="Title" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</LinearLayout>
|
||||||
@@ -128,15 +128,17 @@
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="Widget.Kotatsu.ExploreButton" parent="Widget.Material3.Button.TonalButton.Icon">
|
<style name="Widget.Kotatsu.ExploreButton" parent="Widget.Material3.Button.TonalButton.Icon">
|
||||||
<item name="android:paddingTop">16dp</item>
|
<item name="android:minHeight">58dp</item>
|
||||||
<item name="android:paddingBottom">16dp</item>
|
|
||||||
<item name="android:textColor">?attr/colorOnSurface</item>
|
<item name="android:textColor">?attr/colorOnSurface</item>
|
||||||
<item name="shapeAppearanceOverlay">@style/ShapeAppearanceOverlay.Material3.FloatingActionButton</item>
|
<item name="singleLine">true</item>
|
||||||
|
<item name="shapeAppearance">?shapeAppearanceCornerLarge</item>
|
||||||
<item name="iconPadding">16dp</item>
|
<item name="iconPadding">16dp</item>
|
||||||
<item name="iconGravity">start</item>
|
<item name="iconGravity">start</item>
|
||||||
|
<item name="android:insetTop">2dp</item>
|
||||||
|
<item name="android:insetBottom">2dp</item>
|
||||||
<item name="iconTint">?attr/colorPrimary</item>
|
<item name="iconTint">?attr/colorPrimary</item>
|
||||||
<item name="android:gravity">start|center_vertical</item>
|
<item name="android:gravity">start|center_vertical</item>
|
||||||
<item name="android:textAppearance">@style/TextAppearance.Material3.BodyMedium</item>
|
<item name="android:textAppearance">?textAppearanceButton</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="ThemeOverlay.Kotatsu.MainToolbar" parent="">
|
<style name="ThemeOverlay.Kotatsu.MainToolbar" parent="">
|
||||||
|
|||||||
Reference in New Issue
Block a user