diff --git a/app/schemas/com.hippo.ehviewer.dao.EhDatabase/20.json b/app/schemas/com.hippo.ehviewer.dao.EhDatabase/20.json new file mode 100644 index 000000000..0ff91048b --- /dev/null +++ b/app/schemas/com.hippo.ehviewer.dao.EhDatabase/20.json @@ -0,0 +1,472 @@ +{ + "formatVersion": 1, + "database": { + "version": 20, + "identityHash": "a8dd0226b286cc57a8c99ba7ad416730", + "entities": [ + { + "tableName": "GALLERIES", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TOKEN` TEXT NOT NULL, `TITLE` TEXT, `TITLE_JPN` TEXT, `THUMB` TEXT, `CATEGORY` INTEGER NOT NULL, `POSTED` TEXT, `UPLOADER` TEXT, `RATING` REAL NOT NULL, `PAGES` INTEGER NOT NULL DEFAULT 0, `SIMPLE_LANGUAGE` TEXT, `FAVORITE_SLOT` INTEGER NOT NULL, PRIMARY KEY(`GID`))", + "fields": [ + { + "fieldPath": "gid", + "columnName": "GID", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "token", + "columnName": "TOKEN", + "affinity": "TEXT", + "notNull": true + }, + { + "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": "pages", + "columnName": "PAGES", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "simpleLanguage", + "columnName": "SIMPLE_LANGUAGE", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "favoriteSlot", + "columnName": "FAVORITE_SLOT", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "GID" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "DOWNLOAD_LABELS", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`LABEL` TEXT NOT NULL, `POSITION` INTEGER NOT NULL, `_id` INTEGER, PRIMARY KEY(`_id`))", + "fields": [ + { + "fieldPath": "label", + "columnName": "LABEL", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "position", + "columnName": "POSITION", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "_id" + ] + }, + "indices": [ + { + "name": "index_DOWNLOAD_LABELS_LABEL", + "unique": true, + "columnNames": [ + "LABEL" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DOWNLOAD_LABELS_LABEL` ON `${TABLE_NAME}` (`LABEL`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "DOWNLOADS", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `STATE` INTEGER NOT NULL, `LEGACY` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, `LABEL` TEXT, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION , FOREIGN KEY(`LABEL`) REFERENCES `DOWNLOAD_LABELS`(`LABEL`) ON UPDATE CASCADE ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "gid", + "columnName": "GID", + "affinity": "INTEGER", + "notNull": true + }, + { + "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 + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "GID" + ] + }, + "indices": [ + { + "name": "index_DOWNLOADS_LABEL", + "unique": false, + "columnNames": [ + "LABEL" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DOWNLOADS_LABEL` ON `${TABLE_NAME}` (`LABEL`)" + } + ], + "foreignKeys": [ + { + "table": "GALLERIES", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "GID" + ], + "referencedColumns": [ + "GID" + ] + }, + { + "table": "DOWNLOAD_LABELS", + "onDelete": "SET NULL", + "onUpdate": "CASCADE", + "columns": [ + "LABEL" + ], + "referencedColumns": [ + "LABEL" + ] + } + ] + }, + { + "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}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "gid", + "columnName": "GID", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "TIME", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "GID" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "GALLERIES", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "GID" + ], + "referencedColumns": [ + "GID" + ] + } + ] + }, + { + "tableName": "LOCAL_FAVORITES", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `TIME` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "gid", + "columnName": "GID", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "TIME", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "GID" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "GALLERIES", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "GID" + ], + "referencedColumns": [ + "GID" + ] + } + ] + }, + { + "tableName": "PROGRESS", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`GID` INTEGER NOT NULL, `PAGE` INTEGER NOT NULL, PRIMARY KEY(`GID`), FOREIGN KEY(`GID`) REFERENCES `GALLERIES`(`GID`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "gid", + "columnName": "GID", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "page", + "columnName": "PAGE", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "GID" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "GALLERIES", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "GID" + ], + "referencedColumns": [ + "GID" + ] + } + ] + }, + { + "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, `POSITION` INTEGER NOT NULL, 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": "position", + "columnName": "POSITION", + "affinity": "INTEGER", + "notNull": true + } + ], + "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, 'a8dd0226b286cc57a8c99ba7ad416730')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/hippo/ehviewer/EhApplication.kt b/app/src/main/kotlin/com/hippo/ehviewer/EhApplication.kt index 16d5f03cd..c568fb86c 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/EhApplication.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/EhApplication.kt @@ -117,7 +117,7 @@ class EhApplication : Application(), SingletonImageLoader.Factory { EhDB } launchIO { - DownloadManager.isIdle + DownloadManager.readPagesFromLocal() } launchIO { runSuspendCatching { diff --git a/app/src/main/kotlin/com/hippo/ehviewer/EhDB.kt b/app/src/main/kotlin/com/hippo/ehviewer/EhDB.kt index 9112e4c2f..61b1b278d 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/EhDB.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/EhDB.kt @@ -46,6 +46,10 @@ object EhDB { runCatching { db.galleryDao().delete(galleryInfo) } } + suspend fun updateGalleryInfo(galleryInfoList: List) { + db.galleryDao().update(galleryInfoList) + } + fun getReadProgressFlow(gid: Long) = db.progressDao().getPageFlow(gid) suspend fun getReadProgress(gid: Long) = db.progressDao().getPage(gid) suspend fun putReadProgress(gid: Long, page: Int) = db.progressDao().upsert(ProgressInfo(gid, page)) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/client/data/BaseGalleryInfo.kt b/app/src/main/kotlin/com/hippo/ehviewer/client/data/BaseGalleryInfo.kt index 067f0f74a..fd71d2d65 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/client/data/BaseGalleryInfo.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/client/data/BaseGalleryInfo.kt @@ -65,7 +65,7 @@ data class BaseGalleryInfo( @Ignore override var simpleTags: ArrayList? = null, - @Ignore + @ColumnInfo(name = "PAGES", defaultValue = "0") override var pages: Int = 0, @Ignore diff --git a/app/src/main/kotlin/com/hippo/ehviewer/dao/Database.kt b/app/src/main/kotlin/com/hippo/ehviewer/dao/Database.kt index fa2b64b9b..820da6eb0 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/dao/Database.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/dao/Database.kt @@ -115,7 +115,7 @@ class DownloadsMigration : AutoMigrationSpec BaseGalleryInfo::class, DownloadLabel::class, DownloadEntity::class, DownloadDirname::class, Filter::class, HistoryInfo::class, LocalFavoriteInfo::class, ProgressInfo::class, QuickSearch::class, ], - version = 19, + version = 20, exportSchema = true, autoMigrations = [ AutoMigration( @@ -186,6 +186,10 @@ class DownloadsMigration : AutoMigrationSpec to = 19, spec = DownloadsMigration::class, ), + AutoMigration( + from = 19, + to = 20, + ), ], ) @TypeConverters(FilterModeConverter::class) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/dao/GalleryDao.kt b/app/src/main/kotlin/com/hippo/ehviewer/dao/GalleryDao.kt index 000eb7477..7ea16eaf0 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/dao/GalleryDao.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/dao/GalleryDao.kt @@ -23,6 +23,9 @@ interface GalleryDao { @Update suspend fun update(galleryInfo: BaseGalleryInfo) + @Update + suspend fun update(galleryInfoList: List) + @Upsert suspend fun upsert(galleryInfo: BaseGalleryInfo) diff --git a/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt b/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt index 2295a1b39..5103a1030 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/download/DownloadManager.kt @@ -24,6 +24,7 @@ import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.remember import androidx.compose.runtime.toMutableStateList +import arrow.fx.coroutines.parMapNotNull import com.google.android.material.math.MathUtils import com.hippo.ehviewer.EhDB import com.hippo.ehviewer.Settings @@ -31,9 +32,13 @@ import com.hippo.ehviewer.client.data.BaseGalleryInfo import com.hippo.ehviewer.dao.DownloadInfo import com.hippo.ehviewer.dao.DownloadLabel import com.hippo.ehviewer.image.Image +import com.hippo.ehviewer.spider.COMIC_INFO_FILE import com.hippo.ehviewer.spider.SpiderQueen +import com.hippo.ehviewer.spider.SpiderQueen.Companion.SPIDER_INFO_FILENAME import com.hippo.ehviewer.spider.SpiderQueen.OnSpiderListener import com.hippo.ehviewer.spider.putToDownloadDir +import com.hippo.ehviewer.spider.readComicInfo +import com.hippo.ehviewer.spider.readCompatFromUniFile import com.hippo.ehviewer.util.AppConfig import com.hippo.ehviewer.util.ConcurrentPool import com.hippo.ehviewer.util.SimpleHandler @@ -526,6 +531,23 @@ object DownloadManager : OnSpiderListener { } } + suspend fun readPagesFromLocal() { + val list = allInfoList.filter { it.pages == 0 }.parMapNotNull(concurrency = 5) { info -> + info.downloadDir?.run { + val pages = findFile(SPIDER_INFO_FILENAME)?.let { + readCompatFromUniFile(it)?.pages + } ?: findFile(COMIC_INFO_FILE)?.let { + readComicInfo(it)?.pageCount + } + pages?.let { + info.pages = it + info.galleryInfo + } + } + } + EhDB.updateGalleryInfo(list) + } + val isIdle: Boolean get() = mCurrentTask == null && mWaitList.isEmpty() diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryInfo.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryInfo.kt index 20c897c42..118d98f3b 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryInfo.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/main/GalleryInfo.kt @@ -11,7 +11,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.widthIn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Download import androidx.compose.material.icons.filled.Favorite @@ -195,6 +195,7 @@ fun GalleryInfoGridItem( onLongClick: () -> Unit, info: GalleryInfo, modifier: Modifier = Modifier, + badgeText: String? = info.simpleLanguage, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) = ElevatedCard( modifier = modifier, @@ -219,13 +220,12 @@ fun GalleryInfoGridItem( ) val categoryColor = EhUtils.getCategoryColor(info.category) Badge( - modifier = Modifier.align(Alignment.TopEnd).width(32.dp).height(24.dp), + modifier = Modifier.align(Alignment.TopEnd).widthIn(min = 32.dp).height(24.dp), containerColor = categoryColor, contentColor = if (Settings.harmonizeCategoryColor) contentColorFor(categoryColor) else EhUtils.categoryTextColor, ) { - val simpleLang = info.simpleLanguage - if (simpleLang != null) { - Text(text = simpleLang.uppercase()) + if (badgeText != null) { + Text(text = badgeText) } } } diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt index 95ef3632f..71c657dcf 100644 --- a/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt +++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/screen/DownloadsScreen.kt @@ -479,12 +479,13 @@ fun DownloadsScreen(navigator: DestinationsNavigator) { horizontalArrangement = Arrangement.spacedBy(gridInterval), contentPadding = realPadding, ) { - items(list) { + items(list) { info -> GalleryInfoGridItem( - onClick = ::onItemClick.partially1(it), - onLongClick = { navigator.navigate(it.galleryInfo.asDst()) }, - info = it, + onClick = ::onItemClick.partially1(info), + onLongClick = { navigator.navigate(info.galleryInfo.asDst()) }, + info = info, modifier = Modifier.animateItemPlacement(), + badgeText = info.pages.takeIf { it > 0 }?.toString(), ) } }