diff --git a/app/src/main/java/org/koitharu/kotatsu/core/model/MangaFilter.kt b/app/src/main/java/org/koitharu/kotatsu/core/model/MangaFilter.kt index 814c00571..498492f24 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/model/MangaFilter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/model/MangaFilter.kt @@ -6,5 +6,5 @@ import kotlinx.parcelize.Parcelize @Parcelize data class MangaFilter( val sortOrder: SortOrder?, - val tag: MangaTag?, + val tags: Set, ) : Parcelable \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaRepository.kt index fc960f069..c8904b2a8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/MangaRepository.kt @@ -9,24 +9,12 @@ interface MangaRepository { val sortOrders: Set - suspend fun getList( + suspend fun getList2( offset: Int, query: String? = null, tags: Set? = null, sortOrder: SortOrder? = null, - ): List = if (tags == null || tags.size <= 1) { - getList(offset, query, sortOrder, tags?.singleOrNull()) - } else { - throw NotImplementedError("Multiple filter are not supported by this source yet") - } - - @Deprecated("Use multiple tag variant") - suspend fun getList( - offset: Int, - query: String? = null, - sortOrder: SortOrder? = null, - tag: MangaTag? = null, - ): List = throw NotImplementedError("This is fine") + ): List suspend fun getDetails(manga: Manga): Manga diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/AnibelRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/AnibelRepository.kt index 6142f71d3..bb3622afc 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/AnibelRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/AnibelRepository.kt @@ -16,7 +16,7 @@ class AnibelRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor SortOrder.NEWEST ) - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, tags: Set?, diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt index 242686815..629082010 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ChanRepository.kt @@ -17,11 +17,11 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe SortOrder.ALPHABETICAL ) - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, - sortOrder: SortOrder?, - tag: MangaTag? + tags: Set?, + sortOrder: SortOrder? ): List { val domain = getDomain() val url = when { @@ -31,7 +31,11 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe } "https://$domain/?do=search&subaction=search&story=${query.urlEncoded()}" } - tag != null -> "https://$domain/tags/${tag.key}&n=${getSortKey2(sortOrder)}?offset=$offset" + !tags.isNullOrEmpty() -> tags.joinToString( + prefix = "https://$domain/tags/", + postfix = "&n=${getSortKey2(sortOrder)}?offset=$offset", + separator = "+", + ) { tag -> tag.key } else -> "https://$domain/${getSortKey(sortOrder)}?offset=$offset" } val doc = loaderContext.httpGet(url).parseHtml() diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt index 4d62b7af5..73b223b82 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/DesuMeRepository.kt @@ -20,11 +20,11 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor SortOrder.ALPHABETICAL ) - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, - sortOrder: SortOrder?, - tag: MangaTag? + tags: Set?, + sortOrder: SortOrder? ): List { if (query != null && offset != 0) { return emptyList() @@ -37,9 +37,9 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor append(getSortKey(sortOrder)) append("&page=") append((offset / 20) + 1) - if (tag != null) { + if (!tags.isNullOrEmpty()) { append("&genres=") - append(tag.key) + appendAll(tags, ",") { it.key } } if (query != null) { append("&search=") diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ExHentaiRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ExHentaiRepository.kt index a699357e8..bef2af960 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ExHentaiRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/ExHentaiRepository.kt @@ -32,14 +32,7 @@ class ExHentaiRepository( loaderContext.cookieJar.insertCookies(DOMAIN_UNAUTHORIZED, "nw=1", "sl=dm_2") } - override suspend fun getList( - offset: Int, - query: String?, - sortOrder: SortOrder?, - tag: MangaTag?, - ): List = getList(offset, query, setOfNotNull(tag), sortOrder) - - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, tags: Set?, @@ -80,7 +73,7 @@ class ExHentaiRepository( parseFailed("Cannot find root") } else { updateDm = true - return getList(offset, query, tags, sortOrder) + return getList2(offset, query, tags, sortOrder) } updateDm = false return root.children().mapNotNull { tr -> diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt index 268ad03b0..2d35111e8 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/GroupleRepository.kt @@ -1,6 +1,7 @@ package org.koitharu.kotatsu.core.parser.site import okhttp3.HttpUrl.Companion.toHttpUrl +import okhttp3.Response import org.koitharu.kotatsu.base.domain.MangaLoaderContext import org.koitharu.kotatsu.core.exceptions.ParseException import org.koitharu.kotatsu.core.model.* @@ -18,11 +19,11 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) : SortOrder.RATING ) - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, - sortOrder: SortOrder?, - tag: MangaTag? + tags: Set?, + sortOrder: SortOrder? ): List { val domain = getDomain() val doc = when { @@ -33,22 +34,24 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) : "offset" to (offset upBy PAGE_SIZE_SEARCH).toString() ) ) - tag == null -> loaderContext.httpGet( + tags.isNullOrEmpty() -> loaderContext.httpGet( "https://$domain/list?sortType=${ getSortKey( sortOrder ) }&offset=${offset upBy PAGE_SIZE}" ) - else -> loaderContext.httpGet( - "https://$domain/list/genre/${tag.key}?sortType=${ + tags.size == 1 -> loaderContext.httpGet( + "https://$domain/list/genre/${tags.first().key}?sortType=${ getSortKey( sortOrder ) }&offset=${offset upBy PAGE_SIZE}" ) - }.parseHtml() - val root = doc.body().getElementById("mangaBox") + offset > 0 -> return emptyList() + else -> advancedSearch(domain, tags) + }.parseHtml().body() + val root = (doc.getElementById("mangaBox") ?: doc.getElementById("mangaResults")) ?.selectFirst("div.tiles.row") ?: throw ParseException("Cannot find root") val baseHost = root.baseUri().toHttpUrl().host return root.select("div.tile").mapNotNull { node -> @@ -182,6 +185,43 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) : null -> "updated" } + private suspend fun advancedSearch(domain: String, tags: Set): Response { + val url = "https://$domain/search/advanced" + // Step 1: map catalog genres names to advanced-search genres ids + val tagsIndex = loaderContext.httpGet(url).parseHtml() + .body().selectFirst("form.search-form") + ?.select("div.form-group") + ?.get(1) ?: parseFailed("Genres filter element not found") + val tagNames = tags.map { it.title.lowercase() } + val payload = HashMap() + var foundGenres = 0 + tagsIndex.select("li.property").forEach { li -> + val name = li.text().trim().lowercase() + val id = li.selectFirst("input")?.id() + ?: parseFailed("Id for tag $name not found") + payload[id] = if (name in tagNames) { + foundGenres++ + "in" + } else "" + } + if (foundGenres != tags.size) { + parseFailed("Some genres are not found") + } + // Step 2: advanced search + payload["q"] = "" + payload["s_high_rate"] = "" + payload["s_single"] = "" + payload["s_mature"] = "" + payload["s_completed"] = "" + payload["s_translated"] = "" + payload["s_many_chapters"] = "" + payload["s_wait_upload"] = "" + payload["s_sale"] = "" + payload["years"] = "1900,2099" + payload["+"] = "Искать".urlEncoded() + return loaderContext.httpPost(url, payload) + } + private companion object { private const val PAGE_SIZE = 70 diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HenChanRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HenChanRepository.kt index aaad60566..a358f50df 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HenChanRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/HenChanRepository.kt @@ -11,13 +11,13 @@ class HenChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(load override val defaultDomain = "hentaichan.live" override val source = MangaSource.HENCHAN - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, - sortOrder: SortOrder?, - tag: MangaTag? + tags: Set?, + sortOrder: SortOrder? ): List { - return super.getList(offset, query, sortOrder, tag).map { + return super.getList2(offset, query, tags, sortOrder).map { val cover = it.coverUrl if (cover.contains("_blur")) { it.copy(coverUrl = cover.replace("_blur", "")) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt index e2d7fbc5f..ba0ea771d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaLibRepository.kt @@ -26,11 +26,11 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) : SortOrder.NEWEST ) - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, - sortOrder: SortOrder?, - tag: MangaTag? + tags: Set?, + sortOrder: SortOrder? ): List { if (!query.isNullOrEmpty()) { return if (offset == 0) search(query) else emptyList() @@ -43,8 +43,8 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) : append(getSortKey(sortOrder)) append("&page=") append(page) - if (tag != null) { - append("&includeGenres[]=") + tags?.forEach { tag -> + append("&genres[include][]=") append(tag.key) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt index 44fd5937e..bf5ce1b8f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangaTownRepository.kt @@ -23,11 +23,11 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) : SortOrder.UPDATED ) - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, - sortOrder: SortOrder?, - tag: MangaTag? + tags: Set?, + sortOrder: SortOrder? ): List { val sortKey = when (sortOrder) { SortOrder.ALPHABETICAL -> "?name.az" @@ -43,8 +43,13 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) : } "/search?name=${query.urlEncoded()}".withDomain() } - tag != null -> "/directory/${tag.key}/$page.htm$sortKey".withDomain() - else -> "/directory/$page.htm$sortKey".withDomain() + tags.isNullOrEmpty() -> "/directory/$page.htm$sortKey".withDomain() + tags.size == 1 -> "/directory/${tags.first().key}/$page.htm$sortKey".withDomain() + else -> tags.joinToString( + prefix = "/search?page=$page".withDomain() + ) { tag -> + "&genres[${tag.key}]=1" + } } val doc = loaderContext.httpGet(url).parseHtml() val root = doc.body().selectFirst("ul.manga_pic_list") diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt index ee69048bb..0a94c3d04 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/MangareadRepository.kt @@ -20,12 +20,17 @@ class MangareadRepository( SortOrder.POPULARITY ) - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, - sortOrder: SortOrder?, - tag: MangaTag? + tags: Set?, + sortOrder: SortOrder? ): List { + val tag = when { + tags.isNullOrEmpty() -> null + tags.size == 1 -> tags.first() + else -> throw NotImplementedError("Multiple genres are not supported by this source") + } val payload = createRequestTemplate() payload["page"] = (offset / PAGE_SIZE.toFloat()).toIntUp().toString() payload["vars[meta_key]"] = when (sortOrder) { diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt index bbb236521..9c67b2146 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/NineMangaRepository.kt @@ -23,7 +23,7 @@ abstract class NineMangaRepository( SortOrder.POPULARITY, ) - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, tags: Set?, @@ -146,7 +146,7 @@ abstract class NineMangaRepository( val cateId = li.attr("cate_id") ?: return@mapNotNullToSet null val a = li.selectFirst("a") ?: return@mapNotNullToSet null MangaTag( - title = a.text(), + title = a.text().toTitleCase(), key = cateId, source = source ) diff --git a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/RemangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/RemangaRepository.kt index f368db41e..17a3dd5d7 100644 --- a/app/src/main/java/org/koitharu/kotatsu/core/parser/site/RemangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/core/parser/site/RemangaRepository.kt @@ -9,7 +9,6 @@ import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.utils.ext.* import java.util.* -import kotlin.collections.ArrayList class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) { @@ -24,11 +23,11 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito SortOrder.NEWEST ) - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, - sortOrder: SortOrder?, - tag: MangaTag? + tags: Set?, + sortOrder: SortOrder? ): List { val domain = getDomain() val urlBuilder = StringBuilder() @@ -40,8 +39,9 @@ class RemangaRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposito } else { urlBuilder.append("/api/search/catalog/?ordering=") .append(getSortKey(sortOrder)) - if (tag != null) { - urlBuilder.append("&genres=" + tag.key) + tags?.forEach { tag -> + urlBuilder.append("&genres=") + urlBuilder.append(tag.key) } } urlBuilder diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 066da26ec..7c06f6943 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -217,10 +217,7 @@ abstract class MangaListFragment : BaseFragment(), activity?.invalidateOptionsMenu() } - @CallSuper - override fun onFilterChanged(filter: MangaFilter) { - drawer?.closeDrawers() - } + override fun onFilterChanged(filter: MangaFilter) = Unit override fun onWindowInsetsChanged(insets: Insets) { val headerHeight = (activity as? AppBarOwner)?.appBar?.measureHeight() ?: insets.top diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterAdapter.kt index e7fe78058..983fdf0f1 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterAdapter.kt @@ -6,19 +6,15 @@ import org.koitharu.kotatsu.base.ui.list.BaseViewHolder import org.koitharu.kotatsu.core.model.MangaFilter import org.koitharu.kotatsu.core.model.MangaTag import org.koitharu.kotatsu.core.model.SortOrder -import java.util.* class FilterAdapter( - sortOrders: List = emptyList(), - tags: List = emptyList(), + private val sortOrders: List = emptyList(), + private val tags: List = emptyList(), state: MangaFilter?, private val listener: OnFilterChangedListener ) : RecyclerView.Adapter>() { - private val sortOrders = ArrayList(sortOrders) - private val tags = ArrayList(Collections.singletonList(null) + tags) - - private var currentState = state ?: MangaFilter(sortOrders.firstOrNull(), null) + private var currentState = state ?: MangaFilter(sortOrders.firstOrNull(), emptySet()) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) { VIEW_TYPE_SORT -> FilterSortHolder(parent).apply { @@ -28,7 +24,7 @@ class FilterAdapter( } VIEW_TYPE_TAG -> FilterTagHolder(parent).apply { itemView.setOnClickListener { - setCheckedTag(boundData) + setCheckedTag(boundData ?: return@setOnClickListener, !isChecked) } } else -> throw IllegalArgumentException("Unknown viewType $viewType") @@ -44,7 +40,7 @@ class FilterAdapter( } is FilterTagHolder -> { val item = tags[position - sortOrders.size] - holder.bind(item, item == currentState.tag) + holder.bind(item, item in currentState.tags) } } } @@ -54,19 +50,25 @@ class FilterAdapter( else -> VIEW_TYPE_TAG } - fun setCheckedTag(tag: MangaTag?) { - if (tag != currentState.tag) { - val oldItemPos = tags.indexOf(currentState.tag) - val newItemPos = tags.indexOf(tag) - currentState = currentState.copy(tag = tag) - if (oldItemPos in tags.indices) { - notifyItemChanged(sortOrders.size + oldItemPos) + fun setCheckedTag(tag: MangaTag, isChecked: Boolean) { + currentState = if (tag in currentState.tags) { + if (!isChecked) { + currentState.copy(tags = currentState.tags - tag) + } else { + return } - if (newItemPos in tags.indices) { - notifyItemChanged(sortOrders.size + newItemPos) + } else { + if (isChecked) { + currentState.copy(tags = currentState.tags + tag) + } else { + return } - listener.onFilterChanged(currentState) } + val index = tags.indexOf(tag) + if (index in tags.indices) { + notifyItemChanged(sortOrders.size + index) + } + listener.onFilterChanged(currentState) } fun setCheckedSort(sort: SortOrder) { diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterSortHolder.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterSortHolder.kt index a182131b6..9275ae831 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterSortHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterSortHolder.kt @@ -12,7 +12,7 @@ class FilterSortHolder(parent: ViewGroup) : ) { override fun onBind(data: SortOrder, extra: Boolean) { - binding.radio.setText(data.titleRes) - binding.radio.isChecked = extra + binding.root.setText(data.titleRes) + binding.root.isChecked = extra } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterTagHolder.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterTagHolder.kt index f3bf89635..2054d4cb9 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterTagHolder.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/filter/FilterTagHolder.kt @@ -2,18 +2,20 @@ package org.koitharu.kotatsu.list.ui.filter import android.view.LayoutInflater import android.view.ViewGroup -import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.ui.list.BaseViewHolder import org.koitharu.kotatsu.core.model.MangaTag -import org.koitharu.kotatsu.databinding.ItemCheckableSingleBinding +import org.koitharu.kotatsu.databinding.ItemCheckableMultipleBinding class FilterTagHolder(parent: ViewGroup) : - BaseViewHolder( - ItemCheckableSingleBinding.inflate(LayoutInflater.from(parent.context), parent, false) + BaseViewHolder( + ItemCheckableMultipleBinding.inflate(LayoutInflater.from(parent.context), parent, false) ) { - override fun onBind(data: MangaTag?, extra: Boolean) { - binding.radio.text = data?.title ?: context.getString(R.string.all) - binding.radio.isChecked = extra + val isChecked: Boolean + get() = binding.root.isChecked + + override fun onBind(data: MangaTag, extra: Boolean) { + binding.root.text = data.title + binding.root.isChecked = extra } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt b/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt index 383c295bc..3d4c51571 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/domain/LocalMangaRepository.kt @@ -25,11 +25,11 @@ class LocalMangaRepository(private val context: Context) : MangaRepository { private val filenameFilter = CbzFilter() - override suspend fun getList( + override suspend fun getList2( offset: Int, query: String?, - sortOrder: SortOrder?, - tag: MangaTag? + tags: Set?, + sortOrder: SortOrder? ): List { require(offset == 0) { "LocalMangaRepository does not support pagination" diff --git a/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt index 844cbcbbc..5c12ad115 100644 --- a/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/local/ui/LocalListViewModel.kt @@ -61,7 +61,7 @@ class LocalListViewModel( launchLoadingJob(Dispatchers.Default) { try { listError.value = null - mangaList.value = repository.getList(0, tags = null) + mangaList.value = repository.getList2(0) } catch (e: Throwable) { listError.value = e } diff --git a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt index 2ecb7f7bf..1f9b543a6 100644 --- a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListFragment.kt @@ -31,7 +31,6 @@ class RemoteListFragment : MangaListFragment() { override fun onFilterChanged(filter: MangaFilter) { viewModel.applyFilter(filter) - super.onFilterChanged(filter) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { diff --git a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt index aaf6ccaa3..870480e35 100644 --- a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt @@ -78,10 +78,10 @@ class RemoteListViewModel( loadingJob = launchLoadingJob(Dispatchers.Default) { try { listError.value = null - val list = repository.getList( + val list = repository.getList2( offset = if (append) mangaList.value?.size ?: 0 else 0, sortOrder = appliedFilter?.sortOrder, - tag = appliedFilter?.tag + tags = appliedFilter?.tags, ) if (!append) { mangaList.value = list diff --git a/app/src/main/java/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt b/app/src/main/java/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt index a58e5098c..d099f54c5 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/domain/MangaSearchRepository.kt @@ -29,7 +29,11 @@ class MangaSearchRepository( MangaProviderFactory.getSources(settings, includeHidden = false).asFlow() .flatMapMerge(concurrency) { source -> runCatching { - source.repository.getList(0, query, SortOrder.POPULARITY) + source.repository.getList2( + offset = 0, + query = query, + sortOrder = SortOrder.POPULARITY + ) }.getOrElse { emptyList() }.asFlow() diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt index 35df96c82..659e12082 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/SearchViewModel.kt @@ -71,10 +71,9 @@ class SearchViewModel( loadingJob = launchLoadingJob(Dispatchers.Default) { try { listError.value = null - val list = repository.getList( + val list = repository.getList2( offset = if (append) mangaList.value?.size ?: 0 else 0, - tags = null, - query = query + query = query, ) if (!append) { mangaList.value = list diff --git a/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt b/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt index 52f774e6f..05d310506 100644 --- a/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt +++ b/app/src/main/java/org/koitharu/kotatsu/utils/ext/StringExt.kt @@ -48,6 +48,10 @@ fun String.toCamelCase(): String { return result.toString() } +fun String.toTitleCase(): String { + return replaceFirstChar { x -> x.uppercase() } +} + fun String.transliterate(skipMissing: Boolean): String { val cyr = charArrayOf( 'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', @@ -200,4 +204,20 @@ fun String.levenshteinDistance(other: String): Int { } return cost[lhsLength - 1] +} + +inline fun StringBuilder.appendAll( + items: Iterable, + separator: CharSequence, + transform: (T) -> CharSequence = { it.toString() }, +) { + var isFirst = true + for (item in items) { + if (isFirst) { + isFirst = false + } else { + append(separator) + } + append(transform(item)) + } } \ No newline at end of file diff --git a/app/src/main/res/layout/item_checkable_multiple.xml b/app/src/main/res/layout/item_checkable_multiple.xml new file mode 100644 index 000000000..14b97b714 --- /dev/null +++ b/app/src/main/res/layout/item_checkable_multiple.xml @@ -0,0 +1,13 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_checkable_single.xml b/app/src/main/res/layout/item_checkable_single.xml index af15894cd..12eb91d8d 100644 --- a/app/src/main/res/layout/item_checkable_single.xml +++ b/app/src/main/res/layout/item_checkable_single.xml @@ -2,7 +2,6 @@