Rewrite E-H favorites sync database, fixes:
- Freezing issues - Build times - Probably fixes bloated app size
This commit is contained in:
parent
5224988265
commit
254d739d12
@ -11,9 +11,6 @@ plugins {
|
||||
kotlin("plugin.parcelize")
|
||||
kotlin("plugin.serialization")
|
||||
id("com.github.zellius.shortcut-helper")
|
||||
// Realm (EH)
|
||||
kotlin("kapt")
|
||||
id("realm-android")
|
||||
}
|
||||
|
||||
if (!gradle.startParameter.taskRequests.toString().contains("Debug")) {
|
||||
@ -32,7 +29,7 @@ android {
|
||||
applicationId = "eu.kanade.tachiyomi.sy"
|
||||
minSdk = AndroidConfig.minSdk
|
||||
targetSdk = AndroidConfig.targetSdk
|
||||
versionCode = 23
|
||||
versionCode = 24
|
||||
versionName = "1.7.0"
|
||||
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||
|
@ -57,7 +57,6 @@ import exh.log.XLogLogcatLogger
|
||||
import exh.log.xLogD
|
||||
import exh.log.xLogE
|
||||
import exh.syDebugVersion
|
||||
import io.realm.Realm
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import logcat.LogPriority
|
||||
@ -99,7 +98,6 @@ open class App : Application(), DefaultLifecycleObserver, ImageLoaderFactory {
|
||||
Injekt.importModule(AppModule(this))
|
||||
|
||||
setupNotificationChannels()
|
||||
Realm.init(this)
|
||||
if ((BuildConfig.DEBUG || BuildConfig.BUILD_TYPE == "releaseTest") && DebugToggles.ENABLE_DEBUG_OVERLAY.enabled) {
|
||||
setupDebugOverlay()
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ import eu.kanade.tachiyomi.data.database.queries.HistoryQueries
|
||||
import eu.kanade.tachiyomi.data.database.queries.MangaCategoryQueries
|
||||
import eu.kanade.tachiyomi.data.database.queries.MangaQueries
|
||||
import eu.kanade.tachiyomi.data.database.queries.TrackQueries
|
||||
import exh.favorites.sql.mappers.FavoriteEntryTypeMapping
|
||||
import exh.favorites.sql.models.FavoriteEntry
|
||||
import exh.favorites.sql.queries.FavoriteEntryQueries
|
||||
import exh.merged.sql.mappers.MergedMangaTypeMapping
|
||||
import exh.merged.sql.models.MergedMangaReference
|
||||
import exh.merged.sql.queries.MergedQueries
|
||||
@ -39,7 +42,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
|
||||
* This class provides operations to manage the database through its interfaces.
|
||||
*/
|
||||
open class DatabaseHelper(context: Context) :
|
||||
MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries, HistoryQueries /* SY --> */, SearchMetadataQueries, SearchTagQueries, SearchTitleQueries, MergedQueries /* SY <-- */ {
|
||||
MangaQueries, ChapterQueries, TrackQueries, CategoryQueries, MangaCategoryQueries, HistoryQueries /* SY --> */, SearchMetadataQueries, SearchTagQueries, SearchTitleQueries, MergedQueries, FavoriteEntryQueries /* SY <-- */ {
|
||||
|
||||
private val configuration = SupportSQLiteOpenHelper.Configuration.builder(context)
|
||||
.name(DbOpenCallback.DATABASE_NAME)
|
||||
@ -59,6 +62,7 @@ open class DatabaseHelper(context: Context) :
|
||||
.addTypeMapping(SearchTag::class.java, SearchTagTypeMapping())
|
||||
.addTypeMapping(SearchTitle::class.java, SearchTitleTypeMapping())
|
||||
.addTypeMapping(MergedMangaReference::class.java, MergedMangaTypeMapping())
|
||||
.addTypeMapping(FavoriteEntry::class.java, FavoriteEntryTypeMapping())
|
||||
// SY <--
|
||||
.build()
|
||||
|
||||
|
@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.database.tables.HistoryTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||
import eu.kanade.tachiyomi.data.database.tables.TrackTable
|
||||
import exh.favorites.sql.tables.FavoriteEntryTable
|
||||
import exh.merged.sql.tables.MergedTable
|
||||
import exh.metadata.sql.tables.SearchMetadataTable
|
||||
import exh.metadata.sql.tables.SearchTagTable
|
||||
@ -24,7 +25,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
||||
/**
|
||||
* Version of the database.
|
||||
*/
|
||||
const val DATABASE_VERSION = /* SY --> */ 10 /* SY <-- */
|
||||
const val DATABASE_VERSION = /* SY --> */ 11 /* SY <-- */
|
||||
}
|
||||
|
||||
override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
|
||||
@ -93,6 +94,9 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
||||
if (oldVersion < 10) {
|
||||
db.execSQL(ChapterTable.fixDateUploadIfNeeded)
|
||||
}
|
||||
if (oldVersion < 11) {
|
||||
db.execSQL(FavoriteEntryTable.createTableQuery)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigure(db: SupportSQLiteDatabase) {
|
||||
|
@ -25,6 +25,7 @@ import eu.kanade.tachiyomi.source.online.UrlImportableSource
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import eu.kanade.tachiyomi.util.lang.runAsObservable
|
||||
import eu.kanade.tachiyomi.util.lang.withIOContext
|
||||
import exh.debug.DebugToggles
|
||||
import exh.eh.EHTags
|
||||
import exh.eh.EHentaiUpdateHelper
|
||||
@ -716,7 +717,7 @@ class EHentai(
|
||||
throw UnsupportedOperationException("Unused method was called somehow!")
|
||||
}
|
||||
|
||||
fun fetchFavorites(): Pair<List<ParsedManga>, List<String>> {
|
||||
suspend fun fetchFavorites(): Pair<List<ParsedManga>, List<String>> {
|
||||
val favoriteUrl = "$baseUrl/favorites.php"
|
||||
val result = mutableListOf<ParsedManga>()
|
||||
var page = 1
|
||||
@ -724,13 +725,15 @@ class EHentai(
|
||||
var favNames: List<String>? = null
|
||||
|
||||
do {
|
||||
val response2 = client.newCall(
|
||||
exGet(
|
||||
favoriteUrl,
|
||||
page = page,
|
||||
cache = false
|
||||
)
|
||||
).execute()
|
||||
val response2 = withIOContext {
|
||||
client.newCall(
|
||||
exGet(
|
||||
favoriteUrl,
|
||||
page = page,
|
||||
cache = false
|
||||
)
|
||||
).awaitResponse()
|
||||
}
|
||||
val doc = response2.asJsoup()
|
||||
|
||||
// Parse favorites
|
||||
|
@ -40,7 +40,6 @@ import exh.eh.EHentaiUpdateWorker
|
||||
import exh.eh.EHentaiUpdateWorkerConstants
|
||||
import exh.eh.EHentaiUpdaterStats
|
||||
import exh.favorites.FavoritesIntroDialog
|
||||
import exh.favorites.LocalFavoritesStorage
|
||||
import exh.log.xLogD
|
||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||
import exh.metadata.metadata.base.getFlatMetadataForManga
|
||||
@ -49,7 +48,6 @@ import exh.uconfig.WarnConfigureDialogController
|
||||
import exh.ui.login.EhLoginActivity
|
||||
import exh.util.executeOnIO
|
||||
import exh.util.nullIfBlank
|
||||
import exh.util.trans
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@ -362,12 +360,8 @@ class SettingsEhController : SettingsController() {
|
||||
.setTitle(R.string.favorites_sync_reset)
|
||||
.setMessage(R.string.favorites_sync_reset_message)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
LocalFavoritesStorage().apply {
|
||||
getRealm().use {
|
||||
it.trans {
|
||||
clearSnapshots(it)
|
||||
}
|
||||
}
|
||||
db.inTransaction {
|
||||
db.deleteAllFavoriteEntries().executeAsBlocking()
|
||||
}
|
||||
activity.toast(context.getString(R.string.sync_state_reset), Toast.LENGTH_LONG)
|
||||
}
|
||||
|
@ -350,6 +350,26 @@ object EXHMigrations {
|
||||
preferences.libraryUpdateMangaRestriction() -= MANGA_ONGOING
|
||||
}
|
||||
}
|
||||
if (oldVersion under 24) {
|
||||
try {
|
||||
sequenceOf(
|
||||
"fav-sync",
|
||||
"fav-sync.management",
|
||||
"fav-sync.lock",
|
||||
"fav-sync.note"
|
||||
).map {
|
||||
File(context.filesDir, it)
|
||||
}.filter(File::exists).forEach {
|
||||
if (it.isDirectory) {
|
||||
it.deleteRecursively()
|
||||
} else {
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
xLogE("Failed to delete old favorites database", e)
|
||||
}
|
||||
}
|
||||
|
||||
// if (oldVersion under 1) { } (1 is current release version)
|
||||
// do stuff here when releasing changed crap
|
||||
|
@ -16,7 +16,6 @@ import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
|
||||
import exh.log.xLogStack
|
||||
import exh.source.getMainSource
|
||||
import exh.util.maybeRunBlocking
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
@ -52,8 +51,7 @@ class GalleryAdder {
|
||||
url: String,
|
||||
fav: Boolean = false,
|
||||
forceSource: UrlImportableSource? = null,
|
||||
throttleFunc: suspend () -> Unit = {},
|
||||
protectTrans: Boolean = false
|
||||
throttleFunc: suspend () -> Unit = {}
|
||||
): GalleryAddEvent {
|
||||
logger.d(context.getString(R.string.gallery_adder_importing_manga, url, fav.toString(), forceSource))
|
||||
try {
|
||||
@ -132,9 +130,8 @@ class GalleryAdder {
|
||||
}
|
||||
|
||||
// Fetch and copy details
|
||||
val newManga = maybeRunBlocking(protectTrans) {
|
||||
source.getMangaDetails(manga.toMangaInfo())
|
||||
}
|
||||
val newManga = source.getMangaDetails(manga.toMangaInfo())
|
||||
|
||||
manga.copyFrom(newManga.toSManga())
|
||||
manga.initialized = true
|
||||
|
||||
@ -147,16 +144,14 @@ class GalleryAdder {
|
||||
|
||||
// Fetch and copy chapters
|
||||
try {
|
||||
maybeRunBlocking(protectTrans) {
|
||||
val chapterList = if (source is EHentai) {
|
||||
source.getChapterList(manga.toMangaInfo(), throttleFunc)
|
||||
} else {
|
||||
source.getChapterList(manga.toMangaInfo())
|
||||
}.map { it.toSChapter() }
|
||||
val chapterList = if (source is EHentai) {
|
||||
source.getChapterList(manga.toMangaInfo(), throttleFunc)
|
||||
} else {
|
||||
source.getChapterList(manga.toMangaInfo())
|
||||
}.map { it.toSChapter() }
|
||||
|
||||
if (chapterList.isNotEmpty()) {
|
||||
syncChaptersWithSource(db, chapterList, manga, source)
|
||||
}
|
||||
if (chapterList.isNotEmpty()) {
|
||||
syncChaptersWithSource(db, chapterList, manga, source)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logger.w(context.getString(R.string.gallery_adder_chapter_fetch_error, manga.title), e)
|
||||
|
@ -1,23 +0,0 @@
|
||||
package exh.favorites
|
||||
|
||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.Index
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import io.realm.annotations.RealmClass
|
||||
import java.util.UUID
|
||||
|
||||
@RealmClass
|
||||
open class FavoriteEntry : RealmObject() {
|
||||
@PrimaryKey var id: String = UUID.randomUUID().toString()
|
||||
|
||||
var title: String? = null
|
||||
|
||||
@Index lateinit var gid: String
|
||||
|
||||
@Index lateinit var token: String
|
||||
|
||||
@Index var category: Int = -1
|
||||
|
||||
fun getUrl() = EHentaiSearchMetadata.idAndTokenToUrl(gid, token)
|
||||
}
|
@ -21,19 +21,21 @@ import exh.GalleryAddEvent
|
||||
import exh.GalleryAdder
|
||||
import exh.eh.EHentaiThrottleManager
|
||||
import exh.eh.EHentaiUpdateWorker
|
||||
import exh.favorites.sql.models.FavoriteEntry
|
||||
import exh.log.xLog
|
||||
import exh.source.EH_SOURCE_ID
|
||||
import exh.source.EXH_SOURCE_ID
|
||||
import exh.source.isEhBasedManga
|
||||
import exh.util.ignore
|
||||
import exh.util.trans
|
||||
import exh.util.wifiManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Request
|
||||
import uy.kohesive.injekt.Injekt
|
||||
@ -48,6 +50,9 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
|
||||
private val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
private val dispatcher = newSingleThreadContext("Favorites-sync-worker")
|
||||
|
||||
private val exh by lazy {
|
||||
Injekt.get<SourceManager>().get(EXH_SOURCE_ID) as? EHentai
|
||||
?: EHentai(0, true, context)
|
||||
@ -74,7 +79,7 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
|
||||
status.value = FavoritesSyncStatus.Initializing(context)
|
||||
|
||||
scope.launch(Dispatchers.IO) { beginSync() }
|
||||
scope.launch(dispatcher) { beginSync() }
|
||||
}
|
||||
|
||||
private suspend fun beginSync() {
|
||||
@ -134,32 +139,28 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
// Do not update galleries while syncing favorites
|
||||
EHentaiUpdateWorker.cancelBackground(context)
|
||||
|
||||
storage.getRealm().use { realm ->
|
||||
realm.trans {
|
||||
db.inTransaction {
|
||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_calculating_remote_changes), context = context)
|
||||
val remoteChanges = storage.getChangedRemoteEntries(realm, favorites.first)
|
||||
val localChanges = if (prefs.exhReadOnlySync().get()) {
|
||||
null // Do not build local changes if they are not going to be applied
|
||||
} else {
|
||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_calculating_local_changes), context = context)
|
||||
storage.getChangedDbEntries(realm)
|
||||
}
|
||||
|
||||
// Apply remote categories
|
||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_syncing_category_names), context = context)
|
||||
applyRemoteCategories(favorites.second)
|
||||
|
||||
// Apply change sets
|
||||
applyChangeSetToLocal(errorList, remoteChanges)
|
||||
if (localChanges != null) {
|
||||
applyChangeSetToRemote(errorList, localChanges)
|
||||
}
|
||||
|
||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_cleaning_up), context = context)
|
||||
storage.snapshotEntries(realm)
|
||||
}
|
||||
db.inTransaction {
|
||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_calculating_remote_changes), context = context)
|
||||
val remoteChanges = storage.getChangedRemoteEntries(favorites.first)
|
||||
val localChanges = if (prefs.exhReadOnlySync().get()) {
|
||||
null // Do not build local changes if they are not going to be applied
|
||||
} else {
|
||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_calculating_local_changes), context = context)
|
||||
storage.getChangedDbEntries()
|
||||
}
|
||||
|
||||
// Apply remote categories
|
||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_syncing_category_names), context = context)
|
||||
applyRemoteCategories(favorites.second)
|
||||
|
||||
// Apply change sets
|
||||
applyChangeSetToLocal(errorList, remoteChanges)
|
||||
if (localChanges != null) {
|
||||
applyChangeSetToRemote(errorList, localChanges)
|
||||
}
|
||||
|
||||
status.value = FavoritesSyncStatus.Processing(context.getString(R.string.favorites_sync_cleaning_up), context = context)
|
||||
storage.snapshotEntries()
|
||||
}
|
||||
|
||||
launchUI {
|
||||
@ -378,8 +379,7 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
"${exh.baseUrl}${it.getUrl()}",
|
||||
true,
|
||||
exh,
|
||||
throttleManager::throttle,
|
||||
true
|
||||
throttleManager::throttle
|
||||
)
|
||||
|
||||
if (result is GalleryAddEvent.Fail) {
|
||||
@ -424,6 +424,7 @@ class FavoritesSyncHelper(val context: Context) {
|
||||
|
||||
fun onDestroy() {
|
||||
scope.cancel()
|
||||
dispatcher.close()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -3,92 +3,70 @@ package exh.favorites
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.source.online.all.EHentai
|
||||
import exh.favorites.sql.models.FavoriteEntry
|
||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||
import exh.source.isEhBasedManga
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class LocalFavoritesStorage {
|
||||
private val db: DatabaseHelper by injectLazy()
|
||||
|
||||
private val realmConfig = RealmConfiguration.Builder()
|
||||
.name("fav-sync")
|
||||
.deleteRealmIfMigrationNeeded()
|
||||
.build()
|
||||
fun getChangedDbEntries() = db.getFavoriteMangas()
|
||||
.executeAsBlocking()
|
||||
.asSequence()
|
||||
.loadDbCategories()
|
||||
.parseToFavoriteEntries()
|
||||
.getChangedEntries()
|
||||
|
||||
fun getRealm(): Realm = Realm.getInstance(realmConfig)
|
||||
fun getChangedRemoteEntries(entries: List<EHentai.ParsedManga>) = entries
|
||||
.asSequence()
|
||||
.map {
|
||||
it.fav to it.manga.apply {
|
||||
favorite = true
|
||||
date_added = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
.parseToFavoriteEntries()
|
||||
.getChangedEntries()
|
||||
|
||||
fun getChangedDbEntries(realm: Realm) =
|
||||
getChangedEntries(
|
||||
realm,
|
||||
parseToFavoriteEntries(
|
||||
loadDbCategories(
|
||||
db.getFavoriteMangas()
|
||||
.executeAsBlocking()
|
||||
.asSequence()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun getChangedRemoteEntries(realm: Realm, entries: List<EHentai.ParsedManga>) =
|
||||
getChangedEntries(
|
||||
realm,
|
||||
parseToFavoriteEntries(
|
||||
entries.asSequence().map {
|
||||
it.fav to it.manga.apply {
|
||||
favorite = true
|
||||
date_added = System.currentTimeMillis()
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
fun snapshotEntries(realm: Realm) {
|
||||
val dbMangas = parseToFavoriteEntries(
|
||||
loadDbCategories(
|
||||
db.getFavoriteMangas()
|
||||
.executeAsBlocking()
|
||||
.asSequence()
|
||||
)
|
||||
)
|
||||
fun snapshotEntries() {
|
||||
val dbMangas = db.getFavoriteMangas()
|
||||
.executeAsBlocking()
|
||||
.asSequence()
|
||||
.loadDbCategories()
|
||||
.parseToFavoriteEntries()
|
||||
|
||||
// Delete old snapshot
|
||||
realm.delete(FavoriteEntry::class.java)
|
||||
db.deleteAllFavoriteEntries().executeAsBlocking()
|
||||
|
||||
// Insert new snapshots
|
||||
realm.copyToRealm(dbMangas.toList())
|
||||
db.insertFavoriteEntries(dbMangas.toList()).executeAsBlocking()
|
||||
}
|
||||
|
||||
fun clearSnapshots(realm: Realm) {
|
||||
realm.delete(FavoriteEntry::class.java)
|
||||
fun clearSnapshots() {
|
||||
db.deleteAllFavoriteEntries().executeAsBlocking()
|
||||
}
|
||||
|
||||
private fun getChangedEntries(realm: Realm, entries: Sequence<FavoriteEntry>): ChangeSet {
|
||||
val terminated = entries.toList()
|
||||
private fun Sequence<FavoriteEntry>.getChangedEntries(): ChangeSet {
|
||||
val terminated = toList()
|
||||
|
||||
val databaseEntries = db.getFavoriteEntries().executeAsBlocking()
|
||||
|
||||
val added = terminated.filter {
|
||||
realm.queryRealmForEntry(it) == null
|
||||
queryListForEntry(databaseEntries, it) == null
|
||||
}
|
||||
|
||||
val removed = realm.where(FavoriteEntry::class.java)
|
||||
.findAll()
|
||||
val removed = databaseEntries
|
||||
.filter {
|
||||
queryListForEntry(terminated, it) == null
|
||||
}.map {
|
||||
} /*.map {
|
||||
todo see what this does
|
||||
realm.copyFromRealm(it)
|
||||
}
|
||||
}*/
|
||||
|
||||
return ChangeSet(added, removed)
|
||||
}
|
||||
|
||||
private fun Realm.queryRealmForEntry(entry: FavoriteEntry) =
|
||||
where(FavoriteEntry::class.java)
|
||||
.equalTo(FavoriteEntry::gid.name, entry.gid)
|
||||
.equalTo(FavoriteEntry::token.name, entry.token)
|
||||
.equalTo(FavoriteEntry::category.name, entry.category)
|
||||
.findFirst()
|
||||
|
||||
private fun queryListForEntry(list: List<FavoriteEntry>, entry: FavoriteEntry) =
|
||||
list.find {
|
||||
it.gid == entry.gid &&
|
||||
@ -96,10 +74,10 @@ class LocalFavoritesStorage {
|
||||
it.category == entry.category
|
||||
}
|
||||
|
||||
private fun loadDbCategories(manga: Sequence<Manga>): Sequence<Pair<Int, Manga>> {
|
||||
private fun Sequence<Manga>.loadDbCategories(): Sequence<Pair<Int, Manga>> {
|
||||
val dbCategories = db.getCategories().executeAsBlocking()
|
||||
|
||||
return manga.filter(this::validateDbManga).mapNotNull {
|
||||
return filter(::validateDbManga).mapNotNull {
|
||||
val category = db.getCategoriesForManga(it).executeAsBlocking()
|
||||
|
||||
dbCategories.indexOf(
|
||||
@ -109,17 +87,17 @@ class LocalFavoritesStorage {
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseToFavoriteEntries(manga: Sequence<Pair<Int, Manga>>) =
|
||||
manga.filter {
|
||||
validateDbManga(it.second)
|
||||
}.mapNotNull {
|
||||
FavoriteEntry().apply {
|
||||
title = it.second.originalTitle
|
||||
gid = EHentaiSearchMetadata.galleryId(it.second.url)
|
||||
token = EHentaiSearchMetadata.galleryToken(it.second.url)
|
||||
category = it.first
|
||||
|
||||
if (this.category > MAX_CATEGORIES) {
|
||||
private fun Sequence<Pair<Int, Manga>>.parseToFavoriteEntries() =
|
||||
filter { (_, manga) ->
|
||||
validateDbManga(manga)
|
||||
}.mapNotNull { (categoryId, manga) ->
|
||||
FavoriteEntry(
|
||||
title = manga.originalTitle,
|
||||
gid = EHentaiSearchMetadata.galleryId(manga.url),
|
||||
token = EHentaiSearchMetadata.galleryToken(manga.url),
|
||||
category = categoryId
|
||||
).also {
|
||||
if (it.category > MAX_CATEGORIES) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
package exh.favorites.sql.mappers
|
||||
|
||||
import android.database.Cursor
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.pushtorefresh.storio.sqlite.SQLiteTypeMapping
|
||||
import com.pushtorefresh.storio.sqlite.operations.delete.DefaultDeleteResolver
|
||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.DefaultPutResolver
|
||||
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
||||
import com.pushtorefresh.storio.sqlite.queries.InsertQuery
|
||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
||||
import exh.favorites.sql.models.FavoriteEntry
|
||||
import exh.favorites.sql.tables.FavoriteEntryTable.COL_CATEGORY
|
||||
import exh.favorites.sql.tables.FavoriteEntryTable.COL_GID
|
||||
import exh.favorites.sql.tables.FavoriteEntryTable.COL_ID
|
||||
import exh.favorites.sql.tables.FavoriteEntryTable.COL_TITLE
|
||||
import exh.favorites.sql.tables.FavoriteEntryTable.COL_TOKEN
|
||||
import exh.favorites.sql.tables.FavoriteEntryTable.TABLE
|
||||
|
||||
class FavoriteEntryTypeMapping : SQLiteTypeMapping<FavoriteEntry>(
|
||||
FavoriteEntryPutResolver(),
|
||||
FavoriteEntryGetResolver(),
|
||||
FavoriteEntryDeleteResolver()
|
||||
)
|
||||
|
||||
class FavoriteEntryPutResolver : DefaultPutResolver<FavoriteEntry>() {
|
||||
|
||||
override fun mapToInsertQuery(obj: FavoriteEntry) = InsertQuery.builder()
|
||||
.table(TABLE)
|
||||
.build()
|
||||
|
||||
override fun mapToUpdateQuery(obj: FavoriteEntry) = UpdateQuery.builder()
|
||||
.table(TABLE)
|
||||
.where("$COL_ID = ?")
|
||||
.whereArgs(obj.id)
|
||||
.build()
|
||||
|
||||
override fun mapToContentValues(obj: FavoriteEntry) = contentValuesOf(
|
||||
COL_ID to obj.id,
|
||||
COL_TITLE to obj.title,
|
||||
COL_GID to obj.gid,
|
||||
COL_TOKEN to obj.token,
|
||||
COL_CATEGORY to obj.category
|
||||
)
|
||||
}
|
||||
|
||||
class FavoriteEntryGetResolver : DefaultGetResolver<FavoriteEntry>() {
|
||||
|
||||
override fun mapFromCursor(cursor: Cursor): FavoriteEntry = FavoriteEntry(
|
||||
id = cursor.getLong(cursor.getColumnIndexOrThrow(COL_ID)),
|
||||
title = cursor.getString(cursor.getColumnIndexOrThrow(COL_TITLE)),
|
||||
gid = cursor.getString(cursor.getColumnIndexOrThrow(COL_GID)),
|
||||
token = cursor.getString(cursor.getColumnIndexOrThrow(COL_TOKEN)),
|
||||
category = cursor.getInt(cursor.getColumnIndexOrThrow(COL_CATEGORY))
|
||||
)
|
||||
}
|
||||
|
||||
class FavoriteEntryDeleteResolver : DefaultDeleteResolver<FavoriteEntry>() {
|
||||
|
||||
override fun mapToDeleteQuery(obj: FavoriteEntry) = DeleteQuery.builder()
|
||||
.table(TABLE)
|
||||
.where("$COL_ID = ?")
|
||||
.whereArgs(obj.id)
|
||||
.build()
|
||||
}
|
17
app/src/main/java/exh/favorites/sql/models/FavoriteEntry.kt
Normal file
17
app/src/main/java/exh/favorites/sql/models/FavoriteEntry.kt
Normal file
@ -0,0 +1,17 @@
|
||||
package exh.favorites.sql.models
|
||||
|
||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||
|
||||
data class FavoriteEntry(
|
||||
val id: Long? = null,
|
||||
|
||||
val title: String,
|
||||
|
||||
val gid: String,
|
||||
|
||||
val token: String,
|
||||
|
||||
val category: Int = -1,
|
||||
) {
|
||||
fun getUrl() = EHentaiSearchMetadata.idAndTokenToUrl(gid, token)
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package exh.favorites.sql.queries
|
||||
|
||||
import com.pushtorefresh.storio.sqlite.queries.DeleteQuery
|
||||
import com.pushtorefresh.storio.sqlite.queries.Query
|
||||
import eu.kanade.tachiyomi.data.database.DbProvider
|
||||
import exh.favorites.sql.models.FavoriteEntry
|
||||
import exh.favorites.sql.tables.FavoriteEntryTable
|
||||
|
||||
interface FavoriteEntryQueries : DbProvider {
|
||||
fun getFavoriteEntries() = db.get()
|
||||
.listOfObjects(FavoriteEntry::class.java)
|
||||
.withQuery(
|
||||
Query.builder()
|
||||
.table(FavoriteEntryTable.TABLE)
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
|
||||
fun insertFavoriteEntries(favoriteEntries: List<FavoriteEntry>) = db.put()
|
||||
.objects(favoriteEntries)
|
||||
.prepare()
|
||||
|
||||
fun deleteAllFavoriteEntries() = db.delete()
|
||||
.byQuery(
|
||||
DeleteQuery.builder()
|
||||
.table(FavoriteEntryTable.TABLE)
|
||||
.build()
|
||||
)
|
||||
.prepare()
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package exh.favorites.sql.tables
|
||||
|
||||
object FavoriteEntryTable {
|
||||
|
||||
const val TABLE = "eh_favorites"
|
||||
|
||||
const val COL_ID = "_id"
|
||||
|
||||
const val COL_TITLE = "title"
|
||||
|
||||
const val COL_GID = "gid"
|
||||
|
||||
const val COL_TOKEN = "token"
|
||||
|
||||
const val COL_CATEGORY = "category"
|
||||
|
||||
val createTableQuery: String
|
||||
get() =
|
||||
"""CREATE TABLE $TABLE(
|
||||
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
||||
$COL_TITLE TEXT NOT NULL,
|
||||
$COL_GID TEXT NOT NULL,
|
||||
$COL_TOKEN TEXT NOT NULL,
|
||||
$COL_CATEGORY INTEGER NOT NULL
|
||||
)"""
|
||||
}
|
@ -3,25 +3,8 @@ package exh.util
|
||||
import kotlinx.coroutines.ensureActive
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.coroutines.coroutineContext
|
||||
|
||||
fun <T> Flow<T>.cancellable() = onEach {
|
||||
coroutineContext.ensureActive()
|
||||
}
|
||||
|
||||
@Suppress("BlockingMethodInNonBlockingContext")
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
suspend inline fun <T> maybeRunBlocking(runBlocking: Boolean, crossinline block: suspend () -> T): T {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
return if (runBlocking) {
|
||||
runBlocking { block() }
|
||||
} else {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
@ -1,542 +0,0 @@
|
||||
package exh.util
|
||||
|
||||
import io.realm.Case
|
||||
import io.realm.RealmModel
|
||||
import io.realm.RealmQuery
|
||||
import io.realm.RealmResults
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Realm query with logging
|
||||
*
|
||||
* @author nulldev
|
||||
*/
|
||||
|
||||
inline fun <reified E : RealmModel> RealmQuery<out E>.beginLog(
|
||||
clazz: Class<out E>? =
|
||||
E::class.java
|
||||
): LoggingRealmQuery<out E> =
|
||||
LoggingRealmQuery.fromQuery(this, clazz)
|
||||
|
||||
class LoggingRealmQuery<E : RealmModel>(val query: RealmQuery<E>) {
|
||||
companion object {
|
||||
fun <E : RealmModel> fromQuery(q: RealmQuery<out E>, clazz: Class<out E>?) =
|
||||
LoggingRealmQuery(q).apply {
|
||||
log += "SELECT * FROM ${clazz?.name ?: "???"} WHERE"
|
||||
}
|
||||
}
|
||||
|
||||
private val log = mutableListOf<String>()
|
||||
|
||||
private fun sec(section: String) = "{$section}"
|
||||
|
||||
fun log() = log.joinToString(separator = " ")
|
||||
|
||||
fun isValid(): Boolean {
|
||||
return query.isValid
|
||||
}
|
||||
|
||||
fun isNull(fieldName: String): RealmQuery<E> {
|
||||
log += sec("\"$fieldName\" IS NULL")
|
||||
return query.isNull(fieldName)
|
||||
}
|
||||
|
||||
fun isNotNull(fieldName: String): RealmQuery<E> {
|
||||
log += sec("\"$fieldName\" IS NOT NULL")
|
||||
return query.isNotNull(fieldName)
|
||||
}
|
||||
|
||||
private fun appendEqualTo(fieldName: String, value: String, casing: Case? = null) {
|
||||
log += sec(
|
||||
"\"$fieldName\" == \"$value\"" + (
|
||||
casing?.let {
|
||||
" CASE ${casing.name}"
|
||||
}.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: String): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value)
|
||||
return query.equalTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: String, casing: Case): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value, casing)
|
||||
return query.equalTo(fieldName, value, casing)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: Byte?): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value.toString())
|
||||
return query.equalTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: ByteArray): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value.toString())
|
||||
return query.equalTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: Short?): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value.toString())
|
||||
return query.equalTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: Int?): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value.toString())
|
||||
return query.equalTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: Long?): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value.toString())
|
||||
return query.equalTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: Double?): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value.toString())
|
||||
return query.equalTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: Float?): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value.toString())
|
||||
return query.equalTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: Boolean?): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value.toString())
|
||||
return query.equalTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun equalTo(fieldName: String, value: Date): RealmQuery<E> {
|
||||
appendEqualTo(fieldName, value.toString())
|
||||
return query.equalTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun appendIn(fieldName: String, values: Array<out Any?>, casing: Case? = null) {
|
||||
log += sec(
|
||||
"[${values.joinToString(
|
||||
separator = ", ",
|
||||
transform = {
|
||||
"\"$it\""
|
||||
}
|
||||
)}] IN \"$fieldName\"" + (
|
||||
casing?.let {
|
||||
" CASE ${casing.name}"
|
||||
}.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun `in`(fieldName: String, values: Array<String>): RealmQuery<E> {
|
||||
appendIn(fieldName, values)
|
||||
return query.`in`(fieldName, values)
|
||||
}
|
||||
|
||||
fun `in`(fieldName: String, values: Array<String>, casing: Case): RealmQuery<E> {
|
||||
appendIn(fieldName, values, casing)
|
||||
return query.`in`(fieldName, values, casing)
|
||||
}
|
||||
|
||||
fun `in`(fieldName: String, values: Array<Byte>): RealmQuery<E> {
|
||||
appendIn(fieldName, values)
|
||||
return query.`in`(fieldName, values)
|
||||
}
|
||||
|
||||
fun `in`(fieldName: String, values: Array<Short>): RealmQuery<E> {
|
||||
appendIn(fieldName, values)
|
||||
return query.`in`(fieldName, values)
|
||||
}
|
||||
|
||||
fun `in`(fieldName: String, values: Array<Int>): RealmQuery<E> {
|
||||
appendIn(fieldName, values)
|
||||
return query.`in`(fieldName, values)
|
||||
}
|
||||
|
||||
fun `in`(fieldName: String, values: Array<Long>): RealmQuery<E> {
|
||||
appendIn(fieldName, values)
|
||||
return query.`in`(fieldName, values)
|
||||
}
|
||||
|
||||
fun `in`(fieldName: String, values: Array<Double>): RealmQuery<E> {
|
||||
appendIn(fieldName, values)
|
||||
return query.`in`(fieldName, values)
|
||||
}
|
||||
|
||||
fun `in`(fieldName: String, values: Array<Float>): RealmQuery<E> {
|
||||
appendIn(fieldName, values)
|
||||
return query.`in`(fieldName, values)
|
||||
}
|
||||
|
||||
fun `in`(fieldName: String, values: Array<Boolean>): RealmQuery<E> {
|
||||
appendIn(fieldName, values)
|
||||
return query.`in`(fieldName, values)
|
||||
}
|
||||
|
||||
fun `in`(fieldName: String, values: Array<Date>): RealmQuery<E> {
|
||||
appendIn(fieldName, values)
|
||||
return query.`in`(fieldName, values)
|
||||
}
|
||||
|
||||
private fun appendNotEqualTo(fieldName: String, value: Any?, casing: Case? = null) {
|
||||
log += sec(
|
||||
"\"$fieldName\" != \"$value\"" + (
|
||||
casing?.let {
|
||||
" CASE ${casing.name}"
|
||||
}.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: String): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value)
|
||||
return query.notEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: String, casing: Case): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value, casing)
|
||||
return query.notEqualTo(fieldName, value, casing)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: Byte?): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value)
|
||||
return query.notEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: ByteArray): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value)
|
||||
return query.notEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: Short?): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value)
|
||||
return query.notEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: Int?): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value)
|
||||
return query.notEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: Long?): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value)
|
||||
return query.notEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: Double?): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value)
|
||||
return query.notEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: Float?): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value)
|
||||
return query.notEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: Boolean?): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value)
|
||||
return query.notEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun notEqualTo(fieldName: String, value: Date): RealmQuery<E> {
|
||||
appendNotEqualTo(fieldName, value)
|
||||
return query.notEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
private fun appendGreaterThan(fieldName: String, value: Any?) {
|
||||
log += sec("\"$fieldName\" > $value")
|
||||
}
|
||||
|
||||
fun greaterThan(fieldName: String, value: Int): RealmQuery<E> {
|
||||
appendGreaterThan(fieldName, value)
|
||||
return query.greaterThan(fieldName, value)
|
||||
}
|
||||
|
||||
fun greaterThan(fieldName: String, value: Long): RealmQuery<E> {
|
||||
appendGreaterThan(fieldName, value)
|
||||
return query.greaterThan(fieldName, value)
|
||||
}
|
||||
|
||||
fun greaterThan(fieldName: String, value: Double): RealmQuery<E> {
|
||||
appendGreaterThan(fieldName, value)
|
||||
return query.greaterThan(fieldName, value)
|
||||
}
|
||||
|
||||
fun greaterThan(fieldName: String, value: Float): RealmQuery<E> {
|
||||
appendGreaterThan(fieldName, value)
|
||||
return query.greaterThan(fieldName, value)
|
||||
}
|
||||
|
||||
fun greaterThan(fieldName: String, value: Date): RealmQuery<E> {
|
||||
appendGreaterThan(fieldName, value)
|
||||
return query.greaterThan(fieldName, value)
|
||||
}
|
||||
|
||||
private fun appendGreaterThanOrEqualTo(fieldName: String, value: Any?) {
|
||||
log += sec("\"$fieldName\" >= $value")
|
||||
}
|
||||
|
||||
fun greaterThanOrEqualTo(fieldName: String, value: Int): RealmQuery<E> {
|
||||
appendGreaterThanOrEqualTo(fieldName, value)
|
||||
return query.greaterThanOrEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun greaterThanOrEqualTo(fieldName: String, value: Long): RealmQuery<E> {
|
||||
appendGreaterThanOrEqualTo(fieldName, value)
|
||||
return query.greaterThanOrEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun greaterThanOrEqualTo(fieldName: String, value: Double): RealmQuery<E> {
|
||||
appendGreaterThanOrEqualTo(fieldName, value)
|
||||
return query.greaterThanOrEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun greaterThanOrEqualTo(fieldName: String, value: Float): RealmQuery<E> {
|
||||
appendGreaterThanOrEqualTo(fieldName, value)
|
||||
return query.greaterThanOrEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun greaterThanOrEqualTo(fieldName: String, value: Date): RealmQuery<E> {
|
||||
appendGreaterThanOrEqualTo(fieldName, value)
|
||||
return query.greaterThanOrEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
private fun appendLessThan(fieldName: String, value: Any?) {
|
||||
log += sec("\"$fieldName\" < $value")
|
||||
}
|
||||
|
||||
fun lessThan(fieldName: String, value: Int): RealmQuery<E> {
|
||||
appendLessThan(fieldName, value)
|
||||
return query.lessThan(fieldName, value)
|
||||
}
|
||||
|
||||
fun lessThan(fieldName: String, value: Long): RealmQuery<E> {
|
||||
appendLessThan(fieldName, value)
|
||||
return query.lessThan(fieldName, value)
|
||||
}
|
||||
|
||||
fun lessThan(fieldName: String, value: Double): RealmQuery<E> {
|
||||
appendLessThan(fieldName, value)
|
||||
return query.lessThan(fieldName, value)
|
||||
}
|
||||
|
||||
fun lessThan(fieldName: String, value: Float): RealmQuery<E> {
|
||||
appendLessThan(fieldName, value)
|
||||
return query.lessThan(fieldName, value)
|
||||
}
|
||||
|
||||
fun lessThan(fieldName: String, value: Date): RealmQuery<E> {
|
||||
appendLessThan(fieldName, value)
|
||||
return query.lessThan(fieldName, value)
|
||||
}
|
||||
|
||||
private fun appendLessThanOrEqualTo(fieldName: String, value: Any?) {
|
||||
log += sec("\"$fieldName\" <= $value")
|
||||
}
|
||||
|
||||
fun lessThanOrEqualTo(fieldName: String, value: Int): RealmQuery<E> {
|
||||
appendLessThanOrEqualTo(fieldName, value)
|
||||
return query.lessThanOrEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun lessThanOrEqualTo(fieldName: String, value: Long): RealmQuery<E> {
|
||||
appendLessThanOrEqualTo(fieldName, value)
|
||||
return query.lessThanOrEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun lessThanOrEqualTo(fieldName: String, value: Double): RealmQuery<E> {
|
||||
appendLessThanOrEqualTo(fieldName, value)
|
||||
return query.lessThanOrEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun lessThanOrEqualTo(fieldName: String, value: Float): RealmQuery<E> {
|
||||
appendLessThanOrEqualTo(fieldName, value)
|
||||
return query.lessThanOrEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
fun lessThanOrEqualTo(fieldName: String, value: Date): RealmQuery<E> {
|
||||
appendLessThanOrEqualTo(fieldName, value)
|
||||
return query.lessThanOrEqualTo(fieldName, value)
|
||||
}
|
||||
|
||||
private fun appendBetween(fieldName: String, from: Any?, to: Any?) {
|
||||
log += sec("\"$fieldName\" BETWEEN $from - $to")
|
||||
}
|
||||
|
||||
fun between(fieldName: String, from: Int, to: Int): RealmQuery<E> {
|
||||
appendBetween(fieldName, from, to)
|
||||
return query.between(fieldName, from, to)
|
||||
}
|
||||
|
||||
fun between(fieldName: String, from: Long, to: Long): RealmQuery<E> {
|
||||
appendBetween(fieldName, from, to)
|
||||
return query.between(fieldName, from, to)
|
||||
}
|
||||
|
||||
fun between(fieldName: String, from: Double, to: Double): RealmQuery<E> {
|
||||
appendBetween(fieldName, from, to)
|
||||
return query.between(fieldName, from, to)
|
||||
}
|
||||
|
||||
fun between(fieldName: String, from: Float, to: Float): RealmQuery<E> {
|
||||
appendBetween(fieldName, from, to)
|
||||
return query.between(fieldName, from, to)
|
||||
}
|
||||
|
||||
fun between(fieldName: String, from: Date, to: Date): RealmQuery<E> {
|
||||
appendBetween(fieldName, from, to)
|
||||
return query.between(fieldName, from, to)
|
||||
}
|
||||
|
||||
private fun appendContains(fieldName: String, value: Any?, casing: Case? = null) {
|
||||
log += sec(
|
||||
"\"$fieldName\" CONTAINS \"$value\"" + (
|
||||
casing?.let {
|
||||
" CASE ${casing.name}"
|
||||
}.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun contains(fieldName: String, value: String): RealmQuery<E> {
|
||||
appendContains(fieldName, value)
|
||||
return query.contains(fieldName, value)
|
||||
}
|
||||
|
||||
fun contains(fieldName: String, value: String, casing: Case): RealmQuery<E> {
|
||||
appendContains(fieldName, value, casing)
|
||||
return query.contains(fieldName, value, casing)
|
||||
}
|
||||
|
||||
private fun appendBeginsWith(fieldName: String, value: Any?, casing: Case? = null) {
|
||||
log += sec(
|
||||
"\"$fieldName\" BEGINS WITH \"$value\"" + (
|
||||
casing?.let {
|
||||
" CASE ${casing.name}"
|
||||
}.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun beginsWith(fieldName: String, value: String): RealmQuery<E> {
|
||||
appendBeginsWith(fieldName, value)
|
||||
return query.beginsWith(fieldName, value)
|
||||
}
|
||||
|
||||
fun beginsWith(fieldName: String, value: String, casing: Case): RealmQuery<E> {
|
||||
appendBeginsWith(fieldName, value, casing)
|
||||
return query.beginsWith(fieldName, value, casing)
|
||||
}
|
||||
|
||||
private fun appendEndsWith(fieldName: String, value: Any?, casing: Case? = null) {
|
||||
log += sec(
|
||||
"\"$fieldName\" ENDS WITH \"$value\"" + (
|
||||
casing?.let {
|
||||
" CASE ${casing.name}"
|
||||
}.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun endsWith(fieldName: String, value: String): RealmQuery<E> {
|
||||
appendEndsWith(fieldName, value)
|
||||
return query.endsWith(fieldName, value)
|
||||
}
|
||||
|
||||
fun endsWith(fieldName: String, value: String, casing: Case): RealmQuery<E> {
|
||||
appendEndsWith(fieldName, value, casing)
|
||||
return query.endsWith(fieldName, value, casing)
|
||||
}
|
||||
|
||||
private fun appendLike(fieldName: String, value: Any?, casing: Case? = null) {
|
||||
log += sec(
|
||||
"\"$fieldName\" LIKE \"$value\"" + (
|
||||
casing?.let {
|
||||
" CASE ${casing.name}"
|
||||
}.orEmpty()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun like(fieldName: String, value: String): RealmQuery<E> {
|
||||
appendLike(fieldName, value)
|
||||
return query.like(fieldName, value)
|
||||
}
|
||||
|
||||
fun like(fieldName: String, value: String, casing: Case): RealmQuery<E> {
|
||||
appendLike(fieldName, value, casing)
|
||||
return query.like(fieldName, value, casing)
|
||||
}
|
||||
|
||||
fun beginGroup(): RealmQuery<E> {
|
||||
log += "("
|
||||
return query.beginGroup()
|
||||
}
|
||||
|
||||
fun endGroup(): RealmQuery<E> {
|
||||
log += ")"
|
||||
return query.endGroup()
|
||||
}
|
||||
|
||||
fun or(): RealmQuery<E> {
|
||||
log += "OR"
|
||||
return query.or()
|
||||
}
|
||||
|
||||
operator fun not(): RealmQuery<E> {
|
||||
log += "NOT"
|
||||
return query.not()
|
||||
}
|
||||
|
||||
fun isEmpty(fieldName: String): RealmQuery<E> {
|
||||
log += "\"$fieldName\" IS EMPTY"
|
||||
return query.isEmpty(fieldName)
|
||||
}
|
||||
|
||||
fun isNotEmpty(fieldName: String): RealmQuery<E> {
|
||||
log += "\"$fieldName\" IS NOT EMPTY"
|
||||
return query.isNotEmpty(fieldName)
|
||||
}
|
||||
|
||||
fun sum(fieldName: String): Number {
|
||||
return query.sum(fieldName)
|
||||
}
|
||||
|
||||
fun average(fieldName: String): Double {
|
||||
return query.average(fieldName)
|
||||
}
|
||||
|
||||
fun min(fieldName: String): Number? {
|
||||
return query.min(fieldName)
|
||||
}
|
||||
|
||||
fun minimumDate(fieldName: String): Date? {
|
||||
return query.minimumDate(fieldName)
|
||||
}
|
||||
|
||||
fun max(fieldName: String): Number? {
|
||||
return query.max(fieldName)
|
||||
}
|
||||
|
||||
fun maximumDate(fieldName: String): Date? {
|
||||
return query.maximumDate(fieldName)
|
||||
}
|
||||
|
||||
fun count(): Long {
|
||||
return query.count()
|
||||
}
|
||||
|
||||
fun findAll(): RealmResults<E> {
|
||||
return query.findAll()
|
||||
}
|
||||
|
||||
fun findAllAsync(): RealmResults<E> {
|
||||
return query.findAllAsync()
|
||||
}
|
||||
|
||||
fun findFirst(): E? {
|
||||
return query.findFirst()
|
||||
}
|
||||
|
||||
fun findFirstAsync(): E {
|
||||
return query.findFirstAsync()
|
||||
}
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package exh.util
|
||||
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmModel
|
||||
import io.realm.log.RealmLog
|
||||
import java.util.UUID
|
||||
|
||||
inline fun <T> realmTrans(block: (Realm) -> T): T {
|
||||
return defRealm {
|
||||
it.trans {
|
||||
block(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> defRealm(block: (Realm) -> T): T {
|
||||
return Realm.getDefaultInstance().use {
|
||||
block(it)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> Realm.trans(block: () -> T): T {
|
||||
beginTransaction()
|
||||
try {
|
||||
val res = block()
|
||||
commitTransaction()
|
||||
return res
|
||||
} catch (t: Throwable) {
|
||||
if (isInTransaction) {
|
||||
cancelTransaction()
|
||||
} else {
|
||||
RealmLog.warn("Could not cancel transaction, not currently in a transaction.")
|
||||
}
|
||||
|
||||
throw t
|
||||
} finally {
|
||||
// Just in case
|
||||
if (isInTransaction) {
|
||||
cancelTransaction()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> Realm.useTrans(block: (Realm) -> T): T {
|
||||
return use {
|
||||
trans {
|
||||
block(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T : RealmModel> Realm.createUUIDObj(clazz: Class<T>) =
|
||||
createObject(clazz, UUID.randomUUID().toString())!!
|
||||
|
||||
inline fun <reified T : RealmModel> Realm.createUUIDObj() =
|
||||
createUUIDObj(T::class.java)
|
@ -31,9 +31,6 @@ buildscript {
|
||||
classpath("com.google.gms:google-services:4.3.10")
|
||||
classpath("com.mikepenz.aboutlibraries.plugin:aboutlibraries-plugin:${BuildPluginsVersion.ABOUTLIB_PLUGIN}")
|
||||
classpath(kotlin("serialization", version = BuildPluginsVersion.KOTLIN))
|
||||
// Realm (EH)
|
||||
classpath("io.realm:realm-gradle-plugin:10.8.0")
|
||||
|
||||
// Firebase Crashlytics
|
||||
classpath("com.google.firebase:firebase-crashlytics-gradle:2.8.0")
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user