Merge branch 'master' into devel
This commit is contained in:
@@ -18,8 +18,8 @@ android {
|
||||
applicationId 'org.koitharu.kotatsu'
|
||||
minSdk = 21
|
||||
targetSdk = 35
|
||||
versionCode = 1009
|
||||
versionName = '8.1.3'
|
||||
versionCode = 1010
|
||||
versionName = '8.1.4'
|
||||
generatedDensities = []
|
||||
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
|
||||
ksp {
|
||||
|
||||
@@ -17,9 +17,9 @@ abstract class BookmarksDao {
|
||||
|
||||
@Transaction
|
||||
@Query(
|
||||
"SELECT * FROM manga JOIN bookmarks ON bookmarks.manga_id = manga.manga_id ORDER BY percent",
|
||||
"SELECT * FROM manga JOIN bookmarks ON bookmarks.manga_id = manga.manga_id ORDER BY percent LIMIT :limit OFFSET :offset",
|
||||
)
|
||||
abstract suspend fun findAll(): Map<MangaWithTags, List<BookmarkEntity>>
|
||||
abstract suspend fun findAll(offset: Int, limit: Int): Map<MangaWithTags, List<BookmarkEntity>>
|
||||
|
||||
@Query("SELECT * FROM bookmarks WHERE manga_id = :mangaId AND chapter_id = :chapterId AND page = :page ORDER BY percent")
|
||||
abstract fun observe(mangaId: Long, chapterId: Long, page: Int): Flow<BookmarkEntity?>
|
||||
|
||||
@@ -28,7 +28,7 @@ class BackupRepository @Inject constructor(
|
||||
var offset = 0
|
||||
val entry = BackupEntry(BackupEntry.Name.HISTORY, JSONArray())
|
||||
while (true) {
|
||||
val history = db.getHistoryDao().findAll(offset, PAGE_SIZE)
|
||||
val history = db.getHistoryDao().findAll(offset = offset, limit = PAGE_SIZE)
|
||||
if (history.isEmpty()) {
|
||||
break
|
||||
}
|
||||
@@ -59,7 +59,7 @@ class BackupRepository @Inject constructor(
|
||||
var offset = 0
|
||||
val entry = BackupEntry(BackupEntry.Name.FAVOURITES, JSONArray())
|
||||
while (true) {
|
||||
val favourites = db.getFavouritesDao().findAllRaw(offset, PAGE_SIZE)
|
||||
val favourites = db.getFavouritesDao().findAllRaw(offset = offset, limit = PAGE_SIZE)
|
||||
if (favourites.isEmpty()) {
|
||||
break
|
||||
}
|
||||
@@ -78,19 +78,26 @@ class BackupRepository @Inject constructor(
|
||||
}
|
||||
|
||||
suspend fun dumpBookmarks(): BackupEntry {
|
||||
var offset = 0
|
||||
val entry = BackupEntry(BackupEntry.Name.BOOKMARKS, JSONArray())
|
||||
val all = db.getBookmarksDao().findAll()
|
||||
for ((m, b) in all) {
|
||||
val json = JSONObject()
|
||||
val manga = JsonSerializer(m.manga).toJson()
|
||||
json.put("manga", manga)
|
||||
val tags = JSONArray()
|
||||
m.tags.forEach { tags.put(JsonSerializer(it).toJson()) }
|
||||
json.put("tags", tags)
|
||||
val bookmarks = JSONArray()
|
||||
b.forEach { bookmarks.put(JsonSerializer(it).toJson()) }
|
||||
json.put("bookmarks", bookmarks)
|
||||
entry.data.put(json)
|
||||
while (true) {
|
||||
val bookmarks = db.getBookmarksDao().findAll(offset = offset, limit = PAGE_SIZE)
|
||||
if (bookmarks.isEmpty()) {
|
||||
break
|
||||
}
|
||||
offset += bookmarks.size
|
||||
for ((m, b) in bookmarks) {
|
||||
val json = JSONObject()
|
||||
val manga = JsonSerializer(m.manga).toJson()
|
||||
json.put("manga", manga)
|
||||
val tags = JSONArray()
|
||||
m.tags.forEach { tags.put(JsonSerializer(it).toJson()) }
|
||||
json.put("tags", tags)
|
||||
val bookmarks = JSONArray()
|
||||
b.forEach { bookmarks.put(JsonSerializer(it).toJson()) }
|
||||
json.put("bookmarks", bookmarks)
|
||||
entry.data.put(json)
|
||||
}
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
@@ -151,6 +151,8 @@ fun Manga.chaptersCount(): Int {
|
||||
return max
|
||||
}
|
||||
|
||||
fun Manga.isNsfw(): Boolean = contentRating == ContentRating.ADULT || source.isNsfw()
|
||||
|
||||
fun MangaListFilter.getSummary() = buildSpannedString {
|
||||
if (!query.isNullOrEmpty()) {
|
||||
append(query)
|
||||
|
||||
@@ -100,7 +100,11 @@ abstract class ChaptersPagesViewModel(
|
||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
val bookmarks = mangaDetails.flatMapLatest {
|
||||
if (it != null) bookmarksRepository.observeBookmarks(it.toManga()) else flowOf(emptyList())
|
||||
if (it != null) {
|
||||
bookmarksRepository.observeBookmarks(it.toManga()).withErrorHandling()
|
||||
} else {
|
||||
flowOf(emptyList())
|
||||
}
|
||||
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, emptyList())
|
||||
|
||||
val chapters = combine(
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ErrorReporterReceiver
|
||||
import org.koitharu.kotatsu.core.LocalizedAppContext
|
||||
import org.koitharu.kotatsu.core.model.LocalMangaSource
|
||||
import org.koitharu.kotatsu.core.model.isNsfw
|
||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||
import org.koitharu.kotatsu.core.util.ext.getDrawableOrThrow
|
||||
import org.koitharu.kotatsu.core.util.ext.isReportable
|
||||
@@ -140,10 +141,10 @@ class DownloadNotificationFactory @AssistedInject constructor(
|
||||
builder.setSubText(null)
|
||||
builder.setShowWhen(false)
|
||||
builder.setVisibility(
|
||||
if (state != null && state.manga.isNsfw) {
|
||||
NotificationCompat.VISIBILITY_PRIVATE
|
||||
if (state != null && state.manga.isNsfw()) {
|
||||
NotificationCompat.VISIBILITY_SECRET
|
||||
} else {
|
||||
NotificationCompat.VISIBILITY_PUBLIC
|
||||
NotificationCompat.VISIBILITY_PRIVATE
|
||||
},
|
||||
)
|
||||
when {
|
||||
|
||||
@@ -352,7 +352,7 @@ class SuggestionsWorker @AssistedInject constructor(
|
||||
)
|
||||
setAutoCancel(true)
|
||||
setCategory(NotificationCompat.CATEGORY_RECOMMENDATION)
|
||||
setVisibility(if (manga.isNsfw) NotificationCompat.VISIBILITY_SECRET else NotificationCompat.VISIBILITY_PUBLIC)
|
||||
setVisibility(if (manga.isNsfw()) NotificationCompat.VISIBILITY_SECRET else NotificationCompat.VISIBILITY_PRIVATE)
|
||||
setShortcutId(manga.id.toString())
|
||||
priority = NotificationCompat.PRIORITY_DEFAULT
|
||||
|
||||
|
||||
@@ -27,17 +27,17 @@ abstract class TracksDao : MangaQueryBuilder.ConditionCallback {
|
||||
@Query("SELECT * FROM tracks WHERE manga_id = :mangaId")
|
||||
abstract suspend fun find(mangaId: Long): TrackEntity?
|
||||
|
||||
@Query("SELECT chapters_new FROM tracks WHERE manga_id = :mangaId")
|
||||
abstract suspend fun findNewChapters(mangaId: Long): Int?
|
||||
@Query("SELECT IFNULL(chapters_new,0) FROM tracks WHERE manga_id = :mangaId")
|
||||
abstract suspend fun findNewChapters(mangaId: Long): Int
|
||||
|
||||
@Query("SELECT COUNT(*) FROM tracks")
|
||||
abstract suspend fun getTracksCount(): Int
|
||||
|
||||
@Query("SELECT chapters_new FROM tracks")
|
||||
abstract fun observeNewChapters(): Flow<List<Int>>
|
||||
@Query("SELECT COUNT(*) FROM tracks WHERE chapters_new > 0")
|
||||
abstract fun observeUpdateMangaCount(): Flow<Int>
|
||||
|
||||
@Query("SELECT chapters_new FROM tracks WHERE manga_id = :mangaId")
|
||||
abstract fun observeNewChapters(mangaId: Long): Flow<Int?>
|
||||
@Query("SELECT IFNULL(chapters_new, 0) FROM tracks WHERE manga_id = :mangaId")
|
||||
abstract fun observeNewChapters(mangaId: Long): Flow<Int>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM tracks WHERE chapters_new > 0 ORDER BY last_chapter_date DESC")
|
||||
|
||||
@@ -5,7 +5,6 @@ import androidx.room.withTransaction
|
||||
import dagger.Reusable
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.db.entity.toManga
|
||||
@@ -39,16 +38,16 @@ class TrackingRepository @Inject constructor(
|
||||
private var isGcCalled = AtomicBoolean(false)
|
||||
|
||||
suspend fun getNewChaptersCount(mangaId: Long): Int {
|
||||
return db.getTracksDao().findNewChapters(mangaId) ?: 0
|
||||
return db.getTracksDao().findNewChapters(mangaId)
|
||||
}
|
||||
|
||||
fun observeNewChaptersCount(mangaId: Long): Flow<Int> {
|
||||
return db.getTracksDao().observeNewChapters(mangaId).map { it ?: 0 }
|
||||
return db.getTracksDao().observeNewChapters(mangaId)
|
||||
}
|
||||
|
||||
@Deprecated("")
|
||||
fun observeUpdatedMangaCount(): Flow<Int> {
|
||||
return db.getTracksDao().observeNewChapters().map { list -> list.count { it > 0 } }
|
||||
return db.getTracksDao().observeUpdateMangaCount()
|
||||
.onStart { gcIfNotCalled() }
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ class TrackerDebugViewModel @Inject constructor(
|
||||
|
||||
val content = db.getTracksDao().observeAll()
|
||||
.map { it.toUiList() }
|
||||
.withErrorHandling()
|
||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
private fun List<TrackWithManga>.toUiList(): List<TrackDebugItem> = map {
|
||||
|
||||
@@ -7,7 +7,7 @@ import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.core.app.NotificationChannelCompat
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC
|
||||
import androidx.core.app.NotificationCompat.VISIBILITY_PRIVATE
|
||||
import androidx.core.app.NotificationCompat.VISIBILITY_SECRET
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.app.PendingIntentCompat
|
||||
@@ -17,12 +17,14 @@ import coil3.request.ImageRequest
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.LocalizedAppContext
|
||||
import org.koitharu.kotatsu.core.model.getLocalizedTitle
|
||||
import org.koitharu.kotatsu.core.model.isNsfw
|
||||
import org.koitharu.kotatsu.core.nav.AppRouter
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.util.ext.checkNotificationPermission
|
||||
import org.koitharu.kotatsu.core.util.ext.getQuantityStringSafe
|
||||
import org.koitharu.kotatsu.core.util.ext.mangaSourceExtra
|
||||
import org.koitharu.kotatsu.core.util.ext.toBitmapOrNull
|
||||
import org.koitharu.kotatsu.parsers.model.ContentRating
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import javax.inject.Inject
|
||||
@@ -51,7 +53,7 @@ class TrackerNotificationHelper @Inject constructor(
|
||||
if (newChapters.isEmpty() || !applicationContext.checkNotificationPermission(CHANNEL_ID)) {
|
||||
return null
|
||||
}
|
||||
if (manga.isNsfw && (settings.isTrackerNsfwDisabled || settings.isNsfwContentDisabled)) {
|
||||
if (manga.isNsfw() && (settings.isTrackerNsfwDisabled || settings.isNsfwContentDisabled)) {
|
||||
return null
|
||||
}
|
||||
val id = manga.url.hashCode()
|
||||
@@ -92,7 +94,7 @@ class TrackerNotificationHelper @Inject constructor(
|
||||
false,
|
||||
),
|
||||
)
|
||||
setVisibility(if (manga.isNsfw) VISIBILITY_SECRET else VISIBILITY_PUBLIC)
|
||||
setVisibility(if (manga.isNsfw()) VISIBILITY_SECRET else VISIBILITY_PRIVATE)
|
||||
setShortcutId(manga.id.toString())
|
||||
applyCommonSettings(this)
|
||||
}
|
||||
@@ -127,6 +129,13 @@ class TrackerNotificationHelper @Inject constructor(
|
||||
setNumber(newChaptersCount)
|
||||
setGroup(GROUP_NEW_CHAPTERS)
|
||||
setGroupSummary(true)
|
||||
setVisibility(
|
||||
if (notifications.any { it.manga.isNsfw() }) {
|
||||
VISIBILITY_SECRET
|
||||
} else {
|
||||
VISIBILITY_PRIVATE
|
||||
},
|
||||
)
|
||||
val intent = AppRouter.mangaUpdatesIntent(applicationContext)
|
||||
setContentIntent(
|
||||
PendingIntentCompat.getActivity(
|
||||
|
||||
@@ -1,119 +1,107 @@
|
||||
yaoi
|
||||
yuri
|
||||
trap
|
||||
traps
|
||||
guro
|
||||
furry
|
||||
loli
|
||||
incest
|
||||
tentacles
|
||||
shemale
|
||||
scat
|
||||
яой
|
||||
юри
|
||||
трап
|
||||
копро
|
||||
гуро
|
||||
тентакли
|
||||
футанари
|
||||
инцест
|
||||
boys' love
|
||||
girls' love
|
||||
amputation
|
||||
amputee
|
||||
anal birth
|
||||
anal torture
|
||||
bdsm
|
||||
futanari
|
||||
ntr
|
||||
coprophagia
|
||||
unbirth
|
||||
rape
|
||||
mother
|
||||
beast
|
||||
beastiality
|
||||
bestiality
|
||||
birth
|
||||
blackmail
|
||||
blood
|
||||
body horror
|
||||
bondage
|
||||
boys' love
|
||||
brother
|
||||
bukkake
|
||||
cannibalism
|
||||
cbt
|
||||
choking
|
||||
coprophagia
|
||||
degradation
|
||||
diapers
|
||||
drugs
|
||||
egg laying
|
||||
electrical play
|
||||
electro
|
||||
electro play
|
||||
enema
|
||||
extreme
|
||||
father
|
||||
sister
|
||||
femdom
|
||||
force
|
||||
full censorship
|
||||
furry
|
||||
futanari
|
||||
gang rape
|
||||
gangbang
|
||||
gangbang rape
|
||||
gender bender
|
||||
girls' love
|
||||
guro
|
||||
human pet
|
||||
humiliation
|
||||
hypno
|
||||
incest
|
||||
inflation
|
||||
insect
|
||||
inseki
|
||||
knife play
|
||||
loli
|
||||
lolicon
|
||||
machine
|
||||
mind break
|
||||
mindbreak
|
||||
molestation
|
||||
mosaic
|
||||
mother
|
||||
mutilation
|
||||
necrophila
|
||||
necrophilia
|
||||
netorase
|
||||
nipple torture
|
||||
non-consensual
|
||||
ntr
|
||||
orgasm denial
|
||||
parasite
|
||||
piercing
|
||||
prolapse
|
||||
prostitution
|
||||
public use
|
||||
puke
|
||||
puppy play
|
||||
rape
|
||||
ryona
|
||||
scar
|
||||
scat
|
||||
shemale
|
||||
shota
|
||||
shotacon
|
||||
mother
|
||||
father
|
||||
brother
|
||||
rape
|
||||
blackmail
|
||||
lolicon
|
||||
toddlercon
|
||||
birth
|
||||
mind break
|
||||
ryona
|
||||
beastiality
|
||||
urination
|
||||
sister
|
||||
slave
|
||||
human pet
|
||||
amputee
|
||||
amputation
|
||||
gender bender
|
||||
slavery
|
||||
snuff
|
||||
tentacles
|
||||
toddlercon
|
||||
torture
|
||||
trans
|
||||
transgender
|
||||
full censorship
|
||||
mosaic
|
||||
gang rape
|
||||
furry
|
||||
inseki
|
||||
necrophila
|
||||
prostitution
|
||||
torture
|
||||
vore
|
||||
vaginal birth
|
||||
parasite
|
||||
snuff
|
||||
cannibalism
|
||||
anal birth
|
||||
netorase
|
||||
guro
|
||||
bestiality
|
||||
mutilation
|
||||
vomit
|
||||
inflation
|
||||
necrophilia
|
||||
insect
|
||||
enema
|
||||
diapers
|
||||
beast
|
||||
parasite
|
||||
body horror
|
||||
cbt
|
||||
piercing
|
||||
blood
|
||||
non-consensual
|
||||
machine
|
||||
egg laying
|
||||
femdom
|
||||
humiliation
|
||||
public use
|
||||
bukkake
|
||||
gangbang
|
||||
trap
|
||||
traps
|
||||
unbirth
|
||||
urination
|
||||
incest
|
||||
lolicon
|
||||
drugs
|
||||
slavery
|
||||
degradation
|
||||
bondage
|
||||
watersports
|
||||
choking
|
||||
orgasm denial
|
||||
beastiality
|
||||
electrical play
|
||||
hypno
|
||||
force
|
||||
molestation
|
||||
anal torture
|
||||
prolapse
|
||||
electro
|
||||
knife play
|
||||
scar
|
||||
degradation
|
||||
puke
|
||||
nipple torture
|
||||
extreme
|
||||
vaginal birth
|
||||
violent
|
||||
degradation
|
||||
gangbang rape
|
||||
mindbreak
|
||||
puppy play
|
||||
electro play
|
||||
vomit
|
||||
vore
|
||||
watersports
|
||||
yaoi
|
||||
yuri
|
||||
гуро
|
||||
инцест
|
||||
копро
|
||||
тентакли
|
||||
трап
|
||||
футанари
|
||||
юри
|
||||
яой
|
||||
|
||||
@@ -31,7 +31,7 @@ material = "1.13.0-alpha13"
|
||||
moshi = "1.15.2"
|
||||
okhttp = "4.12.0"
|
||||
okio = "3.11.0"
|
||||
parsers = "e874837efb"
|
||||
parsers = "b165a0d611"
|
||||
preference = "1.2.1"
|
||||
recyclerview = "1.4.0"
|
||||
room = "2.7.1"
|
||||
|
||||
Reference in New Issue
Block a user