Quick search across manga sources

This commit is contained in:
Koitharu
2022-01-26 08:21:53 +02:00
parent 78aa4d76db
commit 79c2bf17fd
9 changed files with 86 additions and 44 deletions

View File

@@ -1,9 +1,8 @@
package org.koitharu.kotatsu.settings.sources
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.*
import androidx.appcompat.widget.SearchView
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.ItemTouchHelper
@@ -20,7 +19,7 @@ import org.koitharu.kotatsu.settings.sources.adapter.SourceConfigListener
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
class SourcesSettingsFragment : BaseFragment<FragmentSettingsSourcesBinding>(),
SourceConfigListener {
SourceConfigListener, SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener {
private var reorderHelper: ItemTouchHelper? = null
private val viewModel by viewModel<SourcesSettingsViewModel>()
@@ -61,6 +60,17 @@ class SourcesSettingsFragment : BaseFragment<FragmentSettingsSourcesBinding>(),
super.onDestroyView()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.opt_sources, menu)
val searchMenuItem = menu.findItem(R.id.action_search)
searchMenuItem.setOnActionExpandListener(this)
val searchView = searchMenuItem.actionView as SearchView
searchView.setOnQueryTextListener(this)
searchView.setIconifiedByDefault(false)
searchView.queryHint = searchMenuItem.title
}
override fun onWindowInsetsChanged(insets: Insets) {
binding.recyclerView.updatePadding(
bottom = insets.bottom,
@@ -85,6 +95,20 @@ class SourcesSettingsFragment : BaseFragment<FragmentSettingsSourcesBinding>(),
viewModel.expandOrCollapse(header.localeId)
}
override fun onQueryTextSubmit(query: String?): Boolean = false
override fun onQueryTextChange(newText: String?): Boolean {
viewModel.performSearch(newText)
return true
}
override fun onMenuItemActionExpand(item: MenuItem?): Boolean = true
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
(item.actionView as SearchView).setQuery("", false)
return true
}
private inner class SourcesReorderCallback : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.DOWN or ItemTouchHelper.UP,
0,

View File

@@ -21,6 +21,7 @@ class SourcesSettingsViewModel(
val items = MutableLiveData<List<SourceConfigItem>>(emptyList())
private val expandedGroups = HashSet<String?>()
private var searchQuery: String? = null
init {
buildList()
@@ -63,9 +64,30 @@ class SourcesSettingsViewModel(
buildList()
}
fun performSearch(query: String?) {
searchQuery = query?.trim()
buildList()
}
private fun buildList() {
val sources = MangaProviderFactory.getSources(settings, includeHidden = true)
val hiddenSources = settings.hiddenSources
val query = searchQuery
if (!query.isNullOrEmpty()) {
items.value = sources.mapNotNull {
if (!it.title.contains(query, ignoreCase = true)) {
return@mapNotNull null
}
SourceConfigItem.SourceItem(
source = it,
isEnabled = it.name !in hiddenSources,
isDraggable = false,
)
}.ifEmpty {
listOf(SourceConfigItem.EmptySearchResult)
}
return
}
val map = sources.groupByTo(TreeMap(LocaleKeyComparator())) {
if (it.name !in hiddenSources) {
KEY_ENABLED

View File

@@ -15,4 +15,5 @@ class SourceConfigAdapter(
sourceConfigGroupDelegate(listener),
sourceConfigItemDelegate(listener, coil, lifecycleOwner),
sourceConfigDraggableItemDelegate(listener),
sourceConfigEmptySearchDelegate(),
)

View File

@@ -4,12 +4,11 @@ import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.View
import android.widget.CompoundButton
import androidx.core.view.isVisible
import androidx.core.view.updatePaddingRelative
import androidx.lifecycle.LifecycleOwner
import coil.ImageLoader
import coil.request.Disposable
import coil.request.ImageRequest
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegate
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.databinding.ItemExpandableBinding
@@ -53,25 +52,15 @@ fun sourceConfigItemDelegate(
on = { item, _, _ -> item is SourceConfigItem.SourceItem && !item.isDraggable }
) {
val eventListener = object : View.OnClickListener, CompoundButton.OnCheckedChangeListener {
override fun onClick(v: View?) = listener.onItemSettingsClick(item)
override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
listener.onItemEnabledChanged(item, isChecked)
}
}
var imageRequest: Disposable? = null
binding.imageViewConfig.setOnClickListener(eventListener)
binding.switchToggle.setOnCheckedChangeListener(eventListener)
binding.switchToggle.setOnCheckedChangeListener { _, isChecked ->
listener.onItemEnabledChanged(item, isChecked)
}
bind {
binding.textViewTitle.text = item.source.title
binding.switchToggle.isChecked = item.isEnabled
binding.imageViewConfig.isVisible = item.isEnabled
binding.root.updatePaddingRelative(
end = if (item.isEnabled) 0 else binding.imageViewConfig.paddingEnd,
)
imageRequest = ImageRequest.Builder(context)
.data(item.faviconUrl)
.error(R.drawable.ic_favicon_fallback)
@@ -119,9 +108,9 @@ fun sourceConfigDraggableItemDelegate(
bind {
binding.textViewTitle.text = item.source.title
binding.switchToggle.isChecked = item.isEnabled
binding.imageViewConfig.isVisible = item.isEnabled
binding.root.updatePaddingRelative(
end = if (item.isEnabled) 0 else binding.imageViewConfig.paddingEnd,
)
}
}
}
fun sourceConfigEmptySearchDelegate() = adapterDelegate<SourceConfigItem.EmptySearchResult, SourceConfigItem>(
R.layout.item_sources_empty
) { }

View File

@@ -2,21 +2,25 @@ package org.koitharu.kotatsu.settings.sources.adapter
import androidx.recyclerview.widget.DiffUtil
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem
import org.koitharu.kotatsu.settings.sources.model.SourceConfigItem.*
class SourceConfigDiffCallback : DiffUtil.ItemCallback<SourceConfigItem>() {
override fun areItemsTheSame(oldItem: SourceConfigItem, newItem: SourceConfigItem): Boolean {
return when {
oldItem.javaClass != newItem.javaClass -> false
oldItem is SourceConfigItem.LocaleGroup && newItem is SourceConfigItem.LocaleGroup -> {
oldItem is LocaleGroup && newItem is LocaleGroup -> {
oldItem.localeId == newItem.localeId
}
oldItem is SourceConfigItem.SourceItem && newItem is SourceConfigItem.SourceItem -> {
oldItem is SourceItem && newItem is SourceItem -> {
oldItem.source == newItem.source
}
oldItem is SourceConfigItem.Header && newItem is SourceConfigItem.Header -> {
oldItem is Header && newItem is Header -> {
oldItem.titleResId == newItem.titleResId
}
oldItem == EmptySearchResult && newItem == EmptySearchResult -> {
true
}
else -> false
}
}

View File

@@ -76,4 +76,6 @@ sealed interface SourceConfigItem {
return result
}
}
object EmptySearchResult : SourceConfigItem
}

View File

@@ -4,7 +4,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="?android:listPreferredItemHeightSmall"
android:background="?android:windowBackground"
android:gravity="center_vertical"
android:orientation="horizontal">
@@ -14,7 +13,7 @@
android:layout_height="?android:listPreferredItemHeightSmall"
android:layout_marginHorizontal="?listPreferredItemPaddingStart"
android:labelFor="@id/textView_title"
android:padding="6dp"
android:padding="8dp"
android:scaleType="fitCenter"
tools:src="@tools:sample/avatars" />
@@ -33,16 +32,7 @@
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switch_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/imageView_config"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/settings"
android:paddingHorizontal="?listPreferredItemPaddingEnd"
android:scaleType="center"
android:src="@drawable/ic_settings" />
android:layout_height="wrap_content"
android:layout_marginEnd="?listPreferredItemPaddingEnd" />
</LinearLayout>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:text="@string/nothing_found"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
android:textColor="?android:textColorSecondary" />

View File

@@ -4,9 +4,10 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_languages"
android:icon="@drawable/ic_locale"
android:title="@string/languages"
app:showAsAction="ifRoom" />
android:id="@+id/action_search"
android:icon="@drawable/ic_search"
android:title="@string/search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" />
</menu>