From 344cd1305c2f3161a4a5222ef7f7000de6c8b288 Mon Sep 17 00:00:00 2001 From: FooIbar <118464521+FooIbar@users.noreply.github.com> Date: Tue, 18 Jul 2023 19:39:12 +0800 Subject: [PATCH] [DO NOT USE] Refine drag and drop reordering, part 1/3 --- .../com.hippo.ehviewer.dao.EhDatabase/11.json | 455 ++++++++++++++++++ app/src/main/java/com/hippo/ehviewer/EhDB.kt | 28 +- .../java/com/hippo/ehviewer/dao/Database.kt | 13 +- .../com/hippo/ehviewer/dao/QuickSearch.kt | 3 + .../com/hippo/ehviewer/dao/QuickSearchDao.kt | 14 +- .../ehviewer/ui/scene/GalleryListScene.kt | 14 +- 6 files changed, 494 insertions(+), 33 deletions(-) create mode 100644 app/schemas/com.hippo.ehviewer.dao.EhDatabase/11.json diff --git a/app/schemas/com.hippo.ehviewer.dao.EhDatabase/11.json b/app/schemas/com.hippo.ehviewer.dao.EhDatabase/11.json new file mode 100644 index 000000000..20e70dafd --- /dev/null +++ b/app/schemas/com.hippo.ehviewer.dao.EhDatabase/11.json @@ -0,0 +1,455 @@ +{ + "formatVersion": 1, + "database": { + "version": 11, + "identityHash": "ae2e43e12a77ce0d90ac30c9a7f79e5d", + "entities": [ + { + "tableName": "DOWNLOADS", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))", + "fields": [ + { + "fieldPath": "state", + "columnName": "STATE", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "legacy", + "columnName": "LEGACY", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "TIME", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "LABEL", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "gid", + "columnName": "GID", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "TOKEN", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "TITLE", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "titleJpn", + "columnName": "TITLE_JPN", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbKey", + "columnName": "THUMB", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "CATEGORY", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "posted", + "columnName": "POSTED", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploader", + "columnName": "UPLOADER", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "rating", + "columnName": "RATING", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "simpleLanguage", + "columnName": "SIMPLE_LANGUAGE", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "GID" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "DOWNLOAD_LABELS", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `TIME` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "label", + "columnName": "LABEL", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "TIME", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "DOWNLOAD_DIRNAME", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `DIRNAME` TEXT NOT NULL, PRIMARY KEY(`GID`))", + "fields": [ + { + "fieldPath": "gid", + "columnName": "GID", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dirname", + "columnName": "DIRNAME", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "GID" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "FILTER", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`MODE` INTEGER NOT NULL, `TEXT` TEXT NOT NULL, `ENABLE` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "mode", + "columnName": "MODE", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "TEXT", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "enable", + "columnName": "ENABLE", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "HISTORY", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `FAVORITE_SLOT` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))", + "fields": [ + { + "fieldPath": "time", + "columnName": "TIME", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "favoriteSlot", + "columnName": "FAVORITE_SLOT", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "gid", + "columnName": "GID", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "TOKEN", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "TITLE", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "titleJpn", + "columnName": "TITLE_JPN", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbKey", + "columnName": "THUMB", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "CATEGORY", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "posted", + "columnName": "POSTED", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploader", + "columnName": "UPLOADER", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "rating", + "columnName": "RATING", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "simpleLanguage", + "columnName": "SIMPLE_LANGUAGE", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "GID" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "LOCAL_FAVORITES", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`TIME` INTEGER NOT NULL, `GID` INTEGER NOT NULL, `TOKEN` TEXT, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `SIMPLE_LANGUAGE` TEXT, PRIMARY KEY(`GID`))", + "fields": [ + { + "fieldPath": "time", + "columnName": "TIME", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "gid", + "columnName": "GID", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "TOKEN", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "TITLE", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "titleJpn", + "columnName": "TITLE_JPN", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbKey", + "columnName": "THUMB", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "CATEGORY", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "posted", + "columnName": "POSTED", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uploader", + "columnName": "UPLOADER", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "rating", + "columnName": "RATING", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "simpleLanguage", + "columnName": "SIMPLE_LANGUAGE", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "GID" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "QUICK_SEARCH", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER, `NAME` TEXT NOT NULL, `MODE` INTEGER NOT NULL, `CATEGORY` INTEGER NOT NULL, `KEYWORD` TEXT, `ADVANCE_SEARCH` INTEGER NOT NULL, `MIN_RATING` INTEGER NOT NULL, `PAGE_FROM` INTEGER NOT NULL, `PAGE_TO` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `POSITION` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "NAME", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mode", + "columnName": "MODE", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "CATEGORY", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "keyword", + "columnName": "KEYWORD", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "advanceSearch", + "columnName": "ADVANCE_SEARCH", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "minRating", + "columnName": "MIN_RATING", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageFrom", + "columnName": "PAGE_FROM", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageTo", + "columnName": "PAGE_TO", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "TIME", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "POSITION", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "_id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'ae2e43e12a77ce0d90ac30c9a7f79e5d')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hippo/ehviewer/EhDB.kt b/app/src/main/java/com/hippo/ehviewer/EhDB.kt index aa9212e03..19207bf6a 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhDB.kt +++ b/app/src/main/java/com/hippo/ehviewer/EhDB.kt @@ -173,40 +173,23 @@ object EhDB { suspend fun insertQuickSearch(quickSearch: QuickSearch) { val dao = db.quickSearchDao() - quickSearch.time = System.currentTimeMillis() quickSearch.id = dao.insert(quickSearch) } private suspend fun importQuickSearch(quickSearchList: List) { val dao = db.quickSearchDao() - for (quickSearch in quickSearchList) { - dao.insert(quickSearch) - } + dao.insert(quickSearchList) } suspend fun deleteQuickSearch(quickSearch: QuickSearch) { val dao = db.quickSearchDao() dao.delete(quickSearch) + dao.fill(quickSearch.position) } - suspend fun moveQuickSearch(fromPosition: Int, toPosition: Int) { - if (fromPosition == toPosition) return - val reverse = fromPosition > toPosition - val offset = if (reverse) toPosition else fromPosition - val limit = if (reverse) fromPosition - toPosition + 1 else toPosition - fromPosition + 1 + suspend fun updateQuickSearch(quickSearchList: List) { val dao = db.quickSearchDao() - val list = dao.list(offset, limit) - val step = if (reverse) 1 else -1 - val start = if (reverse) limit - 1 else 0 - val end = if (reverse) 0 else limit - 1 - val toTime = list[end].time - var i = end - while (if (reverse) i < start else i > 0) { - list[i].time = list[i + step].time - i += step - } - list[start].time = toTime - dao.update(list) + dao.update(quickSearchList) } val historyLazyList: PagingSource @@ -338,9 +321,10 @@ object EhDB { runCatching { val quickSearchList = oldDB.quickSearchDao().list() val currentQuickSearchList = db.quickSearchDao().list() + val offset = currentQuickSearchList.size val importList = quickSearchList.filter { newQS -> currentQuickSearchList.none { it.name == newQS.name } - } + }.onEachIndexed { index, q -> q.position = index + offset } importQuickSearch(importList) } runCatching { diff --git a/app/src/main/java/com/hippo/ehviewer/dao/Database.kt b/app/src/main/java/com/hippo/ehviewer/dao/Database.kt index e17e8d35e..09a89dfaf 100644 --- a/app/src/main/java/com/hippo/ehviewer/dao/Database.kt +++ b/app/src/main/java/com/hippo/ehviewer/dao/Database.kt @@ -19,6 +19,12 @@ class HistoryMigration : AutoMigrationSpec { } } +class QuickSearchMigration : AutoMigrationSpec { + override fun onPostMigrate(db: SupportSQLiteDatabase) { + db.execSQL("UPDATE QUICK_SEARCH SET POSITION = (SELECT COUNT(*) FROM QUICK_SEARCH T WHERE T.TIME < QUICK_SEARCH.TIME)") + } +} + class ThumbKeyMigration : AutoMigrationSpec { override fun onPostMigrate(db: SupportSQLiteDatabase) { val needMigrationTables = arrayOf( @@ -41,7 +47,7 @@ class ThumbKeyMigration : AutoMigrationSpec { @Database( entities = [DownloadInfo::class, DownloadLabel::class, DownloadDirname::class, Filter::class, HistoryInfo::class, LocalFavoriteInfo::class, QuickSearch::class], - version = 10, + version = 11, exportSchema = true, autoMigrations = [ AutoMigration( @@ -71,6 +77,11 @@ class ThumbKeyMigration : AutoMigrationSpec { to = 10, spec = HistoryMigration::class, ), + AutoMigration( + from = 10, + to = 11, + spec = QuickSearchMigration::class, + ), ], ) @TypeConverters(FilterModeConverter::class) diff --git a/app/src/main/java/com/hippo/ehviewer/dao/QuickSearch.kt b/app/src/main/java/com/hippo/ehviewer/dao/QuickSearch.kt index bae11bb26..4f6d5e9ba 100644 --- a/app/src/main/java/com/hippo/ehviewer/dao/QuickSearch.kt +++ b/app/src/main/java/com/hippo/ehviewer/dao/QuickSearch.kt @@ -36,4 +36,7 @@ data class QuickSearch( @ColumnInfo(name = "TIME") var time: Long = 0, + + @ColumnInfo(name = "POSITION", defaultValue = "0") + var position: Int = 0, ) diff --git a/app/src/main/java/com/hippo/ehviewer/dao/QuickSearchDao.kt b/app/src/main/java/com/hippo/ehviewer/dao/QuickSearchDao.kt index b34abafb8..a70659d4d 100644 --- a/app/src/main/java/com/hippo/ehviewer/dao/QuickSearchDao.kt +++ b/app/src/main/java/com/hippo/ehviewer/dao/QuickSearchDao.kt @@ -8,17 +8,23 @@ import androidx.room.Update @Dao interface QuickSearchDao { - @Query("SELECT * FROM QUICK_SEARCH ORDER BY TIME ASC") + @Query("SELECT * FROM QUICK_SEARCH ORDER BY POSITION ASC") suspend fun list(): List - @Query("SELECT * FROM QUICK_SEARCH ORDER BY TIME ASC LIMIT :limit OFFSET :offset") + @Query("SELECT * FROM QUICK_SEARCH ORDER BY POSITION ASC LIMIT :limit OFFSET :offset") suspend fun list(offset: Int, limit: Int): List + @Query("UPDATE QUICK_SEARCH SET POSITION = POSITION - 1 WHERE POSITION > :position") + suspend fun fill(position: Int) + @Update - suspend fun update(downloadLabels: List) + suspend fun update(quickSearchList: List) @Insert - suspend fun insert(t: QuickSearch): Long + suspend fun insert(quickSearch: QuickSearch): Long + + @Insert + suspend fun insert(quickSearchList: List) @Delete suspend fun delete(quickSearch: QuickSearch) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt index c342abd0a..bbb49763a 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.kt @@ -585,6 +585,7 @@ class GalleryListScene : SearchBarScene() { builder.setError(null) dialog.dismiss() val quickSearch = mUrlBuilder.toQuickSearch(text) + quickSearch.position = mQuickSearchList.size EhDB.insertQuickSearch(quickSearch) mQuickSearchList.add(quickSearch) withUIContext { @@ -1043,13 +1044,14 @@ class GalleryListScene : SearchBarScene() { if (fromPosition == toPosition) { return false } + val item = mQuickSearchList.removeAt(fromPosition) + mQuickSearchList.add(toPosition, item) + mAdapter.notifyItemMoved(fromPosition, toPosition) lifecycleScope.launchIO { - EhDB.moveQuickSearch(fromPosition, toPosition) - val item = mQuickSearchList.removeAt(fromPosition) - mQuickSearchList.add(toPosition, item) - withUIContext { - mAdapter.notifyItemMoved(fromPosition, toPosition) - } + val range = if (fromPosition < toPosition) fromPosition..toPosition else toPosition..fromPosition + val list = mQuickSearchList.slice(range) + list.zip(range).forEach { it.first.position = it.second } + EhDB.updateQuickSearch(list) } return true }