* Add onPause\onResume persistence to searchView. Fixes issue #3627 * New controller subclass with built-in SearchView support * Implement new SearchableNucleusController in SourceController * Add query to BasePresenter (for one field it is not worth create a subclass in my opinion), convert BrowseSourceController to inherit from SearchableNucleusController * move to flows to fix an issue in GlobalSearch where it would trigger the search multiple times * Continue conversion to SearchableNucleusController * Convert LibraryController, convert to flows, Known ISSUE with empty string being posted after setting the query upon creation of UI * Fix issues with the post being tide to the SearchView queue which is not processed until shown. Add COLLAPSING state capture which should wrap this up. * refactoring & enforce @StringRes for queryHint
This commit is contained in:
committed by
GitHub
parent
14c114756d
commit
2911fe7a1a
@@ -9,7 +9,6 @@ import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.list.listItems
|
||||
@@ -25,19 +24,11 @@ import eu.kanade.tachiyomi.databinding.SourceMainControllerBinding
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.base.controller.*
|
||||
import eu.kanade.tachiyomi.ui.browse.BrowseController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.latest.LatestUpdatesController
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import reactivecircus.flowbinding.appcompat.QueryTextEvent
|
||||
import reactivecircus.flowbinding.appcompat.queryTextEvents
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@@ -48,7 +39,7 @@ import uy.kohesive.injekt.api.get
|
||||
* [SourceAdapter.OnLatestClickListener] call function data on latest item click
|
||||
*/
|
||||
class SourceController :
|
||||
NucleusController<SourceMainControllerBinding, SourcePresenter>(),
|
||||
SearchableNucleusController<SourceMainControllerBinding, SourcePresenter>(),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
SourceAdapter.OnSourceClickListener {
|
||||
@@ -200,37 +191,6 @@ class SourceController :
|
||||
parentController!!.router.pushController(controller.withFadeTransaction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds items to the options menu.
|
||||
*
|
||||
* @param menu menu containing options.
|
||||
* @param inflater used to load the menu xml.
|
||||
*/
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
// Inflate menu
|
||||
inflater.inflate(R.menu.source_main, menu)
|
||||
|
||||
// Initialize search option.
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
searchView.maxWidth = Int.MAX_VALUE
|
||||
|
||||
// Change hint to show global search.
|
||||
searchView.queryHint = applicationContext?.getString(R.string.action_global_search_hint)
|
||||
|
||||
// Create query listener which opens the global search view.
|
||||
searchView.queryTextEvents()
|
||||
.filterIsInstance<QueryTextEvent.QuerySubmitted>()
|
||||
.onEach { performGlobalSearch(it.queryText.toString()) }
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
|
||||
private fun performGlobalSearch(query: String) {
|
||||
parentController!!.router.pushController(
|
||||
GlobalSearchController(query).withFadeTransaction()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an option menu item has been selected by the user.
|
||||
*
|
||||
@@ -290,4 +250,21 @@ class SourceController :
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
createOptionsMenu(
|
||||
menu,
|
||||
inflater,
|
||||
R.menu.source_main,
|
||||
R.id.action_search,
|
||||
R.string.action_global_search_hint,
|
||||
false // GlobalSearch handles the searching here
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSearchViewQueryTextSubmit(query: String?) {
|
||||
parentController!!.router.pushController(
|
||||
GlobalSearchController(query).withFadeTransaction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+7
-25
@@ -8,7 +8,6 @@ import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
@@ -33,7 +32,7 @@ import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.base.controller.FabController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.SearchableNucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
|
||||
@@ -51,12 +50,8 @@ import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import eu.kanade.tachiyomi.widget.EmptyView
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import reactivecircus.flowbinding.appcompat.QueryTextEvent
|
||||
import reactivecircus.flowbinding.appcompat.queryTextEvents
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
@@ -64,7 +59,7 @@ import uy.kohesive.injekt.injectLazy
|
||||
* Controller to manage the catalogues available in the app.
|
||||
*/
|
||||
open class BrowseSourceController(bundle: Bundle) :
|
||||
NucleusController<SourceControllerBinding, BrowseSourcePresenter>(bundle),
|
||||
SearchableNucleusController<SourceControllerBinding, BrowseSourcePresenter>(bundle),
|
||||
FabController,
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
@@ -259,25 +254,8 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.source_browse, menu)
|
||||
|
||||
// Initialize search menu
|
||||
createOptionsMenu(menu, inflater, R.menu.source_browse, R.id.action_search)
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
searchView.maxWidth = Int.MAX_VALUE
|
||||
|
||||
val query = presenter.query
|
||||
if (query.isNotBlank()) {
|
||||
searchItem.expandActionView()
|
||||
searchView.setQuery(query, true)
|
||||
searchView.clearFocus()
|
||||
}
|
||||
|
||||
searchView.queryTextEvents()
|
||||
.filter { router.backstack.lastOrNull()?.controller() == this@BrowseSourceController }
|
||||
.filterIsInstance<QueryTextEvent.QuerySubmitted>()
|
||||
.onEach { searchWithQuery(it.queryText.toString()) }
|
||||
.launchIn(viewScope)
|
||||
|
||||
searchItem.fixExpand(
|
||||
onExpand = { invalidateMenuOnExpand() },
|
||||
@@ -300,6 +278,10 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
menu.findItem(displayItem).isChecked = true
|
||||
}
|
||||
|
||||
override fun onSearchViewQueryTextSubmit(query: String?) {
|
||||
searchWithQuery(query ?: "")
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
super.onPrepareOptionsMenu(menu)
|
||||
|
||||
|
||||
+4
-6
@@ -66,12 +66,6 @@ open class BrowseSourcePresenter(
|
||||
*/
|
||||
lateinit var source: CatalogueSource
|
||||
|
||||
/**
|
||||
* Query from the view.
|
||||
*/
|
||||
var query = searchQuery ?: ""
|
||||
private set
|
||||
|
||||
/**
|
||||
* Modifiable list of filters.
|
||||
*/
|
||||
@@ -108,6 +102,10 @@ open class BrowseSourcePresenter(
|
||||
*/
|
||||
private var pageSubscription: Subscription? = null
|
||||
|
||||
init {
|
||||
query = searchQuery ?: ""
|
||||
}
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
|
||||
+32
-41
@@ -1,12 +1,7 @@
|
||||
package eu.kanade.tachiyomi.ui.browse.source.globalsearch
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.*
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@@ -15,15 +10,10 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.databinding.GlobalSearchControllerBinding
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.SearchableNucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import reactivecircus.flowbinding.appcompat.QueryTextEvent
|
||||
import reactivecircus.flowbinding.appcompat.queryTextEvents
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
/**
|
||||
@@ -34,7 +24,7 @@ import uy.kohesive.injekt.injectLazy
|
||||
open class GlobalSearchController(
|
||||
protected val initialQuery: String? = null,
|
||||
protected val extensionFilter: String? = null
|
||||
) : NucleusController<GlobalSearchControllerBinding, GlobalSearchPresenter>(),
|
||||
) : SearchableNucleusController<GlobalSearchControllerBinding, GlobalSearchPresenter>(),
|
||||
GlobalSearchCardAdapter.OnMangaClickListener,
|
||||
GlobalSearchAdapter.OnTitleClickListener {
|
||||
|
||||
@@ -45,6 +35,11 @@ open class GlobalSearchController(
|
||||
*/
|
||||
protected var adapter: GlobalSearchAdapter? = null
|
||||
|
||||
/**
|
||||
* Ref to the OptionsMenu.SearchItem created in onCreateOptionsMenu
|
||||
*/
|
||||
private var optionsMenuSearchItem: MenuItem? = null
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
@@ -100,36 +95,32 @@ open class GlobalSearchController(
|
||||
* @param inflater used to load the menu xml.
|
||||
*/
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
// Inflate menu.
|
||||
inflater.inflate(R.menu.global_search, menu)
|
||||
|
||||
// Initialize search menu
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
searchView.maxWidth = Int.MAX_VALUE
|
||||
|
||||
searchItem.setOnActionExpandListener(
|
||||
object : MenuItem.OnActionExpandListener {
|
||||
override fun onMenuItemActionExpand(item: MenuItem?): Boolean {
|
||||
searchView.onActionViewExpanded() // Required to show the query in the view
|
||||
searchView.setQuery(presenter.query, false)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem?): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
createOptionsMenu(
|
||||
menu,
|
||||
inflater,
|
||||
R.menu.global_search,
|
||||
R.id.action_search,
|
||||
null,
|
||||
false // the onMenuItemActionExpand will handle this
|
||||
)
|
||||
|
||||
searchView.queryTextEvents()
|
||||
.filterIsInstance<QueryTextEvent.QuerySubmitted>()
|
||||
.onEach {
|
||||
presenter.search(it.queryText.toString())
|
||||
searchItem.collapseActionView()
|
||||
setTitle() // Update toolbar title
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
optionsMenuSearchItem = menu.findItem(R.id.action_search)
|
||||
}
|
||||
|
||||
override fun onSearchMenuItemActionExpand(item: MenuItem?) {
|
||||
super.onSearchMenuItemActionExpand(item)
|
||||
val searchView = optionsMenuSearchItem?.actionView as SearchView
|
||||
searchView.onActionViewExpanded() // Required to show the query in the view
|
||||
|
||||
if (nonSubmittedQuery.isBlank()) {
|
||||
searchView.setQuery(presenter.query, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSearchViewQueryTextSubmit(query: String?) {
|
||||
presenter.search(query ?: "")
|
||||
optionsMenuSearchItem?.collapseActionView()
|
||||
setTitle() // Update toolbar title
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-6
@@ -47,12 +47,6 @@ open class GlobalSearchPresenter(
|
||||
*/
|
||||
val sources by lazy { getSourcesToQuery() }
|
||||
|
||||
/**
|
||||
* Query from the view.
|
||||
*/
|
||||
var query = ""
|
||||
private set
|
||||
|
||||
/**
|
||||
* Fetches the different sources by user settings.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user