From 5f490309264d0a45ea67e1c1c9432a7f49aec94c Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 29 Feb 2020 11:52:53 +0200 Subject: [PATCH] Single-provider search --- app/src/main/AndroidManifest.xml | 17 ++-- .../domain/search/MangaSuggestionsProvider.kt | 29 ------ .../koitharu/kotatsu/ui/main/MainActivity.kt | 2 - .../ui/main/list/remote/RemoteListFragment.kt | 12 +++ .../ui/search/MangaSuggestionsProvider.kt | 91 +++++++++++++++++++ .../kotatsu/ui/search/SearchActivity.kt | 30 ++++-- .../kotatsu/ui/search/SearchFragment.kt | 8 +- .../kotatsu/ui/search/SearchHelper.kt | 55 +++++++++++ .../kotatsu/ui/search/SearchPresenter.kt | 5 +- .../koitharu/kotatsu/utils/SearchHelper.kt | 20 ---- app/src/main/res/layout/activity_details.xml | 2 +- app/src/main/res/layout/activity_main.xml | 5 +- app/src/main/res/layout/activity_reader.xml | 4 +- app/src/main/res/layout/activity_search.xml | 2 +- app/src/main/res/layout/activity_settings.xml | 2 +- .../main/res/layout/item_search_complete.xml | 16 ++++ app/src/main/res/menu/opt_main.xml | 8 +- app/src/main/res/menu/opt_remote.xml | 14 +++ app/src/main/res/values-night/styles.xml | 4 + app/src/main/res/values/styles.xml | 9 ++ app/src/main/res/xml/search.xml | 10 -- 21 files changed, 243 insertions(+), 102 deletions(-) delete mode 100644 app/src/main/java/org/koitharu/kotatsu/domain/search/MangaSuggestionsProvider.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/search/MangaSuggestionsProvider.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/ui/search/SearchHelper.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/utils/SearchHelper.kt create mode 100644 app/src/main/res/layout/item_search_complete.xml create mode 100644 app/src/main/res/menu/opt_remote.xml delete mode 100644 app/src/main/res/xml/search.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 50907769f..4e3b89737 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,10 +14,10 @@ android:fullBackupContent="@xml/backup_descriptor" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" - android:usesCleartextTraffic="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:theme="@style/AppTheme"> + android:theme="@style/AppTheme" + android:usesCleartextTraffic="true"> @@ -29,14 +29,9 @@ - - - - - - + @@ -44,7 +39,7 @@ () { @@ -25,6 +29,14 @@ class RemoteListFragment : MangaListFragment() { super.onFilterChanged(filter) } + 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) + } + companion object { private const val ARG_SOURCE = "provider" diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/MangaSuggestionsProvider.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/MangaSuggestionsProvider.kt new file mode 100644 index 000000000..5d1bd3ca9 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/MangaSuggestionsProvider.kt @@ -0,0 +1,91 @@ +package org.koitharu.kotatsu.ui.search + +import android.app.SearchManager +import android.content.ContentResolver +import android.content.Context +import android.content.SearchRecentSuggestionsProvider +import android.database.Cursor +import android.net.Uri +import android.provider.SearchRecentSuggestions +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.cursoradapter.widget.CursorAdapter +import org.koitharu.kotatsu.BuildConfig +import org.koitharu.kotatsu.R + +class MangaSuggestionsProvider : SearchRecentSuggestionsProvider() { + + init { + setupSuggestions( + AUTHORITY, + MODE + ) + } + + private class SearchSuggestionAdapter(context: Context, cursor: Cursor) : CursorAdapter( + context, cursor, + FLAG_REGISTER_CONTENT_OBSERVER + ) { + + override fun newView(context: Context, cursor: Cursor?, parent: ViewGroup?): View { + return LayoutInflater.from(context) + .inflate(R.layout.item_search_complete, parent, false) + } + + override fun bindView(view: View, context: Context, cursor: Cursor) { + if (view !is TextView) return + view.text = cursor.getString(cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY)) + } + + override fun convertToString(cursor: Cursor?): CharSequence { + return cursor?.getString(cursor.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY)) + .orEmpty() + } + } + + companion object { + + private const val AUTHORITY = "${BuildConfig.APPLICATION_ID}.MangaSuggestionsProvider" + private const val MODE = DATABASE_MODE_QUERIES + + private val uri = Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(AUTHORITY) + .appendPath(SearchManager.SUGGEST_URI_PATH_QUERY) + .build() + private val projection = arrayOf("_id", SearchManager.SUGGEST_COLUMN_QUERY) + + fun saveQuery(context: Context, query: String) { + SearchRecentSuggestions( + context, + AUTHORITY, + MODE + ).saveRecentQuery(query, null) + } + + fun clearHistory(context: Context) { + SearchRecentSuggestions( + context, + AUTHORITY, + MODE + ).clearHistory() + } + + private fun getCursor(context: Context): Cursor? { + return context.contentResolver?.query(uri, projection, null, arrayOf(""), null) + } + + fun getSuggestionAdapter(context: Context): CursorAdapter? = getCursor( + context + )?.let { cursor -> + SearchSuggestionAdapter(context, cursor).also { + it.setFilterQueryProvider { q -> + context.contentResolver?.query(uri, projection, " ?", arrayOf(q.toString()), null) + } + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchActivity.kt index 13ca3dc9d..89035096e 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchActivity.kt @@ -1,10 +1,11 @@ package org.koitharu.kotatsu.ui.search -import android.app.SearchManager +import android.content.Context import android.content.Intent import android.os.Bundle +import android.os.Parcelable import org.koitharu.kotatsu.R -import org.koitharu.kotatsu.domain.search.MangaSuggestionsProvider +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.ui.common.BaseActivity class SearchActivity : BaseActivity() { @@ -12,22 +13,31 @@ class SearchActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_search) - val query = if (Intent.ACTION_SEARCH == intent.action) { - intent.getStringExtra(SearchManager.QUERY)?.trim() - } else { - null - } - if (query == null) { + val source = intent.getParcelableExtra(EXTRA_SOURCE) + val query = intent.getStringExtra(EXTRA_QUERY) + + if (source == null || query == null) { finish() return } - MangaSuggestionsProvider.saveQuery(this, query) + supportActionBar?.setDisplayHomeAsUpEnabled(true) title = query supportActionBar?.setSubtitle(R.string.search_results) supportFragmentManager .beginTransaction() - .replace(R.id.container, SearchFragment.newInstance(query)) + .replace(R.id.container, SearchFragment.newInstance(source, query)) .commit() } + + companion object { + + private const val EXTRA_SOURCE = "source" + private const val EXTRA_QUERY = "query" + + fun newIntent(context: Context, source: MangaSource, query: String) = + Intent(context, SearchActivity::class.java) + .putExtra(EXTRA_SOURCE, source as Parcelable) + .putExtra(EXTRA_QUERY, query) + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchFragment.kt index fd17b41cd..0cf073fc1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchFragment.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.ui.search import moxy.ktx.moxyPresenter +import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.ui.main.list.MangaListFragment import org.koitharu.kotatsu.utils.ext.withArgs @@ -9,9 +10,10 @@ class SearchFragment : MangaListFragment() { private val presenter by moxyPresenter(factory = ::SearchPresenter) private val query by stringArg(ARG_QUERY) + private val source by arg(ARG_SOURCE) override fun onRequestMoreItems(offset: Int) { - presenter.loadList(query.orEmpty(), offset) + presenter.loadList(source, query.orEmpty(), offset) } override fun getTitle(): CharSequence? { @@ -21,8 +23,10 @@ class SearchFragment : MangaListFragment() { companion object { private const val ARG_QUERY = "query" + private const val ARG_SOURCE = "source" - fun newInstance(query: String) = SearchFragment().withArgs(1) { + fun newInstance(source: MangaSource, query: String) = SearchFragment().withArgs(2) { + putParcelable(ARG_SOURCE, source) putString(ARG_QUERY, query) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchHelper.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchHelper.kt new file mode 100644 index 000000000..1adbf694b --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchHelper.kt @@ -0,0 +1,55 @@ +package org.koitharu.kotatsu.ui.search + +import android.app.SearchManager +import android.content.Context +import android.database.Cursor +import android.view.MenuItem +import android.view.inputmethod.EditorInfo +import androidx.appcompat.widget.SearchView +import org.koitharu.kotatsu.R +import org.koitharu.kotatsu.core.model.MangaSource +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.imeOptions = EditorInfo.IME_ACTION_SEARCH + view.inputType = EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE + view.suggestionsAdapter = MangaSuggestionsProvider.getSuggestionAdapter(context) + view.setOnQueryTextListener(QueryListener(context, source)) + view.setOnSuggestionListener(SuggestionListener(view)) + } + + private class QueryListener(private val context: Context, private val source: MangaSource) : + SearchView.OnQueryTextListener { + + override fun onQueryTextSubmit(query: String?): Boolean { + return if (!query.isNullOrBlank()) { + context.startActivity(SearchActivity.newIntent(context, source, query.trim())) + MangaSuggestionsProvider.saveQuery(context, query) + true + } else false + } + + override fun onQueryTextChange(newText: String?) = false + } + + private class SuggestionListener(private val view: SearchView) : + SearchView.OnSuggestionListener { + + override fun onSuggestionSelect(position: Int) = false + + override fun onSuggestionClick(position: Int): Boolean { + val query = safe { + val c = view.suggestionsAdapter.getItem(position) as? Cursor + c?.getString(c.getColumnIndex(SearchManager.SUGGEST_COLUMN_QUERY)) + } ?: return false + view.setQuery(query, true) + return true + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchPresenter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchPresenter.kt index b9db5ca6d..84abd7ddb 100644 --- a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchPresenter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchPresenter.kt @@ -21,13 +21,12 @@ class SearchPresenter : BasePresenter>() { super.onFirstViewAttach() } - fun loadList(query: String, offset: Int) { + fun loadList(source: MangaSource, query: String, offset: Int) { presenterScope.launch { viewState.onLoadingChanged(true) try { - //TODO select source val list = withContext(Dispatchers.IO) { - MangaProviderFactory.create(MangaSource.READMANGA_RU) + MangaProviderFactory.create(source) .getList(offset, query = query) } if (offset == 0) { diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/SearchHelper.kt b/app/src/main/java/org/koitharu/kotatsu/utils/SearchHelper.kt deleted file mode 100644 index 8d7c928fa..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/utils/SearchHelper.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.koitharu.kotatsu.utils - -import android.app.SearchManager -import android.content.ComponentName -import android.content.Context -import android.view.MenuItem -import androidx.appcompat.widget.SearchView -import org.koitharu.kotatsu.ui.search.SearchActivity - -object SearchHelper { - - @JvmStatic - fun setupSearchView(menuItem: MenuItem) { - val view = menuItem.actionView as? SearchView ?: return - val context = view.context - val searchManager = context.applicationContext.getSystemService(Context.SEARCH_SERVICE) as SearchManager - val info = searchManager.getSearchableInfo(ComponentName(context, SearchActivity::class.java)) - view.setSearchableInfo(info) - } -} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_details.xml b/app/src/main/res/layout/activity_details.xml index c76924fe0..caa3f66b1 100644 --- a/app/src/main/res/layout/activity_details.xml +++ b/app/src/main/res/layout/activity_details.xml @@ -10,7 +10,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?colorPrimary" - android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"> + android:theme="@style/AppToolbarTheme"> + android:theme="@style/AppToolbarTheme"> + app:layout_scrollFlags="scroll|enterAlways" /> diff --git a/app/src/main/res/layout/activity_reader.xml b/app/src/main/res/layout/activity_reader.xml index 07dcb89ab..16201aeeb 100644 --- a/app/src/main/res/layout/activity_reader.xml +++ b/app/src/main/res/layout/activity_reader.xml @@ -28,7 +28,7 @@ android:background="@color/dim" android:elevation="0dp" android:fitsSystemWindows="true" - android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" + android:theme="@style/AppToolbarTheme" app:elevation="0dp"> + android:theme="@style/AppToolbarTheme"> + android:theme="@style/AppToolbarTheme"> + \ No newline at end of file diff --git a/app/src/main/res/menu/opt_main.xml b/app/src/main/res/menu/opt_main.xml index 15f6118e3..b730bac92 100644 --- a/app/src/main/res/menu/opt_main.xml +++ b/app/src/main/res/menu/opt_main.xml @@ -3,12 +3,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> - + \ No newline at end of file diff --git a/app/src/main/res/menu/opt_remote.xml b/app/src/main/res/menu/opt_remote.xml new file mode 100644 index 000000000..82a1adbcc --- /dev/null +++ b/app/src/main/res/menu/opt_remote.xml @@ -0,0 +1,14 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 050fe5805..65256236f 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -1,4 +1,8 @@ \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3bd21339b..fa6c6d0a4 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -10,4 +10,13 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/xml/search.xml b/app/src/main/res/xml/search.xml deleted file mode 100644 index df926a5b3..000000000 --- a/app/src/main/res/xml/search.xml +++ /dev/null @@ -1,10 +0,0 @@ - -