Global search
This commit is contained in:
@@ -4,16 +4,31 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import org.koin.core.KoinComponent
|
||||
import org.koitharu.kotatsu.core.model.Manga
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
import java.util.*
|
||||
|
||||
class MangaSearchRepository : KoinComponent {
|
||||
|
||||
fun globalSearch(query: String): Flow<List<Manga>> = flow {
|
||||
fun globalSearch(query: String, batchSize: Int = 4): Flow<List<Manga>> = flow {
|
||||
val sources = MangaProviderFactory.getSources(false)
|
||||
for (source in sources) {
|
||||
val provider = MangaProviderFactory.create(source)
|
||||
val list = provider.getList(0, query, SortOrder.POPULARITY)
|
||||
emit(list.take(4))
|
||||
val lists = EnumMap<MangaSource, List<Manga>>(MangaSource::class.java)
|
||||
var i = 0
|
||||
while (true) {
|
||||
var isEmitted = false
|
||||
for (source in sources) {
|
||||
val list = lists.getOrPut(source) {
|
||||
MangaProviderFactory.create(source).getList(0, query, SortOrder.POPULARITY)
|
||||
}
|
||||
if (i < list.size) {
|
||||
emit(list.subList(i, (i + batchSize).coerceAtMost(list.lastIndex)))
|
||||
isEmitted = true
|
||||
}
|
||||
}
|
||||
i += batchSize
|
||||
if (!isEmitted) {
|
||||
return@flow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,13 @@ package org.koitharu.kotatsu.ui.list.remote
|
||||
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import moxy.ktx.moxyPresenter
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaFilter
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.ui.list.MangaListFragment
|
||||
import org.koitharu.kotatsu.ui.search.SearchHelper
|
||||
import org.koitharu.kotatsu.ui.search.SearchActivity
|
||||
import org.koitharu.kotatsu.utils.ext.withArgs
|
||||
|
||||
class RemoteListFragment : MangaListFragment<Unit>() {
|
||||
@@ -31,12 +32,17 @@ class RemoteListFragment : MangaListFragment<Unit>() {
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.opt_remote, menu)
|
||||
menu.findItem(R.id.action_search)?.let { menuItem ->
|
||||
SearchHelper.setupSearchView(menuItem, source)
|
||||
}
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.action_search_internal -> {
|
||||
context?.startActivity(SearchActivity.newIntent(requireContext(), source, null))
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val ARG_SOURCE = "provider"
|
||||
|
||||
@@ -4,38 +4,60 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import kotlinx.android.synthetic.main.activity_search.*
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.ui.common.BaseActivity
|
||||
import org.koitharu.kotatsu.utils.ext.showKeyboard
|
||||
|
||||
class SearchActivity : BaseActivity() {
|
||||
class SearchActivity : BaseActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
private lateinit var source: MangaSource
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_search)
|
||||
val source = intent.getParcelableExtra<MangaSource>(EXTRA_SOURCE)
|
||||
val query = intent.getStringExtra(EXTRA_QUERY)
|
||||
|
||||
if (source == null || query == null) {
|
||||
source = intent.getParcelableExtra(EXTRA_SOURCE) ?: run {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
val query = intent.getStringExtra(EXTRA_QUERY)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
title = query
|
||||
supportActionBar?.subtitle = getString(R.string.search_results_on_s, source.title)
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.container, SearchFragment.newInstance(source, query))
|
||||
.commit()
|
||||
searchView.queryHint = getString(R.string.search_on_s, source.title)
|
||||
searchView.suggestionsAdapter = MangaSuggestionsProvider.getSuggestionAdapter(this)
|
||||
searchView.setOnSuggestionListener(SearchHelper.SuggestionListener(searchView))
|
||||
searchView.setOnQueryTextListener(this)
|
||||
|
||||
if (query.isNullOrBlank()) {
|
||||
searchView.requestFocus()
|
||||
searchView.showKeyboard()
|
||||
} else {
|
||||
searchView.setQuery(query, true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
return if (!query.isNullOrBlank()) {
|
||||
title = query
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(R.id.container, SearchFragment.newInstance(source, query))
|
||||
.commit()
|
||||
searchView.clearFocus()
|
||||
MangaSuggestionsProvider.saveQuery(this, query)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String?) = false
|
||||
|
||||
companion object {
|
||||
|
||||
private const val EXTRA_SOURCE = "source"
|
||||
private const val EXTRA_QUERY = "query"
|
||||
|
||||
fun newIntent(context: Context, source: MangaSource, query: String) =
|
||||
fun newIntent(context: Context, source: MangaSource, query: String?) =
|
||||
Intent(context, SearchActivity::class.java)
|
||||
.putExtra(EXTRA_SOURCE, source as Parcelable)
|
||||
.putExtra(EXTRA_QUERY, query)
|
||||
|
||||
@@ -6,22 +6,11 @@ import android.database.Cursor
|
||||
import android.view.MenuItem
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.MangaSource
|
||||
import org.koitharu.kotatsu.ui.search.global.GlobalSearchActivity
|
||||
import org.koitharu.kotatsu.utils.ext.safe
|
||||
|
||||
object SearchHelper {
|
||||
|
||||
@JvmStatic
|
||||
fun setupSearchView(menuItem: MenuItem, source: MangaSource) {
|
||||
val view = menuItem.actionView as? SearchView ?: return
|
||||
val context = view.context
|
||||
view.queryHint = context.getString(R.string.search_manga)
|
||||
view.suggestionsAdapter = MangaSuggestionsProvider.getSuggestionAdapter(context)
|
||||
view.setOnQueryTextListener(QueryListener(context, source))
|
||||
view.setOnSuggestionListener(SuggestionListener(view))
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setupSearchView(menuItem: MenuItem) {
|
||||
val view = menuItem.actionView as? SearchView ?: return
|
||||
@@ -32,16 +21,12 @@ object SearchHelper {
|
||||
view.setOnSuggestionListener(SuggestionListener(view))
|
||||
}
|
||||
|
||||
private class QueryListener(private val context: Context, private val source: MangaSource? = null) :
|
||||
private class QueryListener(private val context: Context) :
|
||||
SearchView.OnQueryTextListener {
|
||||
|
||||
override fun onQueryTextSubmit(query: String?): Boolean {
|
||||
return if (!query.isNullOrBlank()) {
|
||||
if (source == null) {
|
||||
context.startActivity(GlobalSearchActivity.newIntent(context, query.trim()))
|
||||
} else {
|
||||
context.startActivity(SearchActivity.newIntent(context, source, query.trim()))
|
||||
}
|
||||
context.startActivity(GlobalSearchActivity.newIntent(context, query.trim()))
|
||||
MangaSuggestionsProvider.saveQuery(context, query)
|
||||
true
|
||||
} else false
|
||||
@@ -50,7 +35,7 @@ object SearchHelper {
|
||||
override fun onQueryTextChange(newText: String?) = false
|
||||
}
|
||||
|
||||
private class SuggestionListener(private val view: SearchView) :
|
||||
class SuggestionListener(private val view: SearchView) :
|
||||
SearchView.OnSuggestionListener {
|
||||
|
||||
override fun onSuggestionSelect(position: Int) = false
|
||||
|
||||
@@ -10,7 +10,7 @@ class GlobalSearchActivity : BaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_search)
|
||||
setContentView(R.layout.activity_search_global)
|
||||
val query = intent.getStringExtra(EXTRA_QUERY)
|
||||
|
||||
if (query == null) {
|
||||
|
||||
@@ -34,6 +34,7 @@ class GlobalSearchPresenter : BasePresenter<MangaListView<Unit>>() {
|
||||
}
|
||||
}
|
||||
.onFirst {
|
||||
viewState.onListChanged(emptyList())
|
||||
viewState.onLoadingStateChanged(isLoading = false)
|
||||
}
|
||||
.onEmpty {
|
||||
|
||||
Reference in New Issue
Block a user