Search manga

This commit is contained in:
Koitharu
2020-02-06 20:48:47 +02:00
parent 1aee900587
commit b69c624442
13 changed files with 175 additions and 17 deletions

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.koitharu.kotatsu">
<uses-permission android:name="android.permission.INTERNET" />
@@ -16,12 +17,22 @@
<activity android:name=".ui.main.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.default_searchable"
android:value=".ui.search.SearchActivity" />
</activity>
<activity android:name=".ui.details.MangaDetailsActivity" />
<activity android:name=".ui.reader.ReaderActivity" />
<activity android:name=".ui.search.SearchActivity">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data
android:name="android.app.searchable"
android:resource="@xml/search" />
</activity>
</application>
</manifest>

View File

@@ -10,8 +10,6 @@ abstract class BaseMangaRepository(protected val loaderContext: MangaLoaderConte
override val sortOrders: Set<SortOrder> get() = emptySet()
override val isSearchAvailable get() = true
override suspend fun getPageFullUrl(page: MangaPage) : String = page.url
override suspend fun getTags(): Set<MangaTag> = emptySet()

View File

@@ -6,8 +6,6 @@ interface MangaRepository {
val sortOrders: Set<SortOrder>
val isSearchAvailable: Boolean
suspend fun getList(offset: Int, query: String? = null, sortOrder: SortOrder? = null, tag: MangaTag? = null): List<Manga>
suspend fun getDetails(manga: Manga) : Manga

View File

@@ -25,13 +25,11 @@ abstract class GroupleRepository(
sortOrder: SortOrder?,
tag: MangaTag?
): List<Manga> {
val url = if (tag == null) {
"https://$domain/list?sortType=${getSortKey(sortOrder)}&offset=$offset"
} else {
"https://$domain/list/genre/${tag.key}?sortType=${getSortKey(sortOrder)}&offset=$offset"
}
val doc = loaderContext.get(url)
.parseHtml()
val doc = when {
!query.isNullOrEmpty() -> loaderContext.post("https://$domain/search", mapOf("q" to query))
tag == null -> loaderContext.get("https://$domain/list?sortType=${getSortKey(sortOrder)}&offset=$offset")
else -> loaderContext.get( "https://$domain/list/genre/${tag.key}?sortType=${getSortKey(sortOrder)}&offset=$offset")
}.parseHtml()
val root = doc.body().getElementById("mangaBox")
?.selectFirst("div.tiles.row") ?: throw ParseException("Cannot find root")
return root.select("div.tile").mapNotNull { node ->

View File

@@ -1,13 +1,13 @@
package org.koitharu.kotatsu.domain
import org.koin.core.KoinComponent
import org.koin.core.get
import org.koin.core.inject
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.parser.MangaRepository
object MangaProviderFactory : KoinComponent {
private val loaderContext get() = get<MangaLoaderContext>()
private val loaderContext by inject<MangaLoaderContext>()
fun create(source: MangaSource): MangaRepository {
val constructor = source.cls.getConstructor(MangaLoaderContext::class.java)

View File

@@ -57,7 +57,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return drawerToggle.onOptionsItemSelected(item) || when(item.itemId) {
else -> super.onOptionsItemSelected(item)
}
}

View File

@@ -0,0 +1,31 @@
package org.koitharu.kotatsu.ui.search
import android.app.SearchManager
import android.content.Intent
import android.os.Bundle
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.ui.common.BaseActivity
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) {
finish()
return
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
title = query
supportActionBar?.setSubtitle(R.string.search_results)
supportFragmentManager
.beginTransaction()
.replace(R.id.container, SearchFragment.newInstance(query))
.commit()
}
}

View File

@@ -0,0 +1,29 @@
package org.koitharu.kotatsu.ui.search
import moxy.ktx.moxyPresenter
import org.koitharu.kotatsu.ui.main.list.MangaListFragment
import org.koitharu.kotatsu.utils.ext.withArgs
class SearchFragment : MangaListFragment<Unit>() {
private val presenter by moxyPresenter(factory = ::SearchPresenter)
private val query by stringArg(ARG_QUERY)
override fun onRequestMoreItems(offset: Int) {
presenter.loadList(query.orEmpty(), offset)
}
override fun getTitle(): CharSequence? {
return query
}
companion object {
private const val ARG_QUERY = "query"
fun newInstance(query: String) = SearchFragment().withArgs(1) {
putString(ARG_QUERY, query)
}
}
}

View File

@@ -0,0 +1,47 @@
package org.koitharu.kotatsu.ui.search
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import moxy.InjectViewState
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.common.BasePresenter
import org.koitharu.kotatsu.ui.main.list.MangaListView
@InjectViewState
class SearchPresenter : BasePresenter<MangaListView<Unit>>() {
private lateinit var sources: Array<MangaSource>
override fun onFirstViewAttach() {
sources = MangaSource.values()
super.onFirstViewAttach()
}
fun loadList(query: String, offset: Int) {
launch {
viewState.onLoadingChanged(true)
try {
//TODO select source
val list = withContext(Dispatchers.IO) {
MangaProviderFactory.create(MangaSource.READMANGA_RU)
.getList(offset, query = query)
}
if (offset == 0) {
viewState.onListChanged(list)
} else {
viewState.onListAppended(list)
}
} catch (e: Exception) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
viewState.onError(e)
} finally {
viewState.onLoadingChanged(false)
}
}
}
}

View File

@@ -1,13 +1,20 @@
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
//TODO
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)
}
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar">
<androidx.appcompat.widget.Toolbar
android:id="@id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/ThemeOverlay.MaterialComponents.Light" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="@id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -35,4 +35,6 @@
<string name="create_shortcut">Create shortcut…</string>
<string name="share_s">Share %s</string>
<string name="search">Search</string>
<string name="search_manga">Search manga</string>
<string name="search_results">Search results</string>
</resources>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<searchable
xmlns:android="http://schemas.android.com/apk/res/android"
android:hint="@string/search_manga"
android:inputType="textPersonName"
android:label="@string/app_name"
android:voiceLanguageModel="web_search"
android:voiceSearchMode="showVoiceSearchButton|launchRecognizer" />