diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index fdbec1ead..61a9130cd 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,8 +1,6 @@
-
-
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 7bfef59df..d5d35ec44 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/app/build.gradle b/app/build.gradle
index be4098181..73178ec24 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -60,8 +60,8 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.6'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.6'
- implementation 'androidx.core:core-ktx:1.3.0-rc01'
- implementation 'androidx.appcompat:appcompat:1.2.0-rc01'
+ implementation 'androidx.core:core-ktx:1.4.0-alpha01'
+ implementation 'androidx.appcompat:appcompat:1.3.0-alpha01'
implementation 'androidx.activity:activity-ktx:1.2.0-alpha05'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha05'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha03'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7d5df3091..b4dcb53c3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -69,6 +69,8 @@
+
> = 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))
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/common/list/ProgressBarAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/common/list/ProgressBarAdapter.kt
new file mode 100644
index 000000000..8e6ca78f0
--- /dev/null
+++ b/app/src/main/java/org/koitharu/kotatsu/ui/common/list/ProgressBarAdapter.kt
@@ -0,0 +1,24 @@
+package org.koitharu.kotatsu.ui.common.list
+
+import android.view.ViewGroup
+
+class ProgressBarAdapter : BaseRecyclerAdapter() {
+
+ var isVisible: Boolean
+ get() = dataSet.isNotEmpty()
+ set(value) {
+ if (value == dataSet.isEmpty()) {
+ if (value) {
+ appendItem(true)
+ } else {
+ removeItemAt(0)
+ }
+ }
+ }
+
+ override fun getExtra(item: Boolean, position: Int) = Unit
+
+ override fun onCreateViewHolder(parent: ViewGroup) = ProgressBarHolder(parent)
+
+ override fun onGetItemId(item: Boolean) = 1L
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/common/list/ProgressBarHolder.kt b/app/src/main/java/org/koitharu/kotatsu/ui/common/list/ProgressBarHolder.kt
new file mode 100644
index 000000000..0b21be9a7
--- /dev/null
+++ b/app/src/main/java/org/koitharu/kotatsu/ui/common/list/ProgressBarHolder.kt
@@ -0,0 +1,18 @@
+package org.koitharu.kotatsu.ui.common.list
+
+import android.view.View
+import android.view.ViewGroup
+import kotlinx.android.synthetic.main.item_progress.*
+import org.koitharu.kotatsu.R
+
+class ProgressBarHolder(parent: ViewGroup) :
+ BaseViewHolder(parent, R.layout.item_progress) {
+
+ override fun onBind(data: Boolean, extra: Unit) {
+ progressBar.visibility = if (data) {
+ View.VISIBLE
+ } else {
+ View.INVISIBLE
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/list/MainActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/list/MainActivity.kt
index b2aabcf64..1c1c62590 100644
--- a/app/src/main/java/org/koitharu/kotatsu/ui/list/MainActivity.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/ui/list/MainActivity.kt
@@ -32,6 +32,7 @@ import org.koitharu.kotatsu.ui.list.remote.RemoteListFragment
import org.koitharu.kotatsu.ui.list.tracklogs.FeedFragment
import org.koitharu.kotatsu.ui.reader.ReaderActivity
import org.koitharu.kotatsu.ui.reader.ReaderState
+import org.koitharu.kotatsu.ui.search.SearchHelper
import org.koitharu.kotatsu.ui.settings.AppUpdateService
import org.koitharu.kotatsu.ui.settings.SettingsActivity
import org.koitharu.kotatsu.ui.tracker.TrackWorker
@@ -91,8 +92,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
drawerToggle.onConfigurationChanged(newConfig)
}
- override fun onCreateOptionsMenu(menu: Menu?): Boolean {
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.opt_main, menu)
+ menu.findItem(R.id.action_search)?.let { menuItem ->
+ SearchHelper.setupSearchView(menuItem)
+ }
return super.onCreateOptionsMenu(menu)
}
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
index ee2cd56fd..aed6383cd 100644
--- a/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchHelper.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/SearchHelper.kt
@@ -7,6 +7,7 @@ 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 {
@@ -21,12 +22,26 @@ object SearchHelper {
view.setOnSuggestionListener(SuggestionListener(view))
}
- private class QueryListener(private val context: Context, private val source: MangaSource) :
+ @JvmStatic
+ fun setupSearchView(menuItem: MenuItem) {
+ 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))
+ view.setOnSuggestionListener(SuggestionListener(view))
+ }
+
+ private class QueryListener(private val context: Context, private val source: MangaSource? = null) :
SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return if (!query.isNullOrBlank()) {
- context.startActivity(SearchActivity.newIntent(context, source, query.trim()))
+ if (source == null) {
+ context.startActivity(GlobalSearchActivity.newIntent(context, query.trim()))
+ } else {
+ context.startActivity(SearchActivity.newIntent(context, source, query.trim()))
+ }
MangaSuggestionsProvider.saveQuery(context, query)
true
} else false
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 1c74ea3c4..1c436fdbc 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
@@ -15,13 +15,6 @@ import org.koitharu.kotatsu.ui.list.MangaListView
@InjectViewState
class SearchPresenter : BasePresenter>() {
- private lateinit var sources: Array
-
- override fun onFirstViewAttach() {
- sources = MangaSource.values()
- super.onFirstViewAttach()
- }
-
fun loadList(source: MangaSource, query: String, offset: Int) {
presenterScope.launch {
viewState.onLoadingStateChanged(true)
diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/global/GlobalSearchActivity.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/global/GlobalSearchActivity.kt
new file mode 100644
index 000000000..0c5ce0822
--- /dev/null
+++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/global/GlobalSearchActivity.kt
@@ -0,0 +1,38 @@
+package org.koitharu.kotatsu.ui.search.global
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import org.koitharu.kotatsu.R
+import org.koitharu.kotatsu.ui.common.BaseActivity
+
+class GlobalSearchActivity : BaseActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_search)
+ val query = intent.getStringExtra(EXTRA_QUERY)
+
+ if (query == null) {
+ finish()
+ return
+ }
+
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ title = query
+ supportActionBar?.subtitle = getString(R.string.search_results)
+ supportFragmentManager
+ .beginTransaction()
+ .replace(R.id.container, GlobalSearchFragment.newInstance(query))
+ .commit()
+ }
+
+ companion object {
+
+ private const val EXTRA_QUERY = "query"
+
+ fun newIntent(context: Context, query: String) =
+ Intent(context, GlobalSearchActivity::class.java)
+ .putExtra(EXTRA_QUERY, query)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/global/GlobalSearchFragment.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/global/GlobalSearchFragment.kt
new file mode 100644
index 000000000..5a08335ba
--- /dev/null
+++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/global/GlobalSearchFragment.kt
@@ -0,0 +1,32 @@
+package org.koitharu.kotatsu.ui.search.global
+
+import moxy.ktx.moxyPresenter
+import org.koitharu.kotatsu.ui.list.MangaListFragment
+import org.koitharu.kotatsu.utils.ext.withArgs
+
+
+class GlobalSearchFragment: MangaListFragment() {
+
+ private val presenter by moxyPresenter(factory = ::GlobalSearchPresenter)
+
+ private val query by stringArg(ARG_QUERY)
+
+ override fun onRequestMoreItems(offset: Int) {
+ if (offset == 0) {
+ presenter.startSearch(query.orEmpty())
+ }
+ }
+
+ override fun getTitle(): CharSequence? {
+ return query
+ }
+
+ companion object {
+
+ private const val ARG_QUERY = "query"
+
+ fun newInstance(query: String) = GlobalSearchFragment().withArgs(1) {
+ putString(ARG_QUERY, query)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/ui/search/global/GlobalSearchPresenter.kt b/app/src/main/java/org/koitharu/kotatsu/ui/search/global/GlobalSearchPresenter.kt
new file mode 100644
index 000000000..7227fa02e
--- /dev/null
+++ b/app/src/main/java/org/koitharu/kotatsu/ui/search/global/GlobalSearchPresenter.kt
@@ -0,0 +1,49 @@
+package org.koitharu.kotatsu.ui.search.global
+
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.onEmpty
+import kotlinx.coroutines.launch
+import moxy.presenterScope
+import org.koitharu.kotatsu.domain.MangaSearchRepository
+import org.koitharu.kotatsu.ui.common.BasePresenter
+import org.koitharu.kotatsu.ui.list.MangaListView
+import org.koitharu.kotatsu.utils.ext.onFirst
+import java.io.IOException
+
+class GlobalSearchPresenter : BasePresenter>() {
+
+ private lateinit var repository: MangaSearchRepository
+
+ override fun onFirstViewAttach() {
+ repository = MangaSearchRepository()
+ super.onFirstViewAttach()
+ }
+
+ @Suppress("EXPERIMENTAL_API_USAGE")
+ fun startSearch(query: String) {
+ presenterScope.launch {
+ viewState.onLoadingStateChanged(isLoading = true)
+ repository.globalSearch(query)
+ .flowOn(Dispatchers.IO)
+ .catch { e ->
+ if (e is IOException) {
+ viewState.onError(e)
+ }
+ }
+ .onFirst {
+ viewState.onLoadingStateChanged(isLoading = false)
+ }
+ .onEmpty {
+ viewState.onListChanged(emptyList())
+ viewState.onLoadingStateChanged(isLoading = false)
+ }
+ .collect {
+ viewState.onListAppended(it)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoroutineExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoroutineExt.kt
index 67deab49f..c99cf4630 100644
--- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoroutineExt.kt
+++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/CoroutineExt.kt
@@ -1,5 +1,7 @@
package org.koitharu.kotatsu.utils.ext
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.suspendCancellableCoroutine
import okhttp3.Call
import okhttp3.Callback
@@ -25,4 +27,14 @@ suspend fun Call.await() = suspendCancellableCoroutine { cont ->
this.cancel()
}
}
+}
+
+fun Flow.onFirst(action: suspend (T) -> Unit): Flow {
+ var isFirstCall = true
+ return onEach {
+ if (isFirstCall) {
+ action(it)
+ isFirstCall = false
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_progress.xml b/app/src/main/res/layout/item_progress.xml
new file mode 100644
index 000000000..2ca64e856
--- /dev/null
+++ b/app/src/main/res/layout/item_progress.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
\ 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 1fe7aa6d4..e7c152f16 100644
--- a/app/src/main/res/menu/opt_main.xml
+++ b/app/src/main/res/menu/opt_main.xml
@@ -1,2 +1,14 @@
-
\ No newline at end of file
+
\ 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
index 82a1adbcc..598d68e1d 100644
--- a/app/src/main/res/menu/opt_remote.xml
+++ b/app/src/main/res/menu/opt_remote.xml
@@ -4,7 +4,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
- Прочитать позже
Обновления
Here you will see manga updates
+ Результаты поиска
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d9f3e96f7..526a8e6c3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -138,4 +138,5 @@
Read later
Updates
Here you will see manga updates
+ Search results
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 1b7a78987..08e69a267 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.1.0-alpha09'
+ classpath 'com.android.tools.build:gradle:4.1.0-alpha10'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 5ac04b8e9..8a314251c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sat May 16 07:20:00 EEST 2020
+#Sat May 30 09:45:56 EEST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-milestone-1-all.zip