Reduce memory usage

pull/26/head
Koitharu 6 years ago
parent 56e145420c
commit a8c22de601

@ -3,6 +3,7 @@ package org.koitharu.kotatsu.core.db.entity
import androidx.room.Embedded import androidx.room.Embedded
import androidx.room.Junction import androidx.room.Junction
import androidx.room.Relation import androidx.room.Relation
import org.koitharu.kotatsu.utils.ext.mapToSet
data class MangaWithTags( data class MangaWithTags(
@Embedded val manga: MangaEntity, @Embedded val manga: MangaEntity,
@ -14,7 +15,7 @@ data class MangaWithTags(
val tags: List<TagEntity> val tags: List<TagEntity>
) { ) {
fun toManga() = manga.toManga(tags.map { fun toManga() = manga.toManga(tags.mapToSet {
it.toMangaTag() it.toMangaTag()
}.toSet()) })
} }

@ -4,6 +4,7 @@ import androidx.room.Embedded
import androidx.room.Junction import androidx.room.Junction
import androidx.room.Relation import androidx.room.Relation
import org.koitharu.kotatsu.core.model.TrackingLogItem import org.koitharu.kotatsu.core.model.TrackingLogItem
import org.koitharu.kotatsu.utils.ext.mapToSet
import java.util.* import java.util.*
data class TrackLogWithManga( data class TrackLogWithManga(
@ -24,7 +25,7 @@ data class TrackLogWithManga(
fun toTrackingLogItem() = TrackingLogItem( fun toTrackingLogItem() = TrackingLogItem(
id = trackLog.id, id = trackLog.id,
chapters = trackLog.chapters.split('\n').filterNot { x -> x.isEmpty() }, chapters = trackLog.chapters.split('\n').filterNot { x -> x.isEmpty() },
manga = manga.toManga(tags.map { x -> x.toMangaTag() }.toSet()), manga = manga.toManga(tags.mapToSet { x -> x.toMangaTag() }),
createdAt = Date(trackLog.createdAt) createdAt = Date(trackLog.createdAt)
) )
} }

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.core.parser.site package org.koitharu.kotatsu.core.parser.site
import androidx.collection.arraySetOf
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.ParseException import org.koitharu.kotatsu.core.exceptions.ParseException
import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.model.*
@ -13,7 +14,11 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
protected abstract val defaultDomain: String protected abstract val defaultDomain: String
override val sortOrders = setOf(SortOrder.NEWEST, SortOrder.POPULARITY, SortOrder.ALPHABETICAL) override val sortOrders = arraySetOf(
SortOrder.NEWEST,
SortOrder.POPULARITY,
SortOrder.ALPHABETICAL
)
override suspend fun getList( override suspend fun getList(
offset: Int, offset: Int,
@ -51,13 +56,13 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
coverUrl = row.selectFirst("div.manga_images")?.selectFirst("img") coverUrl = row.selectFirst("div.manga_images")?.selectFirst("img")
?.attr("src")?.withDomain(domain).orEmpty(), ?.attr("src")?.withDomain(domain).orEmpty(),
tags = safe { tags = safe {
row.selectFirst("div.genre")?.select("a")?.map { row.selectFirst("div.genre")?.select("a")?.mapToSet {
MangaTag( MangaTag(
title = it.text(), title = it.text(),
key = it.attr("href").substringAfterLast('/').urlEncoded(), key = it.attr("href").substringAfterLast('/').urlEncoded(),
source = source source = source
) )
}?.toSet() }
}.orEmpty(), }.orEmpty(),
source = source source = source
) )
@ -118,17 +123,17 @@ abstract class ChanRepository(loaderContext: MangaLoaderContext) : RemoteMangaRe
val doc = loaderContext.httpGet("https://$domain/catalog").parseHtml() val doc = loaderContext.httpGet("https://$domain/catalog").parseHtml()
val root = doc.body().selectFirst("div.main_fon").getElementById("side") val root = doc.body().selectFirst("div.main_fon").getElementById("side")
.select("ul").last() .select("ul").last()
return root.select("li.sidetag").map { li -> return root.select("li.sidetag").mapToSet { li ->
val a = li.children().last() val a = li.children().last()
MangaTag( MangaTag(
title = a.text().capitalize(), title = a.text().capitalize(),
key = a.attr("href").substringAfterLast('/'), key = a.attr("href").substringAfterLast('/'),
source = source source = source
) )
}.toSet() }
} }
override fun onCreatePreferences() = setOf(R.string.key_parser_domain) override fun onCreatePreferences() = arraySetOf(R.string.key_parser_domain)
private fun getSortKey(sortOrder: SortOrder?) = private fun getSortKey(sortOrder: SortOrder?) =
when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) { when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) {

@ -1,20 +1,18 @@
package org.koitharu.kotatsu.core.parser.site package org.koitharu.kotatsu.core.parser.site
import androidx.collection.arraySetOf
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.ParseException import org.koitharu.kotatsu.core.exceptions.ParseException
import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.domain.MangaLoaderContext
import org.koitharu.kotatsu.utils.ext.map import org.koitharu.kotatsu.utils.ext.*
import org.koitharu.kotatsu.utils.ext.mapIndexed
import org.koitharu.kotatsu.utils.ext.parseHtml
import org.koitharu.kotatsu.utils.ext.parseJson
class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) { class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepository(loaderContext) {
override val source = MangaSource.DESUME override val source = MangaSource.DESUME
override val sortOrders = setOf( override val sortOrders = arraySetOf(
SortOrder.UPDATED, SortOrder.UPDATED,
SortOrder.POPULARITY, SortOrder.POPULARITY,
SortOrder.NEWEST, SortOrder.NEWEST,
@ -76,13 +74,13 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
val json = loaderContext.httpGet(url).parseJson().getJSONObject("response") val json = loaderContext.httpGet(url).parseJson().getJSONObject("response")
?: throw ParseException("Invalid response") ?: throw ParseException("Invalid response")
return manga.copy( return manga.copy(
tags = json.getJSONArray("genres").map { tags = json.getJSONArray("genres").mapToSet {
MangaTag( MangaTag(
key = it.getString("text"), key = it.getString("text"),
title = it.getString("russian"), title = it.getString("russian"),
source = manga.source source = manga.source
) )
}.toSet(), },
description = json.getString("description"), description = json.getString("description"),
chapters = json.getJSONObject("chapters").getJSONArray("list").mapIndexed { i, it -> chapters = json.getJSONObject("chapters").getJSONArray("list").mapIndexed { i, it ->
val chid = it.getLong("id") val chid = it.getLong("id")
@ -113,16 +111,16 @@ class DesuMeRepository(loaderContext: MangaLoaderContext) : RemoteMangaRepositor
val domain = conf.getDomain(DOMAIN) val domain = conf.getDomain(DOMAIN)
val doc = loaderContext.httpGet("https://$domain/manga/").parseHtml() val doc = loaderContext.httpGet("https://$domain/manga/").parseHtml()
val root = doc.body().getElementById("animeFilter").selectFirst(".catalog-genres") val root = doc.body().getElementById("animeFilter").selectFirst(".catalog-genres")
return root.select("li").map { return root.select("li").mapToSet {
MangaTag( MangaTag(
source = source, source = source,
key = it.selectFirst("input").attr("data-genre"), key = it.selectFirst("input").attr("data-genre"),
title = it.selectFirst("label").text() title = it.selectFirst("label").text()
) )
}.toSet() }
} }
override fun onCreatePreferences() = setOf(R.string.key_parser_domain) override fun onCreatePreferences() = arraySetOf(R.string.key_parser_domain)
private fun getSortKey(sortOrder: SortOrder?) = private fun getSortKey(sortOrder: SortOrder?) =
when (sortOrder) { when (sortOrder) {

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.core.parser.site package org.koitharu.kotatsu.core.parser.site
import androidx.collection.arraySetOf
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.ParseException import org.koitharu.kotatsu.core.exceptions.ParseException
import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.model.*
@ -12,7 +13,7 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
protected abstract val defaultDomain: String protected abstract val defaultDomain: String
override val sortOrders = setOf( override val sortOrders = arraySetOf(
SortOrder.UPDATED, SortOrder.POPULARITY, SortOrder.UPDATED, SortOrder.POPULARITY,
SortOrder.NEWEST, SortOrder.RATING SortOrder.NEWEST, SortOrder.RATING
//FIXME SortOrder.ALPHABETICAL //FIXME SortOrder.ALPHABETICAL
@ -70,13 +71,13 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
author = tileInfo?.selectFirst("a.person-link")?.text(), author = tileInfo?.selectFirst("a.person-link")?.text(),
tags = safe { tags = safe {
tileInfo?.select("a.element-link") tileInfo?.select("a.element-link")
?.map { ?.mapToSet {
MangaTag( MangaTag(
title = it.text(), title = it.text(),
key = it.attr("href").substringAfterLast('/'), key = it.attr("href").substringAfterLast('/'),
source = source source = source
) )
}?.toSet() }
}.orEmpty(), }.orEmpty(),
state = when { state = when {
node.selectFirst("div.tags") node.selectFirst("div.tags")
@ -153,16 +154,16 @@ abstract class GroupleRepository(loaderContext: MangaLoaderContext) :
val doc = loaderContext.httpGet("https://$domain/list/genres/sort_name").parseHtml() val doc = loaderContext.httpGet("https://$domain/list/genres/sort_name").parseHtml()
val root = doc.body().getElementById("mangaBox").selectFirst("div.leftContent") val root = doc.body().getElementById("mangaBox").selectFirst("div.leftContent")
.selectFirst("table.table") .selectFirst("table.table")
return root.select("a.element-link").map { a -> return root.select("a.element-link").mapToSet { a ->
MangaTag( MangaTag(
title = a.text().capitalize(), title = a.text().capitalize(),
key = a.attr("href").substringAfterLast('/'), key = a.attr("href").substringAfterLast('/'),
source = source source = source
) )
}.toSet() }
} }
override fun onCreatePreferences() = setOf(R.string.key_parser_domain) override fun onCreatePreferences() = arraySetOf(R.string.key_parser_domain)
private fun getSortKey(sortOrder: SortOrder?) = private fun getSortKey(sortOrder: SortOrder?) =
when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) { when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) {

@ -4,6 +4,7 @@ import org.koitharu.kotatsu.core.exceptions.ParseException
import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.domain.MangaLoaderContext
import org.koitharu.kotatsu.utils.ext.longHashCode import org.koitharu.kotatsu.utils.ext.longHashCode
import org.koitharu.kotatsu.utils.ext.mapToSet
import org.koitharu.kotatsu.utils.ext.parseHtml import org.koitharu.kotatsu.utils.ext.parseHtml
import org.koitharu.kotatsu.utils.ext.withDomain import org.koitharu.kotatsu.utils.ext.withDomain
@ -37,14 +38,14 @@ class HenChanRepository(loaderContext: MangaLoaderContext) : ChanRepository(load
return manga.copy( return manga.copy(
description = root.getElementById("description")?.html()?.substringBeforeLast("<div"), description = root.getElementById("description")?.html()?.substringBeforeLast("<div"),
largeCoverUrl = root.getElementById("cover")?.attr("src")?.withDomain(domain), largeCoverUrl = root.getElementById("cover")?.attr("src")?.withDomain(domain),
tags = root.selectFirst("div.sidetags")?.select("li.sidetag")?.map { tags = root.selectFirst("div.sidetags")?.select("li.sidetag")?.mapToSet {
val a = it.children().last() val a = it.children().last()
MangaTag( MangaTag(
title = a.text(), title = a.text(),
key = a.attr("href").substringAfterLast('/'), key = a.attr("href").substringAfterLast('/'),
source = source source = source
) )
}?.toSet() ?: manga.tags, } ?: manga.tags,
chapters = listOf( chapters = listOf(
MangaChapter( MangaChapter(
id = readLink.longHashCode(), id = readLink.longHashCode(),

@ -1,5 +1,7 @@
package org.koitharu.kotatsu.core.parser.site package org.koitharu.kotatsu.core.parser.site
import androidx.collection.ArraySet
import androidx.collection.arraySetOf
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
@ -16,7 +18,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
override val source = MangaSource.MANGALIB override val source = MangaSource.MANGALIB
override val sortOrders = setOf( override val sortOrders = arraySetOf(
SortOrder.RATING, SortOrder.RATING,
SortOrder.ALPHABETICAL, SortOrder.ALPHABETICAL,
SortOrder.POPULARITY, SortOrder.POPULARITY,
@ -68,7 +70,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
} }
} }
override fun onCreatePreferences() = setOf(R.string.key_parser_domain) override fun onCreatePreferences() = arraySetOf(R.string.key_parser_domain)
override suspend fun getDetails(manga: Manga): Manga { override suspend fun getDetails(manga: Manga): Manga {
val doc = loaderContext.httpGet(manga.url + "?section=info").parseHtml() val doc = loaderContext.httpGet(manga.url + "?section=info").parseHtml()
@ -126,13 +128,13 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
author = info.getElementsMatchingOwnText("Автор").firstOrNull() author = info.getElementsMatchingOwnText("Автор").firstOrNull()
?.nextElementSibling()?.text() ?: manga.author, ?.nextElementSibling()?.text() ?: manga.author,
tags = info.getElementsMatchingOwnText("Жанры")?.firstOrNull() tags = info.getElementsMatchingOwnText("Жанры")?.firstOrNull()
?.nextElementSibling()?.select("a")?.mapNotNull { a -> ?.nextElementSibling()?.select("a")?.mapToSet { a ->
MangaTag( MangaTag(
title = a.text().capitalize(), title = a.text().capitalize(),
key = a.attr("href").substringAfterLast('='), key = a.attr("href").substringAfterLast('='),
source = source source = source
) )
}?.toSet() ?: manga.tags, } ?: manga.tags,
description = info.getElementsMatchingOwnText("Описание")?.firstOrNull() description = info.getElementsMatchingOwnText("Описание")?.firstOrNull()
?.nextElementSibling()?.html(), ?.nextElementSibling()?.html(),
chapters = chapters chapters = chapters
@ -183,7 +185,7 @@ open class MangaLibRepository(loaderContext: MangaLoaderContext) :
if (raw.startsWith("window.__DATA")) { if (raw.startsWith("window.__DATA")) {
val json = JSONObject(raw.substringAfter('=').substringBeforeLast(';')) val json = JSONObject(raw.substringAfter('=').substringBeforeLast(';'))
val genres = json.getJSONObject("filters").getJSONArray("genres") val genres = json.getJSONObject("filters").getJSONArray("genres")
val result = HashSet<MangaTag>(genres.length()) val result = ArraySet<MangaTag>(genres.length())
for (x in genres) { for (x in genres) {
result += MangaTag( result += MangaTag(
source = source, source = source,

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.core.parser.site package org.koitharu.kotatsu.core.parser.site
import androidx.collection.arraySetOf
import org.intellij.lang.annotations.Language import org.intellij.lang.annotations.Language
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.ParseException import org.koitharu.kotatsu.core.exceptions.ParseException
@ -13,7 +14,7 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposi
override val source = MangaSource.MANGATOWN override val source = MangaSource.MANGATOWN
override val sortOrders = setOf( override val sortOrders = arraySetOf(
SortOrder.ALPHABETICAL, SortOrder.ALPHABETICAL,
SortOrder.RATING, SortOrder.RATING,
SortOrder.POPULARITY, SortOrder.POPULARITY,
@ -71,13 +72,13 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposi
"completed" -> MangaState.FINISHED "completed" -> MangaState.FINISHED
else -> null else -> null
}, },
tags = li.selectFirst("p.keyWord")?.select("a")?.mapNotNull tags@{ x -> tags = li.selectFirst("p.keyWord")?.select("a")?.mapNotNullToSet tags@{ x ->
MangaTag( MangaTag(
title = x.attr("title"), title = x.attr("title"),
key = x.attr("href").parseTagKey() ?: return@tags null, key = x.attr("href").parseTagKey() ?: return@tags null,
source = MangaSource.MANGATOWN source = MangaSource.MANGATOWN
) )
}?.toSet().orEmpty(), }.orEmpty(),
url = href url = href
) )
} }
@ -150,22 +151,22 @@ class MangaTownRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposi
.getElementsContainingOwnText("Genres") .getElementsContainingOwnText("Genres")
.first() .first()
.nextElementSibling() .nextElementSibling()
return root.select("li").mapNotNull { li -> return root.select("li").mapNotNullToSet { li ->
val a = li.selectFirst("a") ?: return@mapNotNull null val a = li.selectFirst("a") ?: return@mapNotNullToSet null
val key = a.attr("href").parseTagKey() val key = a.attr("href").parseTagKey()
if (key.isNullOrEmpty()) { if (key.isNullOrEmpty()) {
return@mapNotNull null return@mapNotNullToSet null
} }
MangaTag( MangaTag(
source = MangaSource.MANGATOWN, source = MangaSource.MANGATOWN,
key = key, key = key,
title = a.text() title = a.text()
) )
}.toSet() }
} }
override fun onCreatePreferences() = setOf(R.string.key_parser_domain, R.string.key_parser_ssl) override fun onCreatePreferences() = arraySetOf(R.string.key_parser_domain, R.string.key_parser_ssl)
private fun String.parseTagKey() = split('/').findLast { TAG_REGEX matches it } private fun String.parseTagKey() = split('/').findLast { TAG_REGEX matches it }

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.core.parser.site package org.koitharu.kotatsu.core.parser.site
import androidx.collection.arraySetOf
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.ParseException import org.koitharu.kotatsu.core.exceptions.ParseException
import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.model.*
@ -12,7 +13,7 @@ class NudeMoonRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit
override val source = MangaSource.NUDEMOON override val source = MangaSource.NUDEMOON
override val sortOrders = setOf(SortOrder.NEWEST, SortOrder.POPULARITY, SortOrder.RATING) override val sortOrders = arraySetOf(SortOrder.NEWEST, SortOrder.POPULARITY, SortOrder.RATING)
init { init {
loaderContext.insertCookies( loaderContext.insertCookies(
@ -133,7 +134,7 @@ class NudeMoonRepository(loaderContext: MangaLoaderContext) : RemoteMangaReposit
} }
} }
override fun onCreatePreferences() = setOf(R.string.key_parser_domain) override fun onCreatePreferences() = arraySetOf(R.string.key_parser_domain)
private fun getSortKey(sortOrder: SortOrder?) = private fun getSortKey(sortOrder: SortOrder?) =
when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) { when (sortOrder ?: sortOrders.minByOrNull { it.ordinal }) {

@ -5,6 +5,7 @@ import android.content.SharedPreferences
import android.content.res.Resources import android.content.res.Resources
import android.provider.Settings import android.provider.Settings
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.collection.arraySetOf
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
@ -44,7 +45,7 @@ class AppSettings private constructor(resources: Resources, private val prefs: S
val readerPageSwitch by StringSetPreferenceDelegate( val readerPageSwitch by StringSetPreferenceDelegate(
resources.getString(R.string.key_reader_switchers), resources.getString(R.string.key_reader_switchers),
setOf(PAGE_SWITCH_TAPS) arraySetOf(PAGE_SWITCH_TAPS)
) )
var isTrafficWarningEnabled by BoolPreferenceDelegate( var isTrafficWarningEnabled by BoolPreferenceDelegate(
@ -89,7 +90,7 @@ class AppSettings private constructor(resources: Resources, private val prefs: S
val trackSources by StringSetPreferenceDelegate( val trackSources by StringSetPreferenceDelegate(
resources.getString(R.string.key_track_sources), resources.getString(R.string.key_track_sources),
setOf(TRACK_FAVOURITES, TRACK_HISTORY) arraySetOf(TRACK_FAVOURITES, TRACK_HISTORY)
) )
var appPassword by NullableStringPreferenceDelegate( var appPassword by NullableStringPreferenceDelegate(

@ -11,27 +11,28 @@ import org.koitharu.kotatsu.core.db.entity.MangaEntity
import org.koitharu.kotatsu.core.db.entity.TagEntity import org.koitharu.kotatsu.core.db.entity.TagEntity
import org.koitharu.kotatsu.core.model.FavouriteCategory import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.utils.ext.mapToSet
class FavouritesRepository(private val db: MangaDatabase) { class FavouritesRepository(private val db: MangaDatabase) {
suspend fun getAllManga(): List<Manga> { suspend fun getAllManga(): List<Manga> {
val entities = db.favouritesDao.findAll() val entities = db.favouritesDao.findAll()
return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } return entities.map { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) }
} }
suspend fun getAllManga(offset: Int): List<Manga> { suspend fun getAllManga(offset: Int): List<Manga> {
val entities = db.favouritesDao.findAll(offset, 20) val entities = db.favouritesDao.findAll(offset, 20)
return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } return entities.map { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) }
} }
suspend fun getManga(categoryId: Long): List<Manga> { suspend fun getManga(categoryId: Long): List<Manga> {
val entities = db.favouritesDao.findAll(categoryId) val entities = db.favouritesDao.findAll(categoryId)
return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } return entities.map { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) }
} }
suspend fun getManga(categoryId: Long, offset: Int): List<Manga> { suspend fun getManga(categoryId: Long, offset: Int): List<Manga> {
val entities = db.favouritesDao.findAll(categoryId, offset, 20) val entities = db.favouritesDao.findAll(categoryId, offset, 20)
return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } return entities.map { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) }
} }
suspend fun getAllCategories(): List<FavouriteCategory> { suspend fun getAllCategories(): List<FavouriteCategory> {

@ -13,6 +13,7 @@ import org.koitharu.kotatsu.core.db.entity.TagEntity
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaHistory import org.koitharu.kotatsu.core.model.MangaHistory
import org.koitharu.kotatsu.domain.tracking.TrackingRepository import org.koitharu.kotatsu.domain.tracking.TrackingRepository
import org.koitharu.kotatsu.utils.ext.mapToSet
class HistoryRepository(private val db: MangaDatabase) : KoinComponent { class HistoryRepository(private val db: MangaDatabase) : KoinComponent {
@ -20,7 +21,7 @@ class HistoryRepository(private val db: MangaDatabase) : KoinComponent {
suspend fun getList(offset: Int, limit: Int = 20): List<Manga> { suspend fun getList(offset: Int, limit: Int = 20): List<Manga> {
val entities = db.historyDao.findAll(offset, limit) val entities = db.historyDao.findAll(offset, limit)
return entities.map { it.manga.toManga(it.tags.map(TagEntity::toMangaTag).toSet()) } return entities.map { it.manga.toManga(it.tags.mapToSet(TagEntity::toMangaTag)) }
} }
suspend fun addOrUpdate(manga: Manga, chapterId: Long, page: Int, scroll: Int) { suspend fun addOrUpdate(manga: Manga, chapterId: Long, page: Int, scroll: Int) {

@ -8,7 +8,7 @@ import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.model.MangaTag import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.utils.ext.getStringOrNull import org.koitharu.kotatsu.utils.ext.getStringOrNull
import org.koitharu.kotatsu.utils.ext.map import org.koitharu.kotatsu.utils.ext.mapToSet
import org.koitharu.kotatsu.utils.ext.safe import org.koitharu.kotatsu.utils.ext.safe
class MangaIndex(source: String?) { class MangaIndex(source: String?) {
@ -51,13 +51,13 @@ class MangaIndex(source: String?) {
rating = json.getDouble("rating").toFloat(), rating = json.getDouble("rating").toFloat(),
coverUrl = json.getString("cover"), coverUrl = json.getString("cover"),
description = json.getStringOrNull("description"), description = json.getStringOrNull("description"),
tags = json.getJSONArray("tags").map { x -> tags = json.getJSONArray("tags").mapToSet { x ->
MangaTag( MangaTag(
title = x.getString("title"), title = x.getString("title"),
key = x.getString("key"), key = x.getString("key"),
source = source source = source
) )
}.toSet(), },
chapters = getChapters(json.getJSONObject("chapters"), source) chapters = getChapters(json.getJSONObject("chapters"), source)
) )
} }

@ -56,7 +56,7 @@ class DownloadService : BaseService() {
when (intent?.action) { when (intent?.action) {
ACTION_DOWNLOAD_START -> { ACTION_DOWNLOAD_START -> {
val manga = intent.getParcelableExtra<Manga>(EXTRA_MANGA) val manga = intent.getParcelableExtra<Manga>(EXTRA_MANGA)
val chapters = intent.getLongArrayExtra(EXTRA_CHAPTERS_IDS)?.toSet() val chapters = intent.getLongArrayExtra(EXTRA_CHAPTERS_IDS)?.toArraySet()
if (manga != null) { if (manga != null) {
jobs[startId] = downloadManga(manga, chapters, startId) jobs[startId] = downloadManga(manga, chapters, startId)
Toast.makeText(this, R.string.manga_downloading_, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.manga_downloading_, Toast.LENGTH_SHORT).show()

@ -7,6 +7,7 @@ import org.koin.core.component.get
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.domain.favourites.FavouritesRepository import org.koitharu.kotatsu.domain.favourites.FavouritesRepository
import org.koitharu.kotatsu.ui.base.BasePresenter import org.koitharu.kotatsu.ui.base.BasePresenter
import org.koitharu.kotatsu.utils.ext.mapToSet
@InjectViewState @InjectViewState
class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() { class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() {
@ -29,7 +30,7 @@ class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() {
fun loadMangaCategories(manga: Manga) { fun loadMangaCategories(manga: Manga) {
launchJob { launchJob {
val categories = repository.getCategories(manga.id) val categories = repository.getCategories(manga.id)
viewState.onCheckedCategoriesChanged(categories.map { it.id.toInt() }.toSet()) viewState.onCheckedCategoriesChanged(categories.mapToSet { it.id.toInt() })
} }
} }

@ -11,6 +11,7 @@ import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.domain.MangaProviderFactory import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.utils.ext.mapToSet
import org.koitharu.kotatsu.utils.ext.safe import org.koitharu.kotatsu.utils.ext.safe
class SourcesAdapter(private val onItemClickListener: OnRecyclerItemClickListener<MangaSource>) : class SourcesAdapter(private val onItemClickListener: OnRecyclerItemClickListener<MangaSource>) :
@ -44,7 +45,7 @@ class SourcesAdapter(private val onItemClickListener: OnRecyclerItemClickListene
} else { } else {
hiddenItems.add(holder.requireData()) hiddenItems.add(holder.requireData())
} }
settings.hiddenSources = hiddenItems.map { x -> x.name }.toSet() settings.hiddenSources = hiddenItems.mapToSet { x -> x.name }
} }
holder.imageView_config.setOnClickListener { v -> holder.imageView_config.setOnClickListener { v ->
onItemClickListener.onItemClick(holder.requireData(), holder.bindingAdapterPosition, v) onItemClickListener.onItemClick(holder.requireData(), holder.bindingAdapterPosition, v)

@ -30,7 +30,28 @@ fun <T> List<T>.medianOrNull(): T? = when {
inline fun <T, R> Collection<T>.mapToSet(transform: (T) -> R): Set<R> { inline fun <T, R> Collection<T>.mapToSet(transform: (T) -> R): Set<R> {
val destination = ArraySet<R>(size) val destination = ArraySet<R>(size)
for (item in this) for (item in this) {
destination.add(transform(item)) destination.add(transform(item))
}
return destination return destination
} }
inline fun <T, R> Collection<T>.mapNotNullToSet(transform: (T) -> R?): Set<R> {
val destination = ArraySet<R>(size)
for (item in this) {
destination.add(transform(item) ?: continue)
}
return destination
}
fun LongArray.toArraySet(): Set<Long> {
return when (size) {
0 -> emptySet()
1 -> setOf(this[0])
else -> ArraySet<Long>(size).also { set ->
for (item in this) {
set.add(item)
}
}
}
}

@ -3,7 +3,7 @@ package org.koitharu.kotatsu.utils.ext
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
fun <T : Fragment> T.withArgs(size: Int, block: Bundle.() -> Unit): T { inline fun <T : Fragment> T.withArgs(size: Int, block: Bundle.() -> Unit): T {
val b = Bundle(size) val b = Bundle(size)
b.block() b.block()
this.arguments = b this.arguments = b

@ -1,5 +1,6 @@
package org.koitharu.kotatsu.utils.ext package org.koitharu.kotatsu.utils.ext
import androidx.collection.ArraySet
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
@ -37,3 +38,13 @@ private class JSONIterator(private val array: JSONArray) : Iterator<JSONObject>
override fun next(): JSONObject = array.getJSONObject(index++) override fun next(): JSONObject = array.getJSONObject(index++)
} }
fun <T> JSONArray.mapToSet(block: (JSONObject) -> T): Set<T> {
val len = length()
val result = ArraySet<T>(len)
for (i in 0 until len) {
val jo = getJSONObject(i)
result.add(block(jo))
}
return result
}

@ -1,6 +1,7 @@
package org.koitharu.kotatsu.utils.ext package org.koitharu.kotatsu.utils.ext
import android.net.Uri import android.net.Uri
import androidx.collection.arraySetOf
import java.math.BigInteger import java.math.BigInteger
import java.net.URLEncoder import java.net.URLEncoder
import java.security.MessageDigest import java.security.MessageDigest
@ -71,7 +72,7 @@ fun String.transliterate(skipMissing: Boolean): String {
} }
fun String.toFileNameSafe() = this.transliterate(false) fun String.toFileNameSafe() = this.transliterate(false)
.replace(Regex("[^a-z0-9_\\-]", setOf(RegexOption.IGNORE_CASE)), " ") .replace(Regex("[^a-z0-9_\\-]", arraySetOf(RegexOption.IGNORE_CASE)), " ")
.replace(Regex("\\s+"), "_") .replace(Regex("\\s+"), "_")
fun String.ellipsize(maxLength: Int) = if (this.length > maxLength) { fun String.ellipsize(maxLength: Int) = if (this.length > maxLength) {

@ -143,7 +143,7 @@ fun View.measureWidth(): Int {
} else vw } else vw
} }
fun ViewPager2.doOnPageChanged(callback: (Int) -> Unit) { inline fun ViewPager2.doOnPageChanged(crossinline callback: (Int) -> Unit) {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
@ -153,7 +153,7 @@ fun ViewPager2.doOnPageChanged(callback: (Int) -> Unit) {
}) })
} }
fun RecyclerView.doOnCurrentItemChanged(callback: (Int) -> Unit) { inline fun RecyclerView.doOnCurrentItemChanged(crossinline callback: (Int) -> Unit) {
addOnScrollListener(object : RecyclerView.OnScrollListener() { addOnScrollListener(object : RecyclerView.OnScrollListener() {
private var lastItem = -1 private var lastItem = -1

Loading…
Cancel
Save