From 254d739d127dab60f407ed348e646c33523b395d Mon Sep 17 00:00:00 2001 From: Jobobby04 Date: Sun, 23 Jan 2022 16:40:15 -0500 Subject: [PATCH] Rewrite E-H favorites sync database, fixes: - Freezing issues - Build times - Probably fixes bloated app size --- app/build.gradle.kts | 5 +- app/src/main/java/eu/kanade/tachiyomi/App.kt | 2 - .../tachiyomi/data/database/DatabaseHelper.kt | 6 +- .../tachiyomi/data/database/DbOpenCallback.kt | 6 +- .../tachiyomi/source/online/all/EHentai.kt | 19 +- .../ui/setting/SettingsEhController.kt | 10 +- app/src/main/java/exh/EXHMigrations.kt | 20 + app/src/main/java/exh/GalleryAdder.kt | 25 +- .../main/java/exh/favorites/FavoriteEntry.kt | 23 - .../java/exh/favorites/FavoritesSyncHelper.kt | 59 +- .../exh/favorites/LocalFavoritesStorage.kt | 120 ++-- .../sql/mappers/FavoriteEntryTypeMapping.kt | 65 +++ .../exh/favorites/sql/models/FavoriteEntry.kt | 17 + .../sql/queries/FavoriteEntryQueries.kt | 30 + .../sql/tables/FavoriteEntryTable.kt | 26 + app/src/main/java/exh/util/CoroutineUtil.kt | 17 - .../main/java/exh/util/LoggingRealmQuery.kt | 542 ------------------ app/src/main/java/exh/util/RealmUtil.kt | 56 -- build.gradle.kts | 3 - 19 files changed, 271 insertions(+), 780 deletions(-) delete mode 100644 app/src/main/java/exh/favorites/FavoriteEntry.kt create mode 100644 app/src/main/java/exh/favorites/sql/mappers/FavoriteEntryTypeMapping.kt create mode 100644 app/src/main/java/exh/favorites/sql/models/FavoriteEntry.kt create mode 100644 app/src/main/java/exh/favorites/sql/queries/FavoriteEntryQueries.kt create mode 100644 app/src/main/java/exh/favorites/sql/tables/FavoriteEntryTable.kt delete mode 100644 app/src/main/java/exh/util/LoggingRealmQuery.kt delete mode 100644 app/src/main/java/exh/util/RealmUtil.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d421373d3..71f869713 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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()}\"") diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index 8c9d74c4f..8af0b3c91 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -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() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt index 61101df93..0406967f6 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt @@ -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() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt index d6cb5f072..f700368c2 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt @@ -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) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt index 4872ad0e0..15f6faa07 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt @@ -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> { + suspend fun fetchFavorites(): Pair, List> { val favoriteUrl = "$baseUrl/favorites.php" val result = mutableListOf() var page = 1 @@ -724,13 +725,15 @@ class EHentai( var favNames: List? = 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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt index 243a43e2e..1f0a10fd4 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt @@ -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) } diff --git a/app/src/main/java/exh/EXHMigrations.kt b/app/src/main/java/exh/EXHMigrations.kt index edfdd11de..96e689b2a 100644 --- a/app/src/main/java/exh/EXHMigrations.kt +++ b/app/src/main/java/exh/EXHMigrations.kt @@ -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 diff --git a/app/src/main/java/exh/GalleryAdder.kt b/app/src/main/java/exh/GalleryAdder.kt index bdc1237ac..597abf1ee 100755 --- a/app/src/main/java/exh/GalleryAdder.kt +++ b/app/src/main/java/exh/GalleryAdder.kt @@ -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) diff --git a/app/src/main/java/exh/favorites/FavoriteEntry.kt b/app/src/main/java/exh/favorites/FavoriteEntry.kt deleted file mode 100644 index b57edbbdb..000000000 --- a/app/src/main/java/exh/favorites/FavoriteEntry.kt +++ /dev/null @@ -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) -} diff --git a/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt b/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt index 10715c333..ac2eb9262 100644 --- a/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt +++ b/app/src/main/java/exh/favorites/FavoritesSyncHelper.kt @@ -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().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 { diff --git a/app/src/main/java/exh/favorites/LocalFavoritesStorage.kt b/app/src/main/java/exh/favorites/LocalFavoritesStorage.kt index 34d1b9857..0f6b51708 100644 --- a/app/src/main/java/exh/favorites/LocalFavoritesStorage.kt +++ b/app/src/main/java/exh/favorites/LocalFavoritesStorage.kt @@ -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) = 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) = - 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): ChangeSet { - val terminated = entries.toList() + private fun Sequence.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, entry: FavoriteEntry) = list.find { it.gid == entry.gid && @@ -96,10 +74,10 @@ class LocalFavoritesStorage { it.category == entry.category } - private fun loadDbCategories(manga: Sequence): Sequence> { + private fun Sequence.loadDbCategories(): Sequence> { 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>) = - 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>.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 } } diff --git a/app/src/main/java/exh/favorites/sql/mappers/FavoriteEntryTypeMapping.kt b/app/src/main/java/exh/favorites/sql/mappers/FavoriteEntryTypeMapping.kt new file mode 100644 index 000000000..83104c166 --- /dev/null +++ b/app/src/main/java/exh/favorites/sql/mappers/FavoriteEntryTypeMapping.kt @@ -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( + FavoriteEntryPutResolver(), + FavoriteEntryGetResolver(), + FavoriteEntryDeleteResolver() +) + +class FavoriteEntryPutResolver : DefaultPutResolver() { + + 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() { + + 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() { + + override fun mapToDeleteQuery(obj: FavoriteEntry) = DeleteQuery.builder() + .table(TABLE) + .where("$COL_ID = ?") + .whereArgs(obj.id) + .build() +} diff --git a/app/src/main/java/exh/favorites/sql/models/FavoriteEntry.kt b/app/src/main/java/exh/favorites/sql/models/FavoriteEntry.kt new file mode 100644 index 000000000..c41be75f2 --- /dev/null +++ b/app/src/main/java/exh/favorites/sql/models/FavoriteEntry.kt @@ -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) +} diff --git a/app/src/main/java/exh/favorites/sql/queries/FavoriteEntryQueries.kt b/app/src/main/java/exh/favorites/sql/queries/FavoriteEntryQueries.kt new file mode 100644 index 000000000..1abe99886 --- /dev/null +++ b/app/src/main/java/exh/favorites/sql/queries/FavoriteEntryQueries.kt @@ -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) = db.put() + .objects(favoriteEntries) + .prepare() + + fun deleteAllFavoriteEntries() = db.delete() + .byQuery( + DeleteQuery.builder() + .table(FavoriteEntryTable.TABLE) + .build() + ) + .prepare() +} diff --git a/app/src/main/java/exh/favorites/sql/tables/FavoriteEntryTable.kt b/app/src/main/java/exh/favorites/sql/tables/FavoriteEntryTable.kt new file mode 100644 index 000000000..24424d6be --- /dev/null +++ b/app/src/main/java/exh/favorites/sql/tables/FavoriteEntryTable.kt @@ -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 + )""" +} diff --git a/app/src/main/java/exh/util/CoroutineUtil.kt b/app/src/main/java/exh/util/CoroutineUtil.kt index 0daa273f0..5ba733aa8 100644 --- a/app/src/main/java/exh/util/CoroutineUtil.kt +++ b/app/src/main/java/exh/util/CoroutineUtil.kt @@ -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 Flow.cancellable() = onEach { coroutineContext.ensureActive() } - -@Suppress("BlockingMethodInNonBlockingContext") -@OptIn(ExperimentalContracts::class) -suspend inline fun maybeRunBlocking(runBlocking: Boolean, crossinline block: suspend () -> T): T { - contract { - callsInPlace(block, InvocationKind.EXACTLY_ONCE) - } - return if (runBlocking) { - runBlocking { block() } - } else { - block() - } -} diff --git a/app/src/main/java/exh/util/LoggingRealmQuery.kt b/app/src/main/java/exh/util/LoggingRealmQuery.kt deleted file mode 100644 index 73a65f26a..000000000 --- a/app/src/main/java/exh/util/LoggingRealmQuery.kt +++ /dev/null @@ -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 RealmQuery.beginLog( - clazz: Class? = - E::class.java -): LoggingRealmQuery = - LoggingRealmQuery.fromQuery(this, clazz) - -class LoggingRealmQuery(val query: RealmQuery) { - companion object { - fun fromQuery(q: RealmQuery, clazz: Class?) = - LoggingRealmQuery(q).apply { - log += "SELECT * FROM ${clazz?.name ?: "???"} WHERE" - } - } - - private val log = mutableListOf() - - private fun sec(section: String) = "{$section}" - - fun log() = log.joinToString(separator = " ") - - fun isValid(): Boolean { - return query.isValid - } - - fun isNull(fieldName: String): RealmQuery { - log += sec("\"$fieldName\" IS NULL") - return query.isNull(fieldName) - } - - fun isNotNull(fieldName: String): RealmQuery { - 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 { - appendEqualTo(fieldName, value) - return query.equalTo(fieldName, value) - } - - fun equalTo(fieldName: String, value: String, casing: Case): RealmQuery { - appendEqualTo(fieldName, value, casing) - return query.equalTo(fieldName, value, casing) - } - - fun equalTo(fieldName: String, value: Byte?): RealmQuery { - appendEqualTo(fieldName, value.toString()) - return query.equalTo(fieldName, value) - } - - fun equalTo(fieldName: String, value: ByteArray): RealmQuery { - appendEqualTo(fieldName, value.toString()) - return query.equalTo(fieldName, value) - } - - fun equalTo(fieldName: String, value: Short?): RealmQuery { - appendEqualTo(fieldName, value.toString()) - return query.equalTo(fieldName, value) - } - - fun equalTo(fieldName: String, value: Int?): RealmQuery { - appendEqualTo(fieldName, value.toString()) - return query.equalTo(fieldName, value) - } - - fun equalTo(fieldName: String, value: Long?): RealmQuery { - appendEqualTo(fieldName, value.toString()) - return query.equalTo(fieldName, value) - } - - fun equalTo(fieldName: String, value: Double?): RealmQuery { - appendEqualTo(fieldName, value.toString()) - return query.equalTo(fieldName, value) - } - - fun equalTo(fieldName: String, value: Float?): RealmQuery { - appendEqualTo(fieldName, value.toString()) - return query.equalTo(fieldName, value) - } - - fun equalTo(fieldName: String, value: Boolean?): RealmQuery { - appendEqualTo(fieldName, value.toString()) - return query.equalTo(fieldName, value) - } - - fun equalTo(fieldName: String, value: Date): RealmQuery { - appendEqualTo(fieldName, value.toString()) - return query.equalTo(fieldName, value) - } - - fun appendIn(fieldName: String, values: Array, casing: Case? = null) { - log += sec( - "[${values.joinToString( - separator = ", ", - transform = { - "\"$it\"" - } - )}] IN \"$fieldName\"" + ( - casing?.let { - " CASE ${casing.name}" - }.orEmpty() - ) - ) - } - - fun `in`(fieldName: String, values: Array): RealmQuery { - appendIn(fieldName, values) - return query.`in`(fieldName, values) - } - - fun `in`(fieldName: String, values: Array, casing: Case): RealmQuery { - appendIn(fieldName, values, casing) - return query.`in`(fieldName, values, casing) - } - - fun `in`(fieldName: String, values: Array): RealmQuery { - appendIn(fieldName, values) - return query.`in`(fieldName, values) - } - - fun `in`(fieldName: String, values: Array): RealmQuery { - appendIn(fieldName, values) - return query.`in`(fieldName, values) - } - - fun `in`(fieldName: String, values: Array): RealmQuery { - appendIn(fieldName, values) - return query.`in`(fieldName, values) - } - - fun `in`(fieldName: String, values: Array): RealmQuery { - appendIn(fieldName, values) - return query.`in`(fieldName, values) - } - - fun `in`(fieldName: String, values: Array): RealmQuery { - appendIn(fieldName, values) - return query.`in`(fieldName, values) - } - - fun `in`(fieldName: String, values: Array): RealmQuery { - appendIn(fieldName, values) - return query.`in`(fieldName, values) - } - - fun `in`(fieldName: String, values: Array): RealmQuery { - appendIn(fieldName, values) - return query.`in`(fieldName, values) - } - - fun `in`(fieldName: String, values: Array): RealmQuery { - 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 { - appendNotEqualTo(fieldName, value) - return query.notEqualTo(fieldName, value) - } - - fun notEqualTo(fieldName: String, value: String, casing: Case): RealmQuery { - appendNotEqualTo(fieldName, value, casing) - return query.notEqualTo(fieldName, value, casing) - } - - fun notEqualTo(fieldName: String, value: Byte?): RealmQuery { - appendNotEqualTo(fieldName, value) - return query.notEqualTo(fieldName, value) - } - - fun notEqualTo(fieldName: String, value: ByteArray): RealmQuery { - appendNotEqualTo(fieldName, value) - return query.notEqualTo(fieldName, value) - } - - fun notEqualTo(fieldName: String, value: Short?): RealmQuery { - appendNotEqualTo(fieldName, value) - return query.notEqualTo(fieldName, value) - } - - fun notEqualTo(fieldName: String, value: Int?): RealmQuery { - appendNotEqualTo(fieldName, value) - return query.notEqualTo(fieldName, value) - } - - fun notEqualTo(fieldName: String, value: Long?): RealmQuery { - appendNotEqualTo(fieldName, value) - return query.notEqualTo(fieldName, value) - } - - fun notEqualTo(fieldName: String, value: Double?): RealmQuery { - appendNotEqualTo(fieldName, value) - return query.notEqualTo(fieldName, value) - } - - fun notEqualTo(fieldName: String, value: Float?): RealmQuery { - appendNotEqualTo(fieldName, value) - return query.notEqualTo(fieldName, value) - } - - fun notEqualTo(fieldName: String, value: Boolean?): RealmQuery { - appendNotEqualTo(fieldName, value) - return query.notEqualTo(fieldName, value) - } - - fun notEqualTo(fieldName: String, value: Date): RealmQuery { - 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 { - appendGreaterThan(fieldName, value) - return query.greaterThan(fieldName, value) - } - - fun greaterThan(fieldName: String, value: Long): RealmQuery { - appendGreaterThan(fieldName, value) - return query.greaterThan(fieldName, value) - } - - fun greaterThan(fieldName: String, value: Double): RealmQuery { - appendGreaterThan(fieldName, value) - return query.greaterThan(fieldName, value) - } - - fun greaterThan(fieldName: String, value: Float): RealmQuery { - appendGreaterThan(fieldName, value) - return query.greaterThan(fieldName, value) - } - - fun greaterThan(fieldName: String, value: Date): RealmQuery { - 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 { - appendGreaterThanOrEqualTo(fieldName, value) - return query.greaterThanOrEqualTo(fieldName, value) - } - - fun greaterThanOrEqualTo(fieldName: String, value: Long): RealmQuery { - appendGreaterThanOrEqualTo(fieldName, value) - return query.greaterThanOrEqualTo(fieldName, value) - } - - fun greaterThanOrEqualTo(fieldName: String, value: Double): RealmQuery { - appendGreaterThanOrEqualTo(fieldName, value) - return query.greaterThanOrEqualTo(fieldName, value) - } - - fun greaterThanOrEqualTo(fieldName: String, value: Float): RealmQuery { - appendGreaterThanOrEqualTo(fieldName, value) - return query.greaterThanOrEqualTo(fieldName, value) - } - - fun greaterThanOrEqualTo(fieldName: String, value: Date): RealmQuery { - 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 { - appendLessThan(fieldName, value) - return query.lessThan(fieldName, value) - } - - fun lessThan(fieldName: String, value: Long): RealmQuery { - appendLessThan(fieldName, value) - return query.lessThan(fieldName, value) - } - - fun lessThan(fieldName: String, value: Double): RealmQuery { - appendLessThan(fieldName, value) - return query.lessThan(fieldName, value) - } - - fun lessThan(fieldName: String, value: Float): RealmQuery { - appendLessThan(fieldName, value) - return query.lessThan(fieldName, value) - } - - fun lessThan(fieldName: String, value: Date): RealmQuery { - 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 { - appendLessThanOrEqualTo(fieldName, value) - return query.lessThanOrEqualTo(fieldName, value) - } - - fun lessThanOrEqualTo(fieldName: String, value: Long): RealmQuery { - appendLessThanOrEqualTo(fieldName, value) - return query.lessThanOrEqualTo(fieldName, value) - } - - fun lessThanOrEqualTo(fieldName: String, value: Double): RealmQuery { - appendLessThanOrEqualTo(fieldName, value) - return query.lessThanOrEqualTo(fieldName, value) - } - - fun lessThanOrEqualTo(fieldName: String, value: Float): RealmQuery { - appendLessThanOrEqualTo(fieldName, value) - return query.lessThanOrEqualTo(fieldName, value) - } - - fun lessThanOrEqualTo(fieldName: String, value: Date): RealmQuery { - 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 { - appendBetween(fieldName, from, to) - return query.between(fieldName, from, to) - } - - fun between(fieldName: String, from: Long, to: Long): RealmQuery { - appendBetween(fieldName, from, to) - return query.between(fieldName, from, to) - } - - fun between(fieldName: String, from: Double, to: Double): RealmQuery { - appendBetween(fieldName, from, to) - return query.between(fieldName, from, to) - } - - fun between(fieldName: String, from: Float, to: Float): RealmQuery { - appendBetween(fieldName, from, to) - return query.between(fieldName, from, to) - } - - fun between(fieldName: String, from: Date, to: Date): RealmQuery { - 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 { - appendContains(fieldName, value) - return query.contains(fieldName, value) - } - - fun contains(fieldName: String, value: String, casing: Case): RealmQuery { - 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 { - appendBeginsWith(fieldName, value) - return query.beginsWith(fieldName, value) - } - - fun beginsWith(fieldName: String, value: String, casing: Case): RealmQuery { - 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 { - appendEndsWith(fieldName, value) - return query.endsWith(fieldName, value) - } - - fun endsWith(fieldName: String, value: String, casing: Case): RealmQuery { - 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 { - appendLike(fieldName, value) - return query.like(fieldName, value) - } - - fun like(fieldName: String, value: String, casing: Case): RealmQuery { - appendLike(fieldName, value, casing) - return query.like(fieldName, value, casing) - } - - fun beginGroup(): RealmQuery { - log += "(" - return query.beginGroup() - } - - fun endGroup(): RealmQuery { - log += ")" - return query.endGroup() - } - - fun or(): RealmQuery { - log += "OR" - return query.or() - } - - operator fun not(): RealmQuery { - log += "NOT" - return query.not() - } - - fun isEmpty(fieldName: String): RealmQuery { - log += "\"$fieldName\" IS EMPTY" - return query.isEmpty(fieldName) - } - - fun isNotEmpty(fieldName: String): RealmQuery { - 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 { - return query.findAll() - } - - fun findAllAsync(): RealmResults { - return query.findAllAsync() - } - - fun findFirst(): E? { - return query.findFirst() - } - - fun findFirstAsync(): E { - return query.findFirstAsync() - } -} diff --git a/app/src/main/java/exh/util/RealmUtil.kt b/app/src/main/java/exh/util/RealmUtil.kt deleted file mode 100644 index 475bea220..000000000 --- a/app/src/main/java/exh/util/RealmUtil.kt +++ /dev/null @@ -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 realmTrans(block: (Realm) -> T): T { - return defRealm { - it.trans { - block(it) - } - } -} - -inline fun defRealm(block: (Realm) -> T): T { - return Realm.getDefaultInstance().use { - block(it) - } -} - -inline fun 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 Realm.useTrans(block: (Realm) -> T): T { - return use { - trans { - block(this) - } - } -} - -fun Realm.createUUIDObj(clazz: Class) = - createObject(clazz, UUID.randomUUID().toString())!! - -inline fun Realm.createUUIDObj() = - createUUIDObj(T::class.java) diff --git a/build.gradle.kts b/build.gradle.kts index eda78d6f0..571ee2e1a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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") }