diff --git a/app/schemas/io.legado.app.data.AppDatabase/53.json b/app/schemas/io.legado.app.data.AppDatabase/53.json new file mode 100644 index 000000000..518a1bfb7 --- /dev/null +++ b/app/schemas/io.legado.app.data.AppDatabase/53.json @@ -0,0 +1,1692 @@ +{ + "formatVersion": 1, + "database": { + "version": 53, + "identityHash": "c55dac79aa9cc3dcdb72e91da4282005", + "entities": [ + { + "tableName": "books", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookUrl` TEXT NOT NULL DEFAULT '', `tocUrl` TEXT NOT NULL DEFAULT '', `origin` TEXT NOT NULL DEFAULT '', `originName` TEXT NOT NULL DEFAULT '', `name` TEXT NOT NULL DEFAULT '', `author` TEXT NOT NULL DEFAULT '', `kind` TEXT, `customTag` TEXT, `coverUrl` TEXT, `customCoverUrl` TEXT, `intro` TEXT, `customIntro` TEXT, `charset` TEXT, `type` INTEGER NOT NULL DEFAULT 0, `group` INTEGER NOT NULL DEFAULT 0, `latestChapterTitle` TEXT, `latestChapterTime` INTEGER NOT NULL DEFAULT 0, `lastCheckTime` INTEGER NOT NULL DEFAULT 0, `lastCheckCount` INTEGER NOT NULL DEFAULT 0, `totalChapterNum` INTEGER NOT NULL DEFAULT 0, `durChapterTitle` TEXT, `durChapterIndex` INTEGER NOT NULL DEFAULT 0, `durChapterPos` INTEGER NOT NULL DEFAULT 0, `durChapterTime` INTEGER NOT NULL DEFAULT 0, `wordCount` TEXT, `canUpdate` INTEGER NOT NULL DEFAULT 1, `order` INTEGER NOT NULL DEFAULT 0, `originOrder` INTEGER NOT NULL DEFAULT 0, `variable` TEXT, `readConfig` TEXT, PRIMARY KEY(`bookUrl`))", + "fields": [ + { + "fieldPath": "bookUrl", + "columnName": "bookUrl", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "tocUrl", + "columnName": "tocUrl", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "origin", + "columnName": "origin", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "originName", + "columnName": "originName", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "author", + "columnName": "author", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "kind", + "columnName": "kind", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customTag", + "columnName": "customTag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "coverUrl", + "columnName": "coverUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customCoverUrl", + "columnName": "customCoverUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "intro", + "columnName": "intro", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customIntro", + "columnName": "customIntro", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "charset", + "columnName": "charset", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "latestChapterTitle", + "columnName": "latestChapterTitle", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "latestChapterTime", + "columnName": "latestChapterTime", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "lastCheckTime", + "columnName": "lastCheckTime", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "lastCheckCount", + "columnName": "lastCheckCount", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "totalChapterNum", + "columnName": "totalChapterNum", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "durChapterTitle", + "columnName": "durChapterTitle", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "durChapterIndex", + "columnName": "durChapterIndex", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "durChapterPos", + "columnName": "durChapterPos", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "durChapterTime", + "columnName": "durChapterTime", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "wordCount", + "columnName": "wordCount", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "canUpdate", + "columnName": "canUpdate", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "originOrder", + "columnName": "originOrder", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "variable", + "columnName": "variable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "readConfig", + "columnName": "readConfig", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "bookUrl" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_books_name_author", + "unique": true, + "columnNames": [ + "name", + "author" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_books_name_author` ON `${TABLE_NAME}` (`name`, `author`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "book_groups", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`groupId` INTEGER NOT NULL, `groupName` TEXT NOT NULL, `cover` TEXT, `order` INTEGER NOT NULL, `show` INTEGER NOT NULL, PRIMARY KEY(`groupId`))", + "fields": [ + { + "fieldPath": "groupId", + "columnName": "groupId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "groupName", + "columnName": "groupName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cover", + "columnName": "cover", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "show", + "columnName": "show", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "groupId" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "book_sources", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookSourceUrl` TEXT NOT NULL, `bookSourceName` TEXT NOT NULL, `bookSourceGroup` TEXT, `bookSourceType` INTEGER NOT NULL, `bookUrlPattern` TEXT, `customOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `enabledExplore` INTEGER NOT NULL, `enabledReview` INTEGER, `enabledCookieJar` INTEGER DEFAULT 0, `concurrentRate` TEXT, `header` TEXT, `loginUrl` TEXT, `loginUi` TEXT, `loginCheckJs` TEXT, `bookSourceComment` TEXT, `variableComment` TEXT, `lastUpdateTime` INTEGER NOT NULL, `respondTime` INTEGER NOT NULL, `weight` INTEGER NOT NULL, `exploreUrl` TEXT, `ruleExplore` TEXT, `searchUrl` TEXT, `ruleSearch` TEXT, `ruleBookInfo` TEXT, `ruleToc` TEXT, `ruleContent` TEXT, `ruleReview` TEXT, PRIMARY KEY(`bookSourceUrl`))", + "fields": [ + { + "fieldPath": "bookSourceUrl", + "columnName": "bookSourceUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bookSourceName", + "columnName": "bookSourceName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bookSourceGroup", + "columnName": "bookSourceGroup", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bookSourceType", + "columnName": "bookSourceType", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "bookUrlPattern", + "columnName": "bookUrlPattern", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "customOrder", + "columnName": "customOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "enabledExplore", + "columnName": "enabledExplore", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "enabledReview", + "columnName": "enabledReview", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enabledCookieJar", + "columnName": "enabledCookieJar", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "concurrentRate", + "columnName": "concurrentRate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "header", + "columnName": "header", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loginUrl", + "columnName": "loginUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loginUi", + "columnName": "loginUi", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loginCheckJs", + "columnName": "loginCheckJs", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bookSourceComment", + "columnName": "bookSourceComment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "variableComment", + "columnName": "variableComment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastUpdateTime", + "columnName": "lastUpdateTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "respondTime", + "columnName": "respondTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "exploreUrl", + "columnName": "exploreUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleExplore", + "columnName": "ruleExplore", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchUrl", + "columnName": "searchUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleSearch", + "columnName": "ruleSearch", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleBookInfo", + "columnName": "ruleBookInfo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleToc", + "columnName": "ruleToc", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleContent", + "columnName": "ruleContent", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleReview", + "columnName": "ruleReview", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "bookSourceUrl" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_book_sources_bookSourceUrl", + "unique": false, + "columnNames": [ + "bookSourceUrl" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_book_sources_bookSourceUrl` ON `${TABLE_NAME}` (`bookSourceUrl`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "chapters", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `isVolume` INTEGER NOT NULL, `baseUrl` TEXT NOT NULL, `bookUrl` TEXT NOT NULL, `index` INTEGER NOT NULL, `isVip` INTEGER NOT NULL, `isPay` INTEGER NOT NULL, `resourceUrl` TEXT, `tag` TEXT, `start` INTEGER, `end` INTEGER, `startFragmentId` TEXT, `endFragmentId` TEXT, `variable` TEXT, PRIMARY KEY(`url`, `bookUrl`), FOREIGN KEY(`bookUrl`) REFERENCES `books`(`bookUrl`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isVolume", + "columnName": "isVolume", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "baseUrl", + "columnName": "baseUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bookUrl", + "columnName": "bookUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "index", + "columnName": "index", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isVip", + "columnName": "isVip", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isPay", + "columnName": "isPay", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "resourceUrl", + "columnName": "resourceUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tag", + "columnName": "tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "start", + "columnName": "start", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "end", + "columnName": "end", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "startFragmentId", + "columnName": "startFragmentId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "endFragmentId", + "columnName": "endFragmentId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "variable", + "columnName": "variable", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "url", + "bookUrl" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_chapters_bookUrl", + "unique": false, + "columnNames": [ + "bookUrl" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_chapters_bookUrl` ON `${TABLE_NAME}` (`bookUrl`)" + }, + { + "name": "index_chapters_bookUrl_index", + "unique": true, + "columnNames": [ + "bookUrl", + "index" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_chapters_bookUrl_index` ON `${TABLE_NAME}` (`bookUrl`, `index`)" + } + ], + "foreignKeys": [ + { + "table": "books", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "bookUrl" + ], + "referencedColumns": [ + "bookUrl" + ] + } + ] + }, + { + "tableName": "replace_rules", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL DEFAULT '', `group` TEXT, `pattern` TEXT NOT NULL DEFAULT '', `replacement` TEXT NOT NULL DEFAULT '', `scope` TEXT, `scopeTitle` INTEGER NOT NULL DEFAULT 0, `scopeContent` INTEGER NOT NULL DEFAULT 1, `isEnabled` INTEGER NOT NULL DEFAULT 1, `isRegex` INTEGER NOT NULL DEFAULT 1, `timeoutMillisecond` INTEGER NOT NULL DEFAULT 3000, `sortOrder` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "group", + "columnName": "group", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pattern", + "columnName": "pattern", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "replacement", + "columnName": "replacement", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "scope", + "columnName": "scope", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "scopeTitle", + "columnName": "scopeTitle", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "scopeContent", + "columnName": "scopeContent", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "isRegex", + "columnName": "isRegex", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + }, + { + "fieldPath": "timeoutMillisecond", + "columnName": "timeoutMillisecond", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "3000" + }, + { + "fieldPath": "order", + "columnName": "sortOrder", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_replace_rules_id", + "unique": false, + "columnNames": [ + "id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_replace_rules_id` ON `${TABLE_NAME}` (`id`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "searchBooks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bookUrl` TEXT NOT NULL, `origin` TEXT NOT NULL, `originName` TEXT NOT NULL, `type` INTEGER NOT NULL, `name` TEXT NOT NULL, `author` TEXT NOT NULL, `kind` TEXT, `coverUrl` TEXT, `intro` TEXT, `wordCount` TEXT, `latestChapterTitle` TEXT, `tocUrl` TEXT NOT NULL, `time` INTEGER NOT NULL, `variable` TEXT, `originOrder` INTEGER NOT NULL, PRIMARY KEY(`bookUrl`), FOREIGN KEY(`origin`) REFERENCES `book_sources`(`bookSourceUrl`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "bookUrl", + "columnName": "bookUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "origin", + "columnName": "origin", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "originName", + "columnName": "originName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "author", + "columnName": "author", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "kind", + "columnName": "kind", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "coverUrl", + "columnName": "coverUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "intro", + "columnName": "intro", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "wordCount", + "columnName": "wordCount", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "latestChapterTitle", + "columnName": "latestChapterTitle", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "tocUrl", + "columnName": "tocUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "variable", + "columnName": "variable", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "originOrder", + "columnName": "originOrder", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "bookUrl" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_searchBooks_bookUrl", + "unique": true, + "columnNames": [ + "bookUrl" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_searchBooks_bookUrl` ON `${TABLE_NAME}` (`bookUrl`)" + }, + { + "name": "index_searchBooks_origin", + "unique": false, + "columnNames": [ + "origin" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_searchBooks_origin` ON `${TABLE_NAME}` (`origin`)" + } + ], + "foreignKeys": [ + { + "table": "book_sources", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "origin" + ], + "referencedColumns": [ + "bookSourceUrl" + ] + } + ] + }, + { + "tableName": "search_keywords", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`word` TEXT NOT NULL, `usage` INTEGER NOT NULL, `lastUseTime` INTEGER NOT NULL, PRIMARY KEY(`word`))", + "fields": [ + { + "fieldPath": "word", + "columnName": "word", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "usage", + "columnName": "usage", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastUseTime", + "columnName": "lastUseTime", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "word" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_search_keywords_word", + "unique": true, + "columnNames": [ + "word" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_search_keywords_word` ON `${TABLE_NAME}` (`word`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "cookies", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `cookie` TEXT NOT NULL, PRIMARY KEY(`url`))", + "fields": [ + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cookie", + "columnName": "cookie", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "url" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_cookies_url", + "unique": true, + "columnNames": [ + "url" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_cookies_url` ON `${TABLE_NAME}` (`url`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "rssSources", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sourceUrl` TEXT NOT NULL, `sourceName` TEXT NOT NULL, `sourceIcon` TEXT NOT NULL, `sourceGroup` TEXT, `sourceComment` TEXT, `enabled` INTEGER NOT NULL, `variableComment` TEXT, `enabledCookieJar` INTEGER DEFAULT 0, `concurrentRate` TEXT, `header` TEXT, `loginUrl` TEXT, `loginUi` TEXT, `loginCheckJs` TEXT, `sortUrl` TEXT, `singleUrl` INTEGER NOT NULL, `articleStyle` INTEGER NOT NULL, `ruleArticles` TEXT, `ruleNextPage` TEXT, `ruleTitle` TEXT, `rulePubDate` TEXT, `ruleDescription` TEXT, `ruleImage` TEXT, `ruleLink` TEXT, `ruleContent` TEXT, `style` TEXT, `enableJs` INTEGER NOT NULL, `loadWithBaseUrl` INTEGER NOT NULL, `customOrder` INTEGER NOT NULL, PRIMARY KEY(`sourceUrl`))", + "fields": [ + { + "fieldPath": "sourceUrl", + "columnName": "sourceUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sourceName", + "columnName": "sourceName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sourceIcon", + "columnName": "sourceIcon", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sourceGroup", + "columnName": "sourceGroup", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sourceComment", + "columnName": "sourceComment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "variableComment", + "columnName": "variableComment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "enabledCookieJar", + "columnName": "enabledCookieJar", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "concurrentRate", + "columnName": "concurrentRate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "header", + "columnName": "header", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loginUrl", + "columnName": "loginUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loginUi", + "columnName": "loginUi", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loginCheckJs", + "columnName": "loginCheckJs", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "sortUrl", + "columnName": "sortUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "singleUrl", + "columnName": "singleUrl", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "articleStyle", + "columnName": "articleStyle", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ruleArticles", + "columnName": "ruleArticles", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleNextPage", + "columnName": "ruleNextPage", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleTitle", + "columnName": "ruleTitle", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "rulePubDate", + "columnName": "rulePubDate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleDescription", + "columnName": "ruleDescription", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleImage", + "columnName": "ruleImage", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleLink", + "columnName": "ruleLink", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ruleContent", + "columnName": "ruleContent", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "style", + "columnName": "style", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "enableJs", + "columnName": "enableJs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "loadWithBaseUrl", + "columnName": "loadWithBaseUrl", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "customOrder", + "columnName": "customOrder", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "sourceUrl" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_rssSources_sourceUrl", + "unique": false, + "columnNames": [ + "sourceUrl" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_rssSources_sourceUrl` ON `${TABLE_NAME}` (`sourceUrl`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "bookmarks", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`time` INTEGER NOT NULL, `bookName` TEXT NOT NULL, `bookAuthor` TEXT NOT NULL, `chapterIndex` INTEGER NOT NULL, `chapterPos` INTEGER NOT NULL, `chapterName` TEXT NOT NULL, `bookText` TEXT NOT NULL, `content` TEXT NOT NULL, PRIMARY KEY(`time`))", + "fields": [ + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "bookName", + "columnName": "bookName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bookAuthor", + "columnName": "bookAuthor", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "chapterIndex", + "columnName": "chapterIndex", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chapterPos", + "columnName": "chapterPos", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "chapterName", + "columnName": "chapterName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bookText", + "columnName": "bookText", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "time" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_bookmarks_bookName_bookAuthor", + "unique": false, + "columnNames": [ + "bookName", + "bookAuthor" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_bookmarks_bookName_bookAuthor` ON `${TABLE_NAME}` (`bookName`, `bookAuthor`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "rssArticles", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`origin` TEXT NOT NULL, `sort` TEXT NOT NULL, `title` TEXT NOT NULL, `order` INTEGER NOT NULL, `link` TEXT NOT NULL, `pubDate` TEXT, `description` TEXT, `content` TEXT, `image` TEXT, `read` INTEGER NOT NULL, `variable` TEXT, PRIMARY KEY(`origin`, `link`))", + "fields": [ + { + "fieldPath": "origin", + "columnName": "origin", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sort", + "columnName": "sort", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "order", + "columnName": "order", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pubDate", + "columnName": "pubDate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "read", + "columnName": "read", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "variable", + "columnName": "variable", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "origin", + "link" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "rssReadRecords", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`record` TEXT NOT NULL, `read` INTEGER NOT NULL, PRIMARY KEY(`record`))", + "fields": [ + { + "fieldPath": "record", + "columnName": "record", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "read", + "columnName": "read", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "record" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "rssStars", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`origin` TEXT NOT NULL, `sort` TEXT NOT NULL, `title` TEXT NOT NULL, `starTime` INTEGER NOT NULL, `link` TEXT NOT NULL, `pubDate` TEXT, `description` TEXT, `content` TEXT, `image` TEXT, `variable` TEXT, PRIMARY KEY(`origin`, `link`))", + "fields": [ + { + "fieldPath": "origin", + "columnName": "origin", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sort", + "columnName": "sort", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "starTime", + "columnName": "starTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pubDate", + "columnName": "pubDate", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "content", + "columnName": "content", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "image", + "columnName": "image", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "variable", + "columnName": "variable", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "origin", + "link" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "txtTocRules", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `rule` TEXT NOT NULL, `example` TEXT, `serialNumber` INTEGER NOT NULL, `enable` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "rule", + "columnName": "rule", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "example", + "columnName": "example", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "serialNumber", + "columnName": "serialNumber", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "enable", + "columnName": "enable", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "readRecord", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL DEFAULT 0, `lastRead` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`deviceId`, `bookName`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bookName", + "columnName": "bookName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "readTime", + "columnName": "readTime", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "lastRead", + "columnName": "lastRead", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "deviceId", + "bookName" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "httpTTS", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `contentType` TEXT, `concurrentRate` TEXT DEFAULT '0', `loginUrl` TEXT, `loginUi` TEXT, `header` TEXT, `enabledCookieJar` INTEGER DEFAULT 0, `loginCheckJs` TEXT, `lastUpdateTime` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "contentType", + "columnName": "contentType", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "concurrentRate", + "columnName": "concurrentRate", + "affinity": "TEXT", + "notNull": false, + "defaultValue": "'0'" + }, + { + "fieldPath": "loginUrl", + "columnName": "loginUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "loginUi", + "columnName": "loginUi", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "header", + "columnName": "header", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "enabledCookieJar", + "columnName": "enabledCookieJar", + "affinity": "INTEGER", + "notNull": false, + "defaultValue": "0" + }, + { + "fieldPath": "loginCheckJs", + "columnName": "loginCheckJs", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lastUpdateTime", + "columnName": "lastUpdateTime", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "caches", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` TEXT, `deadline` INTEGER NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "deadline", + "columnName": "deadline", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [ + { + "name": "index_caches_key", + "unique": true, + "columnNames": [ + "key" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_caches_key` ON `${TABLE_NAME}` (`key`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "ruleSubs", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `type` INTEGER NOT NULL, `customOrder` INTEGER NOT NULL, `autoUpdate` INTEGER NOT NULL, `update` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "customOrder", + "columnName": "customOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "autoUpdate", + "columnName": "autoUpdate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "update", + "columnName": "update", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "keyboardAssists", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` INTEGER NOT NULL DEFAULT 0, `key` TEXT NOT NULL DEFAULT '', `value` TEXT NOT NULL DEFAULT '', `serialNo` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`type`, `key`))", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" + }, + { + "fieldPath": "serialNo", + "columnName": "serialNo", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "columnNames": [ + "type", + "key" + ], + "autoGenerate": false + }, + "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, 'c55dac79aa9cc3dcdb72e91da4282005')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md index 634f47a01..bf011d6b2 100644 --- a/app/src/main/assets/updateLog.md +++ b/app/src/main/assets/updateLog.md @@ -11,11 +11,11 @@ * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。 * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! -**2022/09/03** +**2022/09/07** * 更新cronet: 105.0.5195.79 -* 更新cronet: 105.0.5195.77 * 更新web端书源编辑 by jgckM +* 修复仿真翻页点击时翻页动画异常,化仿真翻页点击翻页效果 by 821938089 **2022/08/31** diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt index a83c7ae9f..095e4c6b3 100644 --- a/app/src/main/java/io/legado/app/constant/PreferKey.kt +++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt @@ -32,6 +32,7 @@ object PreferKey { const val prevKeys = "prevKeyCodes" const val nextKeys = "nextKeyCodes" const val showDiscovery = "showDiscovery" + const val enableReview = "enableReview" const val showRss = "showRss" const val bookshelfLayout = "bookshelfLayout" const val bookshelfSort = "bookshelfSort" diff --git a/app/src/main/java/io/legado/app/data/AppDatabase.kt b/app/src/main/java/io/legado/app/data/AppDatabase.kt index 64624717a..270614a1e 100644 --- a/app/src/main/java/io/legado/app/data/AppDatabase.kt +++ b/app/src/main/java/io/legado/app/data/AppDatabase.kt @@ -20,7 +20,7 @@ val appDb by lazy { } @Database( - version = 52, + version = 53, exportSchema = true, entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class, ReplaceRule::class, SearchBook::class, SearchKeyword::class, Cookie::class, @@ -36,7 +36,8 @@ val appDb by lazy { AutoMigration(from = 48, to = 49), AutoMigration(from = 49, to = 50), AutoMigration(from = 50, to = 51), - AutoMigration(from = 51, to = 52) + AutoMigration(from = 51, to = 52), + AutoMigration(from = 52, to = 53) ] ) abstract class AppDatabase : RoomDatabase() { diff --git a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt index f98063115..c9aff1b65 100644 --- a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt @@ -50,6 +50,9 @@ interface BookSourceDao { @Query("select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' order by customOrder asc") fun flowExplore(): Flow> +// @Query("select * from book_sources where enabledReview = 1 order by customOrder asc") +// fun flowReview(): Flow> + @Query("select * from book_sources where loginUrl is not null and loginUrl != ''") fun flowLogin(): Flow> diff --git a/app/src/main/java/io/legado/app/data/entities/BookChapterReview.kt b/app/src/main/java/io/legado/app/data/entities/BookChapterReview.kt new file mode 100644 index 000000000..64a838f42 --- /dev/null +++ b/app/src/main/java/io/legado/app/data/entities/BookChapterReview.kt @@ -0,0 +1,15 @@ +package io.legado.app.data.entities + +import android.os.Parcelable +import androidx.room.ColumnInfo +import kotlinx.parcelize.Parcelize + +@Parcelize +class BookChapterReview( + @ColumnInfo(defaultValue = "0") + var bookId: Long = 0, + var chapterId: Long = 0, + var summaryUrl: String = "", +): Parcelable { + +} diff --git a/app/src/main/java/io/legado/app/data/entities/BookSource.kt b/app/src/main/java/io/legado/app/data/entities/BookSource.kt index 9f27a31a7..0184b8688 100644 --- a/app/src/main/java/io/legado/app/data/entities/BookSource.kt +++ b/app/src/main/java/io/legado/app/data/entities/BookSource.kt @@ -37,7 +37,9 @@ data class BookSource( // 是否启用 var enabled: Boolean = true, // 启用发现 - var enabledExplore: Boolean = true, + var enabledExplore: Boolean = false, + // 启用段评 + var enabledReview: Boolean? = false, // 启用okhttp CookieJAr 自动保存每次请求的cookie @ColumnInfo(defaultValue = "0") override var enabledCookieJar: Boolean? = false, @@ -74,7 +76,9 @@ data class BookSource( // 目录页规则 var ruleToc: TocRule? = null, // 正文页规则 - var ruleContent: ContentRule? = null + var ruleContent: ContentRule? = null, + // 段评规则 + var ruleReview: ReviewRule? = null ) : Parcelable, BaseSource { override fun getTag(): String { @@ -170,6 +174,13 @@ data class BookSource( return rule } + fun getReviewRule(): ReviewRule { + ruleReview?.let { return it } + val rule = ReviewRule() + ruleReview = rule + return rule + } + fun getDisPlayNameGroup(): String { return if (bookSourceGroup.isNullOrBlank()) { bookSourceName @@ -303,5 +314,13 @@ data class BookSource( fun stringToContentRule(json: String?) = GSON.fromJsonObject(json).getOrNull() + @TypeConverter + fun stringToReviewRule(json: String?) = + GSON.fromJsonObject(json).getOrNull() + + @TypeConverter + fun reviewRuleToString(reviewRule: ReviewRule?): String = + GSON.toJson(reviewRule) + } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/data/entities/rule/ReviewRule.kt b/app/src/main/java/io/legado/app/data/entities/rule/ReviewRule.kt new file mode 100644 index 000000000..07ff9fb32 --- /dev/null +++ b/app/src/main/java/io/legado/app/data/entities/rule/ReviewRule.kt @@ -0,0 +1,20 @@ +package io.legado.app.data.entities.rule + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class ReviewRule( + var reviewUrl: String? = null, // 段评URL + var avatarRule: String? = null, // 段评发布者头像 + var contentRule: String? = null, // 段评内容 + var postTimeRule: String? = null, // 段评发布时间 + var reviewQuoteUrl: String? = null, // 获取段评回复URL + + // 这些功能将在以上功能完成以后实现 + var voteUpUrl: String? = null, // 点赞URL + var voteDownUrl: String? = null, // 点踩URL + var postReviewUrl: String? = null, // 发送回复URL + var postQuoteUrl: String? = null, // 发送回复段评URL + var deleteUrl: String? = null, // 删除段评URL +): Parcelable diff --git a/app/src/main/java/io/legado/app/help/JsExtensions.kt b/app/src/main/java/io/legado/app/help/JsExtensions.kt index 8280c8e6b..16c9dc14e 100644 --- a/app/src/main/java/io/legado/app/help/JsExtensions.kt +++ b/app/src/main/java/io/legado/app/help/JsExtensions.kt @@ -27,6 +27,7 @@ import splitties.init.appCtx import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.File +import java.io.FileOutputStream import java.net.URLEncoder import java.nio.charset.Charset import java.text.SimpleDateFormat @@ -125,7 +126,7 @@ interface JsExtensions { html = html, javaScript = js, headerMap = getSource()?.getHeaderMap(true), - tag = getSource()?.getKey() + tag = getSource()?.getKey() ).getStrResponse().body } } @@ -143,15 +144,18 @@ interface JsExtensions { * 使用内置浏览器打开链接,并等待网页结果 */ fun startBrowserAwait(url: String, title: String): StrResponse { - return StrResponse(url, SourceVerificationHelp.getVerificationResult(getSource(), url, title, true)) + return StrResponse( + url, + SourceVerificationHelp.getVerificationResult(getSource(), url, title, true) + ) } /** * 打开图片验证码对话框,等待返回验证结果 */ - fun getVerificationCode(imageUrl: String): String { - return SourceVerificationHelp.getVerificationResult(getSource(), imageUrl, "", false) - } + fun getVerificationCode(imageUrl: String): String { + return SourceVerificationHelp.getVerificationResult(getSource(), imageUrl, "", false) + } /** * 可从网络,本地文件(阅读私有缓存目录和书籍保存位置支持相对路径)导入JavaScript脚本 @@ -203,6 +207,29 @@ interface JsExtensions { } } + /** + * 下载文件 + * @param url 下载地址:可带参数type,文件后缀,不带默认zip + * @return 下载的文件相对路径 + */ + fun downloadFile(url: String): String { + val analyzeUrl = AnalyzeUrl(url, source = getSource()) + val type = analyzeUrl.type ?: "zip" + val path = FileUtils.getPath( + FileUtils.createFolderIfNotExist(FileUtils.getCachePath()), + "${MD5Utils.md5Encode16(url)}.${type}" + ) + FileUtils.delete(path) + analyzeUrl.getInputStream().use { iStream -> + val file = FileUtils.createFileIfNotExist(path) + FileOutputStream(file).use { oStream -> + iStream.copyTo(oStream) + } + } + return path.substring(FileUtils.getCachePath().length) + } + + /** * 实现16进制字符串转文件 * @param content 需要转成文件的16进制字符串 @@ -211,18 +238,18 @@ interface JsExtensions { */ fun downloadFile(content: String, url: String): String { val type = AnalyzeUrl(url, source = getSource()).type ?: return "" - val zipPath = FileUtils.getPath( + val path = FileUtils.getPath( FileUtils.createFolderIfNotExist(FileUtils.getCachePath()), "${MD5Utils.md5Encode16(url)}.${type}" ) - FileUtils.delete(zipPath) - val zipFile = FileUtils.createFileIfNotExist(zipPath) + FileUtils.delete(path) + val file = FileUtils.createFileIfNotExist(path) StringUtils.hexStringToByte(content).let { if (it.isNotEmpty()) { - zipFile.writeBytes(it) + file.writeBytes(it) } } - return zipPath.substring(FileUtils.getCachePath().length) + return path.substring(FileUtils.getCachePath().length) } /** @@ -433,13 +460,15 @@ interface JsExtensions { } /** - * js实现文件夹内所有文件读取 + * js实现文件夹内所有文本文件读取 + * @param path 文件夹相对路径 + * @return 所有文件字符串换行连接 */ - fun getTxtInFolder(unzipPath: String): String { - if (unzipPath.isEmpty()) return "" - val unzipFolder = getFile(unzipPath) + fun getTxtInFolder(path: String): String { + if (path.isEmpty()) return "" + val folder = getFile(path) val contents = StringBuilder() - unzipFolder.listFiles().let { + folder.listFiles().let { if (it != null) { for (f in it) { val charsetName = EncodingDetect.getEncode(f) @@ -449,7 +478,7 @@ interface JsExtensions { contents.deleteCharAt(contents.length - 1) } } - FileUtils.delete(unzipFolder.absolutePath) + FileUtils.delete(folder.absolutePath) return contents.toString() } @@ -833,7 +862,7 @@ interface JsExtensions { } fun desBase64DecodeToString( - data: String, key: String, transformation: String, iv: String + data: String, key: String, transformation: String, iv: String ): String? { return EncoderUtils.decryptBase64DES( data.encodeToByteArray(), @@ -1028,7 +1057,10 @@ interface JsExtensions { algorithm: String, key: String ): String { - return Base64.encodeToString(HMac(algorithm, key.toByteArray()).digest(data), Base64.NO_WRAP) + return Base64.encodeToString( + HMac(algorithm, key.toByteArray()).digest(data), + Base64.NO_WRAP + ) } fun md5Encode(str: String): String { diff --git a/app/src/main/java/io/legado/app/help/SourceAnalyzer.kt b/app/src/main/java/io/legado/app/help/SourceAnalyzer.kt index 55fcdc40d..25b80b893 100644 --- a/app/src/main/java/io/legado/app/help/SourceAnalyzer.kt +++ b/app/src/main/java/io/legado/app/help/SourceAnalyzer.kt @@ -151,6 +151,7 @@ object SourceAnalyzer { source.enabled = sourceAny.enabled source.enabledExplore = sourceAny.enabledExplore source.enabledCookieJar = sourceAny.enabledCookieJar + source.enabledReview = sourceAny.enabledReview source.concurrentRate = sourceAny.concurrentRate source.header = sourceAny.header source.loginUrl = when (sourceAny.loginUrl) { @@ -206,6 +207,13 @@ object SourceAnalyzer { GSON.fromJsonObject(GSON.toJson(sourceAny.ruleContent)) .getOrNull() } + source.ruleReview = if (sourceAny.ruleReview is String) { + GSON.fromJsonObject(sourceAny.ruleReview.toString()) + .getOrNull() + } else { + GSON.fromJsonObject(GSON.toJson(sourceAny.ruleReview)) + .getOrNull() + } } source } @@ -221,6 +229,7 @@ object SourceAnalyzer { var customOrder: Int = 0, // 手动排序编号 var enabled: Boolean = true, // 是否启用 var enabledExplore: Boolean = true, // 启用发现 + var enabledReview: Boolean = false, // 启用段评 var enabledCookieJar: Boolean = false, // 启用CookieJar var concurrentRate: String? = null, // 并发率 var header: String? = null, // 请求头 @@ -238,7 +247,8 @@ object SourceAnalyzer { var ruleSearch: Any? = null, // 搜索规则 var ruleBookInfo: Any? = null, // 书籍信息页规则 var ruleToc: Any? = null, // 目录页规则 - var ruleContent: Any? = null // 正文页规则 + var ruleContent: Any? = null, // 正文页规则 + var ruleReview: Any? = null // 段评规则 ) // default规则适配 diff --git a/app/src/main/java/io/legado/app/help/config/AppConfig.kt b/app/src/main/java/io/legado/app/help/config/AppConfig.kt index b339af4aa..ee57a52d7 100644 --- a/app/src/main/java/io/legado/app/help/config/AppConfig.kt +++ b/app/src/main/java/io/legado/app/help/config/AppConfig.kt @@ -154,6 +154,12 @@ object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { val autoRefreshBook: Boolean get() = appCtx.getPrefBoolean(PreferKey.autoRefresh) + var enableReview: Boolean + get() = appCtx.getPrefBoolean(PreferKey.enableReview, false) + set(value) { + appCtx.putPrefBoolean(PreferKey.enableReview, value) + } + var threadCount: Int get() = appCtx.getPrefInt(PreferKey.threadCount, 16) set(value) { diff --git a/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt index 605070c0e..4b500e078 100644 --- a/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt +++ b/app/src/main/java/io/legado/app/help/config/ReadBookConfig.kt @@ -449,7 +449,7 @@ object ReadBookConfig { var letterSpacing: Float = 0.1f,//字间距 var lineSpacingExtra: Int = 12,//行间距 var paragraphSpacing: Int = 2,//段距 - var titleMode: Int = 0,//标题居中 + var titleMode: Int = 0,//标题位置 0:居左 1:居中 2:隐藏 var titleSize: Int = 0, var titleTopSpacing: Int = 0, var titleBottomSpacing: Int = 0, diff --git a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt index 7d37300a1..f80a4e0a7 100644 --- a/app/src/main/java/io/legado/app/model/webBook/WebBook.kt +++ b/app/src/main/java/io/legado/app/model/webBook/WebBook.kt @@ -300,6 +300,13 @@ object WebBook { } } + /** + * 获取段评 + */ + fun getReview() { + // TODO + } + /** * 精准搜索 */ diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt index fe9a6ded7..e2c97dd09 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt @@ -256,6 +256,7 @@ class ReadBookActivity : BaseReadBookActivity(), else -> when (item.itemId) { R.id.menu_enable_replace -> item.isChecked = book.getUseReplaceRule() R.id.menu_re_segment -> item.isChecked = book.getReSegment() + R.id.menu_enable_review -> item.isChecked = AppConfig.enableReview R.id.menu_reverse_content -> item.isVisible = onLine } } @@ -356,6 +357,11 @@ class ReadBookActivity : BaseReadBookActivity(), menu?.findItem(R.id.menu_re_segment)?.isChecked = it.getReSegment() ReadBook.loadContent(false) } + R.id.menu_enable_review -> { + AppConfig.enableReview = !AppConfig.enableReview + menu?.findItem(R.id.menu_enable_review)?.isChecked = AppConfig.enableReview + ReadBook.loadContent(false) + } R.id.menu_page_anim -> showPageAnimConfig { binding.readView.upPageAnim() ReadBook.loadContent(false) diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt index 232e90959..27642ab6a 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt @@ -4,6 +4,8 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Paint import android.graphics.RectF +import android.text.StaticLayout +import android.text.TextPaint import android.util.AttributeSet import android.view.View import io.legado.app.R @@ -14,10 +16,7 @@ import io.legado.app.help.config.AppConfig import io.legado.app.help.config.ReadBookConfig import io.legado.app.lib.theme.accentColor import io.legado.app.model.ReadBook -import io.legado.app.ui.book.read.page.entities.TextChar -import io.legado.app.ui.book.read.page.entities.TextLine -import io.legado.app.ui.book.read.page.entities.TextPage -import io.legado.app.ui.book.read.page.entities.TextPos +import io.legado.app.ui.book.read.page.entities.* import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ImageProvider import io.legado.app.ui.book.read.page.provider.TextPageFactory @@ -28,7 +27,7 @@ import io.legado.app.utils.toastOnUi import kotlin.math.min /** - * 阅读内容界面 + * 阅读内容视图 */ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) { var selectAble = context.getPrefBoolean(PreferKey.textSelectAble, true) @@ -65,12 +64,18 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at callBack = activity as CallBack } + /** + * 设置内容 + */ fun setContent(textPage: TextPage) { this.textPage = textPage imagePaint.isAntiAlias = AppConfig.useAntiAlias invalidate() } + /** + * 更新绘制区域 + */ fun upVisibleRect() { visibleRect.set( ChapterProvider.paddingLeft.toFloat(), @@ -121,6 +126,9 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at } } + /** + * 绘制页面 + */ private fun draw( canvas: Canvas, textPage: TextPage, @@ -150,15 +158,90 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at ChapterProvider.contentPaint } val textColor = if (textLine.isReadAloud) context.accentColor else ReadBookConfig.textColor + val linePaint = Paint() + linePaint.strokeWidth = textPaint.textSize / 21 + linePaint.color = textColor + val reviewCountPaint = TextPaint() + reviewCountPaint.textSize = textPaint.textSize * 0.6F + reviewCountPaint.color = textColor textLine.textChars.forEach { - if (it.isImage) { - drawImage(canvas, textPage, textLine, it, lineTop, lineBottom) - } else { - textPaint.color = textColor - if (it.isSearchResult) { - textPaint.color = context.accentColor + when (it.style) { + 0 -> { + textPaint.color = textColor + if (it.isSearchResult) { + textPaint.color = context.accentColor + } + canvas.drawText(it.charData, it.start, lineBase, textPaint) + } + 1 -> drawImage(canvas, textPage, textLine, it, lineTop, lineBottom) + 2 -> { + if (textLine.reviewCount <= 0) return@forEach + canvas.drawLine( + it.start, + lineBase - textPaint.textSize * 2 / 5, + it.start + textPaint.textSize / 6, + lineBase - textPaint.textSize / 4, + linePaint + ) + canvas.drawLine( + it.start, + lineBase - textPaint.textSize * 0.38F, + it.start + textPaint.textSize / 6, + lineBase - textPaint.textSize * 0.55F, + linePaint + ) + canvas.drawLine( + it.start + textPaint.textSize / 6, + lineBase - textPaint.textSize / 4, + it.start + textPaint.textSize / 6, + lineBase, + linePaint + ) + canvas.drawLine( + it.start + textPaint.textSize / 6, + lineBase - textPaint.textSize * 0.55F, + it.start + textPaint.textSize / 6, + lineBase - textPaint.textSize * 0.8F, + linePaint + ) + canvas.drawLine( + it.start + textPaint.textSize / 6, + lineBase, + it.start + textPaint.textSize * 1.6F, + lineBase, + linePaint + ) + canvas.drawLine( + it.start + textPaint.textSize / 6, + lineBase - textPaint.textSize * 0.8F, + it.start + textPaint.textSize * 1.6F, + lineBase - textPaint.textSize * 0.8F, + linePaint + ) + canvas.drawLine( + it.start + textPaint.textSize * 1.6F, + lineBase - textPaint.textSize * 0.8F, + it.start + textPaint.textSize * 1.6F, + lineBase, + linePaint + ) + if (textLine.reviewCount < 100) canvas.drawText( + textLine.reviewCount.toString(), + it.start + textPaint.textSize * 0.87F - + StaticLayout.getDesiredWidth( + textLine.reviewCount.toString(), + reviewCountPaint + ) / 2, + lineBase - textPaint.textSize / 6, + reviewCountPaint + ) + else canvas.drawText( + "99+", + it.start + textPaint.textSize * 0.35F, + lineBase - textPaint.textSize / 6, + reviewCountPaint + ) } - canvas.drawText(it.charData, it.start, lineBase, textPaint) } if (it.selected) { canvas.drawRect(it.start, lineTop, it.end, lineBottom, selectedPaint) @@ -174,7 +257,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at canvas: Canvas, textPage: TextPage, textLine: TextLine, - textChar: TextChar, + textChar: TextColumn, lineTop: Float, lineBottom: Float ) { @@ -259,6 +342,9 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at invalidate() } + /** + * 重置滚动位置 + */ fun resetPageOffset() { pageOffset = 0 } @@ -269,18 +355,34 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at fun longPress( x: Float, y: Float, - select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit, + select: (textPos: TextPos) -> Unit, ) { - touch(x, y) { _, relativePos, _, lineIndex, _, charIndex, textChar -> - if (textChar.isImage) { - callBack.onImageLongPress(x, y, textChar.charData) + touch(x, y) { _, textPos, _, _, textColumn -> + if (textColumn.style == 2) return@touch + if (textColumn.style == 1) { + callBack.onImageLongPress(x, y, textColumn.charData) } else { if (!selectAble) return@touch - textChar.selected = true + textColumn.selected = true invalidate() - select(relativePos, lineIndex, charIndex) + select(textPos) + } + } + } + + /** + * 单击 + * @return true:已处理, false:未处理 + */ + fun click(x: Float, y: Float): Boolean { + var handled = false + touch(x, y) { _, textPos, textPage, textLine, textColumn -> + if (textColumn.style == 2) { + context.toastOnUi("Button Pressed!") + handled = true } } + return handled } /** @@ -289,12 +391,13 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at fun selectText( x: Float, y: Float, - select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit, + select: (textPos: TextPos) -> Unit, ) { - touch(x, y) { _, relativePos, _, lineIndex, _, charIndex, textChar -> - textChar.selected = true + touch(x, y) { _, textPos, _, _, textColumn -> + if (textColumn.style == 2) return@touch + textColumn.selected = true invalidate() - select(relativePos, lineIndex, charIndex) + select(textPos) } } @@ -302,11 +405,10 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at * 开始选择符移动 */ fun selectStartMove(x: Float, y: Float) { - touch(x, y) { relativeOffset, relativePos, _, lineIndex, textLine, charIndex, textChar -> - val pos = TextPos(relativePos, lineIndex, charIndex) - if (selectStart.compare(pos) != 0) { - if (pos.compare(selectEnd) <= 0) { - selectStart.upData(pos = pos) + touch(x, y) { relativeOffset, textPos, _, textLine, textChar -> + if (selectStart.compare(textPos) != 0) { + if (textPos.compare(selectEnd) <= 0) { + selectStart.upData(pos = textPos) upSelectedStart( textChar.start, textLine.lineBottom + relativeOffset, @@ -322,11 +424,10 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at * 结束选择符移动 */ fun selectEndMove(x: Float, y: Float) { - touch(x, y) { relativeOffset, relativePos, _, lineIndex, textLine, charIndex, textChar -> - val pos = TextPos(relativePos, lineIndex, charIndex) - if (pos.compare(selectEnd) != 0) { - if (pos.compare(selectStart) >= 0) { - selectEnd.upData(pos) + touch(x, y) { relativeOffset, textPos, _, textLine, textChar -> + if (textPos.compare(selectEnd) != 0) { + if (textPos.compare(selectStart) >= 0) { + selectEnd.upData(textPos) upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset) upSelectChars() } @@ -334,17 +435,19 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at } } + /** + * 触碰位置信息 + * @param touched 回调 + */ private fun touch( x: Float, y: Float, touched: ( relativeOffset: Float, - relativePos: Int, + textPos: TextPos, textPage: TextPage, - lineIndex: Int, textLine: TextLine, - charIndex: Int, - textChar: TextChar + textColumn: TextColumn ) -> Unit ) { if (!visibleRect.contains(x, y)) return @@ -363,9 +466,8 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at if (textChar.isTouch(x)) { touched.invoke( relativeOffset, - relativePos, textPage, - lineIndex, textLine, - charIndex, textChar + TextPos(relativePos, lineIndex, charIndex), + textPage, textLine, textChar ) return } @@ -415,6 +517,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at textPos.lineIndex = lineIndex for ((charIndex, textChar) in textLine.textChars.withIndex()) { textPos.charIndex = charIndex + if (textChar.style == 2) continue textChar.selected = textPos.compare(selectStart) >= 0 && textPos.compare(selectEnd) <= 0 textChar.isSearchResult = textChar.selected && callBack.isSelectingSearchResult diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt index f8d86fa9e..048f9725b 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt @@ -14,6 +14,7 @@ import io.legado.app.help.config.ReadTipConfig import io.legado.app.model.ReadBook import io.legado.app.ui.book.read.ReadBookActivity import io.legado.app.ui.book.read.page.entities.TextPage +import io.legado.app.ui.book.read.page.entities.TextPos import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.widget.BatteryView import io.legado.app.utils.activity @@ -24,7 +25,7 @@ import splitties.views.backgroundColor import java.util.* /** - * 阅读界面 + * 页面视图 */ class PageView(context: Context) : FrameLayout(context) { @@ -104,6 +105,9 @@ class PageView(context: Context) : FrameLayout(context) { isGone = ReadBookConfig.hideStatusBar || readBookActivity?.isInMultiWindow == true } + /** + * 更新阅读信息 + */ private fun upTipStyle() = binding.run { tvHeaderLeft.tag = null tvHeaderMiddle.tag = null @@ -189,6 +193,10 @@ class PageView(context: Context) : FrameLayout(context) { } } + /** + * 获取信息视图 + * @param tip 信息类型 + */ private fun getTipView(tip: Int): BatteryView? = binding.run { return when (tip) { ReadTipConfig.tipHeaderLeft -> tvHeaderLeft @@ -201,6 +209,9 @@ class PageView(context: Context) : FrameLayout(context) { } } + /** + * 更新背景 + */ fun upBg() { if (ReadBookConfig.bgAlpha < 100) { binding.vwRoot.backgroundColor = ReadBookConfig.bgMeanColor @@ -211,15 +222,24 @@ class PageView(context: Context) : FrameLayout(context) { upBgAlpha() } + /** + * 更新背景透明度 + */ fun upBgAlpha() { binding.vwBg.alpha = ReadBookConfig.bgAlpha / 100f } + /** + * 更新时间信息 + */ fun upTime() { tvTime?.text = timeFormat.format(Date(System.currentTimeMillis())) upTimeBattery() } + /** + * 更新电池信息 + */ @SuppressLint("SetTextI18n") fun upBattery(battery: Int) { this.battery = battery @@ -228,6 +248,9 @@ class PageView(context: Context) : FrameLayout(context) { upTimeBattery() } + /** + * 更新电池信息 + */ @SuppressLint("SetTextI18n") private fun upTimeBattery() { val time = timeFormat.format(Date(System.currentTimeMillis())) @@ -235,6 +258,9 @@ class PageView(context: Context) : FrameLayout(context) { tvTimeBatteryP?.text = "$time $battery%" } + /** + * 设置内容 + */ fun setContent(textPage: TextPage, resetPageOffset: Boolean = true) { setProgress(textPage) if (resetPageOffset) { @@ -243,14 +269,23 @@ class PageView(context: Context) : FrameLayout(context) { binding.contentTextView.setContent(textPage) } + /** + * 设置无障碍文本 + */ fun setContentDescription(content: String) { binding.contentTextView.contentDescription = content } + /** + * 重置滚动位置 + */ fun resetPageOffset() { binding.contentTextView.resetPageOffset() } + /** + * 设置进度 + */ @SuppressLint("SetTextI18n") fun setProgress(textPage: TextPage) = textPage.apply { tvBookName?.text = ReadBook.book?.name @@ -260,24 +295,44 @@ class PageView(context: Context) : FrameLayout(context) { tvPageAndTotal?.text = "${index.plus(1)}/$pageSize $readProgress" } + /** + * 滚动事件 + */ fun scroll(offset: Int) { binding.contentTextView.scroll(offset) } + /** + * 更新是否开启选择功能 + */ fun upSelectAble(selectAble: Boolean) { binding.contentTextView.selectAble = selectAble } + /** + * 优先处理页面内单击 + * @return true:已处理, false:未处理 + */ + fun onClick(x: Float, y: Float): Boolean { + return binding.contentTextView.click(x, y - headerHeight) + } + + /** + * 长按事件 + */ fun longPress( x: Float, y: Float, - select: (relativePagePos: Int, lineIndex: Int, charIndex: Int) -> Unit, + select: (textPos: TextPos) -> Unit, ) { return binding.contentTextView.longPress(x, y - headerHeight, select) } + /** + * 选择文本 + */ fun selectText( x: Float, y: Float, - select: (relativePagePos: Int, lineIndex: Int, charIndex: Int) -> Unit, + select: (textPos: TextPos) -> Unit, ) { return binding.contentTextView.selectText(x, y - headerHeight, select) } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt index 6a8abc9bf..b2218c710 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/ReadView.kt @@ -28,11 +28,14 @@ import io.legado.app.ui.book.read.page.provider.TextPageFactory import io.legado.app.utils.activity import io.legado.app.utils.invisible import io.legado.app.utils.screenshot +import io.legado.app.utils.toastOnUi import java.text.BreakIterator import java.util.* import kotlin.math.abs - +/** + * 阅读视图 + */ class ReadView(context: Context, attrs: AttributeSet) : FrameLayout(context, attrs), DataSource { @@ -222,7 +225,9 @@ class ReadView(context: Context, attrs: AttributeSet) : pressDown = false if (!isPageMove) { if (!longPressed && !pressOnTextSelected) { - onSingleTapUp() + if (!curPage.onClick(startX, startY)) { + onSingleTapUp() + } return true } } @@ -292,18 +297,18 @@ class ReadView(context: Context, attrs: AttributeSet) : */ private fun onLongPress() { kotlin.runCatching { - curPage.longPress(startX, startY) { relativePos, lineIndex, charIndex -> + curPage.longPress(startX, startY) { textPos: TextPos -> isTextSelected = true pressOnTextSelected = true - initialTextPos.upData(relativePos, lineIndex, charIndex) - val startPos = TextPos(relativePos, lineIndex, charIndex) - val endPos = TextPos(relativePos, lineIndex, charIndex) - val page = curPage.relativePage(relativePos) + initialTextPos.upData(textPos) + val startPos = textPos.copy() + val endPos = textPos.copy() + val page = curPage.relativePage(textPos.relativePagePos) val stringBuilder = StringBuilder() - var cIndex = charIndex - var lineStart = lineIndex - var lineEnd = lineIndex - for (index in lineIndex - 1 downTo 0) { + var cIndex = textPos.charIndex + var lineStart = textPos.lineIndex + var lineEnd = textPos.lineIndex + for (index in textPos.lineIndex - 1 downTo 0) { val textLine = page.getLine(index) if (textLine.isParagraphEnd) { break @@ -313,7 +318,7 @@ class ReadView(context: Context, attrs: AttributeSet) : cIndex += textLine.charSize } } - for (index in lineIndex until page.lineSize) { + for (index in textPos.lineIndex until page.lineSize) { val textLine = page.getLine(index) stringBuilder.append(textLine.text) lineEnd += 1 @@ -419,11 +424,15 @@ class ReadView(context: Context, attrs: AttributeSet) : * 选择文本 */ private fun selectText(x: Float, y: Float) { - curPage.selectText(x, y) { relativePagePos, lineIndex, charIndex -> - val compare = initialTextPos.compare(relativePagePos, lineIndex, charIndex) + curPage.selectText(x, y) { textPos -> + val compare = initialTextPos.compare(textPos) when { compare >= 0 -> { - curPage.selectStartMoveIndex(relativePagePos, lineIndex, charIndex) + curPage.selectStartMoveIndex( + textPos.relativePagePos, + textPos.lineIndex, + textPos.charIndex + ) curPage.selectEndMoveIndex( initialTextPos.relativePagePos, initialTextPos.lineIndex, @@ -436,7 +445,11 @@ class ReadView(context: Context, attrs: AttributeSet) : initialTextPos.lineIndex, initialTextPos.charIndex ) - curPage.selectEndMoveIndex(relativePagePos, lineIndex, charIndex) + curPage.selectEndMoveIndex( + textPos.relativePagePos, + textPos.lineIndex, + textPos.charIndex + ) } } } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt index 9201880ea..ee703faf9 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt @@ -119,7 +119,11 @@ abstract class HorizontalPageDelegate(readView: ReadView) : PageDelegate(readVie abortAnim() if (!hasNext()) return setDirection(PageDirection.NEXT) - readView.setStartPoint(viewWidth.toFloat(), 1f, false) + val y = when { + viewHeight / 2 < startY -> viewHeight.toFloat() * 0.9f + else -> 1f + } + readView.setStartPoint(viewWidth.toFloat() * 0.9f, y, false) onAnimStart(animationSpeed) } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChapter.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChapter.kt index dfbf4a9cf..50d254e58 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChapter.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChapter.kt @@ -2,6 +2,9 @@ package io.legado.app.ui.book.read.page.entities import kotlin.math.min +/** + * 章节信息 + */ @Suppress("unused") data class TextChapter( val position: Int, diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextColumn.kt similarity index 73% rename from app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt rename to app/src/main/java/io/legado/app/ui/book/read/page/entities/TextColumn.kt index 518067182..82badb33f 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextColumn.kt @@ -1,11 +1,14 @@ package io.legado.app.ui.book.read.page.entities -data class TextChar( +/** + * 字符信息 + */ +data class TextColumn( val charData: String, var start: Float, var end: Float, + val style: Int = 0, //0:文字,1:图片,2:按钮 var selected: Boolean = false, - var isImage: Boolean = false, var isSearchResult: Boolean = false ) { diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt index 3ec66b3ca..076c1bc2f 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt @@ -4,10 +4,14 @@ import android.text.TextPaint import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.utils.textHeight +/** + * 行信息 + */ @Suppress("unused", "MemberVisibilityCanBePrivate") data class TextLine( var text: String = "", - val textChars: ArrayList = arrayListOf(), + val textChars: ArrayList = arrayListOf(), + val reviewCount: Int = 0, var lineTop: Float = 0f, var lineBase: Float = 0f, var lineBottom: Float = 0f, @@ -27,13 +31,13 @@ data class TextLine( lineBase = lineBottom - textPaint.fontMetrics.descent } - fun getTextChar(index: Int): TextChar { + fun getTextChar(index: Int): TextColumn { return textChars.getOrElse(index) { textChars.last() } } - fun getTextCharReverseAt(index: Int): TextChar { + fun getTextCharReverseAt(index: Int): TextColumn { return textChars[textChars.lastIndex - index] } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt index 3997b1ead..9c9446860 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt @@ -11,6 +11,9 @@ import splitties.init.appCtx import java.text.DecimalFormat import kotlin.math.min +/** + * 页面信息 + */ @Suppress("unused", "MemberVisibilityCanBePrivate") data class TextPage( var index: Int = 0, @@ -111,7 +114,12 @@ data class TextPage( val cw = StaticLayout.getDesiredWidth(char, ChapterProvider.contentPaint) val x1 = x + cw textLine.textChars.add( - TextChar(char, start = x, end = x1) + TextColumn( + char, + start = x, + end = x1, + style = if (textLine.text.length - 1 == index && char == "\uD83D\uDCAC") 2 else 0 + ) ) x = x1 } diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPos.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPos.kt index cc650c622..35dbfa589 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPos.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPos.kt @@ -1,5 +1,8 @@ package io.legado.app.ui.book.read.page.entities +/** + * 位置信息 + */ data class TextPos( var relativePagePos: Int, var lineIndex: Int, diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt index 5ae48f727..8dd49e208 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt @@ -14,7 +14,7 @@ import io.legado.app.help.config.AppConfig import io.legado.app.help.config.ReadBookConfig import io.legado.app.model.ReadBook import io.legado.app.ui.book.read.page.entities.TextChapter -import io.legado.app.ui.book.read.page.entities.TextChar +import io.legado.app.ui.book.read.page.entities.TextColumn import io.legado.app.ui.book.read.page.entities.TextLine import io.legado.app.ui.book.read.page.entities.TextPage import io.legado.app.utils.* @@ -116,7 +116,13 @@ object ChapterProvider { if (ReadBookConfig.titleMode != 2) { displayTitle.splitNotBlank("\n").forEach { text -> setTypeText( - book, absStartX, durY, text, textPages, stringBuilder, titlePaint, + book, + absStartX, + durY, + if (AppConfig.enableReview) text + "\ud83d\udcac" else text, + textPages, + stringBuilder, + titlePaint, isTitle = true, isTitleWithNoContent = contents.isEmpty(), isVolumeTitle = bookChapter.isVolume @@ -171,7 +177,13 @@ object ChapterProvider { val text = content.substring(start, content.length) if (text.isNotBlank()) { setTypeText( - book, absStartX, durY, text, textPages, stringBuilder, contentPaint + book, + absStartX, + durY, + if (AppConfig.enableReview) text + "\ud83d\udcac" else text, + textPages, + stringBuilder, + contentPaint ).let { absStartX = it.first durY = it.second @@ -252,7 +264,7 @@ object ChapterProvider { Pair(0f, width.toFloat()) } textLine.textChars.add( - TextChar(charData = src, start = x + start, end = x + end, isImage = true) + TextColumn(charData = src, start = x + start, end = x + end, style = 1) ) textPages.last().textLines.add(textLine) } @@ -276,9 +288,8 @@ object ChapterProvider { srcList: LinkedList? = null ): Pair { var absStartX = x - val layout = if (ReadBookConfig.useZhLayout) { - ZhLayout(text, textPaint, visibleWidth) - } else StaticLayout( + val layout = if (ReadBookConfig.useZhLayout) ZhLayout(text, textPaint, visibleWidth) + else StaticLayout( text, textPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true ) var durY = when { @@ -398,10 +409,14 @@ object ChapterProvider { } val bodyIndent = ReadBookConfig.paragraphIndent val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length - bodyIndent.toStringArray().forEach { char -> + for (char in bodyIndent.toStringArray()) { val x1 = x + icw textLine.textChars.add( - TextChar(charData = char, start = absStartX + x, end = absStartX + x1) + TextColumn( + charData = char, + start = absStartX + x, + end = absStartX + x1 + ) ) x = x1 } @@ -436,7 +451,7 @@ object ChapterProvider { words.forEachIndexed { index, char -> val cw = StaticLayout.getDesiredWidth(char, textPaint) val x1 = if (index != words.lastIndex) (x + cw + d) else (x + cw) - addCharToLine(book, absStartX, textLine, char, x, x1, srcList) + addCharToLine(book, absStartX, textLine, char, x, x1, index + 1 == words.size, srcList) x = x1 } exceed(absStartX, textLine, words) @@ -455,10 +470,10 @@ object ChapterProvider { srcList: LinkedList? ) { var x = startX - words.forEach { char -> + words.forEachIndexed { index, char -> val cw = StaticLayout.getDesiredWidth(char, textPaint) val x1 = x + cw - addCharToLine(book, absStartX, textLine, char, x, x1, srcList) + addCharToLine(book, absStartX, textLine, char, x, x1, index + 1 == words.size, srcList) x = x1 } exceed(absStartX, textLine, words) @@ -474,25 +489,27 @@ object ChapterProvider { char: String, xStart: Float, xEnd: Float, + isLineEnd: Boolean, srcList: LinkedList? ) { if (srcList != null && char == srcReplaceChar) { val src = srcList.removeFirst() ImageProvider.cacheImage(book, src, ReadBook.bookSource) textLine.textChars.add( - TextChar( + TextColumn( charData = src, start = absStartX + xStart, end = absStartX + xEnd, - isImage = true + style = 1 ) ) } else { textLine.textChars.add( - TextChar( + TextColumn( charData = char, start = absStartX + xStart, - end = absStartX + xEnd + end = absStartX + xEnd, + style = if (isLineEnd && char == "\uD83D\uDCAC") 2 else 0 ) ) } @@ -612,12 +629,12 @@ object ChapterProvider { * 更新绘制尺寸 */ fun upLayout() { - when(AppConfig.doublePageHorizontal){ + when (AppConfig.doublePageHorizontal) { "0" -> doublePage = false "1" -> doublePage = true "2" -> { doublePage = (viewWidth > viewHeight) - && ReadBook.pageAnim() != 3 + && ReadBook.pageAnim() != 3 } } diff --git a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt index da0d39aac..b1e799e51 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt @@ -41,10 +41,11 @@ class BookSourceEditActivity : private val adapter by lazy { BookSourceEditAdapter() } private val sourceEntities: ArrayList = ArrayList() private val searchEntities: ArrayList = ArrayList() - private val findEntities: ArrayList = ArrayList() + private val exploreEntities: ArrayList = ArrayList() private val infoEntities: ArrayList = ArrayList() private val tocEntities: ArrayList = ArrayList() private val contentEntities: ArrayList = ArrayList() + private val reviewEntities: ArrayList = ArrayList() private val qrCodeResult = registerForActivityResult(QrCodeResult()) { it ?: return@registerForActivityResult viewModel.importSource(it) { source -> @@ -180,10 +181,11 @@ class BookSourceEditActivity : private fun setEditEntities(tabPosition: Int?) { when (tabPosition) { 1 -> adapter.editEntities = searchEntities - 2 -> adapter.editEntities = findEntities + 2 -> adapter.editEntities = exploreEntities 3 -> adapter.editEntities = infoEntities 4 -> adapter.editEntities = tocEntities 5 -> adapter.editEntities = contentEntities + 6 -> adapter.editEntities = reviewEntities else -> adapter.editEntities = sourceEntities } binding.recyclerView.scrollToPosition(0) @@ -192,8 +194,9 @@ class BookSourceEditActivity : private fun upSourceView(source: BookSource? = viewModel.bookSource) { source?.let { binding.cbIsEnable.isChecked = it.enabled - binding.cbIsEnableFind.isChecked = it.enabledExplore + binding.cbIsEnableExplore.isChecked = it.enabledExplore binding.cbIsEnableCookie.isChecked = it.enabledCookieJar ?: false + binding.cbIsEnableReview.isChecked = it.enabledReview ?: false binding.spType.setSelection( when (it.bookSourceType) { BookType.file -> 3 @@ -203,7 +206,7 @@ class BookSourceEditActivity : } ) } - //基本信息 + // 基本信息 sourceEntities.clear() sourceEntities.apply { add(EditEntity("bookSourceUrl", source?.bookSourceUrl, R.string.source_url)) @@ -218,7 +221,7 @@ class BookSourceEditActivity : add(EditEntity("variableComment", source?.variableComment, R.string.variable_comment)) add(EditEntity("concurrentRate", source?.concurrentRate, R.string.concurrent_rate)) } - //搜索 + // 搜索 val sr = source?.getSearchRule() searchEntities.clear() searchEntities.apply { @@ -234,10 +237,10 @@ class BookSourceEditActivity : add(EditEntity("coverUrl", sr?.coverUrl, R.string.rule_cover_url)) add(EditEntity("bookUrl", sr?.bookUrl, R.string.r_book_url)) } - //发现 + // 发现 val er = source?.getExploreRule() - findEntities.clear() - findEntities.apply { + exploreEntities.clear() + exploreEntities.apply { add(EditEntity("exploreUrl", source?.exploreUrl, R.string.r_find_url)) add(EditEntity("bookList", er?.bookList, R.string.r_book_list)) add(EditEntity("name", er?.name, R.string.r_book_name)) @@ -249,7 +252,7 @@ class BookSourceEditActivity : add(EditEntity("coverUrl", er?.coverUrl, R.string.rule_cover_url)) add(EditEntity("bookUrl", er?.bookUrl, R.string.r_book_url)) } - //详情页 + // 详情页 val ir = source?.getBookInfoRule() infoEntities.clear() infoEntities.apply { @@ -265,7 +268,7 @@ class BookSourceEditActivity : add(EditEntity("canReName", ir?.canReName, R.string.rule_can_re_name)) add(EditEntity("downloadUrls", ir?.downloadUrls, R.string.download_url_rule)) } - //目录页 + // 目录页 val tr = source?.getTocRule() tocEntities.clear() tocEntities.apply { @@ -279,7 +282,7 @@ class BookSourceEditActivity : add(EditEntity("isPay", tr?.isPay, R.string.rule_is_pay)) add(EditEntity("nextTocUrl", tr?.nextTocUrl, R.string.rule_next_toc_url)) } - //正文页 + // 正文页 val cr = source?.getContentRule() contentEntities.clear() contentEntities.apply { @@ -291,6 +294,21 @@ class BookSourceEditActivity : add(EditEntity("imageStyle", cr?.imageStyle, R.string.rule_image_style)) add(EditEntity("payAction", cr?.payAction, R.string.rule_pay_action)) } + // 段评 + val rr = source?.getReviewRule() + reviewEntities.clear() + reviewEntities.apply { + add(EditEntity("reviewUrl", rr?.reviewUrl, R.string.rule_review_url)) + add(EditEntity("avatarRule", rr?.avatarRule, R.string.rule_avatar)) + add(EditEntity("contentRule", rr?.contentRule, R.string.rule_review_content)) + add(EditEntity("postTimeRule", rr?.postTimeRule, R.string.rule_post_time)) + add(EditEntity("reviewQuoteUrl", rr?.reviewQuoteUrl, R.string.rule_review_quote)) + add(EditEntity("voteUpUrl", rr?.voteUpUrl, R.string.review_vote_up)) + add(EditEntity("voteDownUrl", rr?.voteDownUrl, R.string.review_vote_down)) + add(EditEntity("postReviewUrl", rr?.postReviewUrl, R.string.post_review_url)) + add(EditEntity("postQuoteUrl", rr?.postQuoteUrl, R.string.post_quote_url)) + add(EditEntity("deleteUrl", rr?.deleteUrl, R.string.delete_review_url)) + } binding.tabLayout.selectTab(binding.tabLayout.getTabAt(0)) setEditEntities(0) } @@ -298,8 +316,9 @@ class BookSourceEditActivity : private fun getSource(): BookSource { val source = viewModel.bookSource?.copy() ?: BookSource() source.enabled = binding.cbIsEnable.isChecked - source.enabledExplore = binding.cbIsEnableFind.isChecked + source.enabledExplore = binding.cbIsEnableExplore.isChecked source.enabledCookieJar = binding.cbIsEnableCookie.isChecked + source.enabledReview = binding.cbIsEnableReview.isChecked source.bookSourceType = when (binding.spType.selectedItemPosition) { 3 -> BookType.file 2 -> BookType.image @@ -311,6 +330,7 @@ class BookSourceEditActivity : val bookInfoRule = BookInfoRule() val tocRule = TocRule() val contentRule = ContentRule() + val reviewRule = ReviewRule() sourceEntities.forEach { when (it.key) { "bookSourceUrl" -> source.bookSourceUrl = it.value ?: "" @@ -351,7 +371,7 @@ class BookSourceEditActivity : viewModel.ruleComplete(it.value, searchRule.bookList, 2) } } - findEntities.forEach { + exploreEntities.forEach { when (it.key) { "exploreUrl" -> source.exploreUrl = it.value "bookList" -> exploreRule.bookList = it.value @@ -429,11 +449,30 @@ class BookSourceEditActivity : "payAction" -> contentRule.payAction = it.value } } + reviewEntities.forEach { + when (it.key) { + "reviewUrl" -> reviewRule.reviewUrl = it.value + "avatarRule" -> reviewRule.avatarRule = + viewModel.ruleComplete(it.value, reviewRule.reviewUrl, 3) + "contentRule" -> reviewRule.contentRule = + viewModel.ruleComplete(it.value, reviewRule.reviewUrl) + "postTimeRule" -> reviewRule.postTimeRule = + viewModel.ruleComplete(it.value, reviewRule.reviewUrl) + "reviewQuoteUrl" -> reviewRule.reviewQuoteUrl = + viewModel.ruleComplete(it.value, reviewRule.reviewUrl, 2) + "voteUpUrl" -> reviewRule.voteUpUrl = it.value + "voteDownUrl" -> reviewRule.voteDownUrl = it.value + "postReviewUrl" -> reviewRule.postReviewUrl = it.value + "postQuoteUrl" -> reviewRule.postQuoteUrl = it.value + "deleteUrl" -> reviewRule.deleteUrl =it.value + } + } source.ruleSearch = searchRule source.ruleExplore = exploreRule source.ruleBookInfo = bookInfoRule source.ruleToc = tocRule source.ruleContent = contentRule + source.ruleReview = reviewRule return source } diff --git a/app/src/main/res/layout/activity_book_source_edit.xml b/app/src/main/res/layout/activity_book_source_edit.xml index ceae414f2..bed6d5a36 100644 --- a/app/src/main/res/layout/activity_book_source_edit.xml +++ b/app/src/main/res/layout/activity_book_source_edit.xml @@ -27,7 +27,7 @@ android:text="@string/is_enable" /> - + + + + + @@ -94,6 +109,11 @@ android:layout_height="wrap_content" android:text="@string/source_tab_content" /> + + + + 选中所选区间 返回时提示放入书架 阅读未放入书架的书籍在返回时提示放入书架 + Review + 段评URL(reviewUrl) + 段评发布者头像(avatarRule) + 段评内容(contentRule) + 段评回复URL(reviewQuoteUrl) + 段评发布时间(postTimeRule) + 点踩URL(voteUpUrl) + 点赞URL(voteDownUrl) + 发送回复URL(postReviewUrl) + 发送回复段评URL(postQuoteUrl) + 删除段评URL(deleteUrl) 标志:发现已启用 标志:发现已禁用 show read title addition area diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index aedfeae99..49ae6bd4a 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -1013,6 +1013,17 @@ 选中所选区间 返回时提示放入书架 阅读未放入书架的书籍在返回时提示放入书架 + Review + 段评URL(reviewUrl) + 段评发布者头像(avatarRule) + 段评内容(contentRule) + 段评发布时间(postTimeRule) + 段评回复URL(reviewQuoteUrl) + 点踩URL(voteUpUrl) + 点赞URL(voteDownUrl) + 发送回复URL(postReviewUrl) + 发送回复段评URL(postQuoteUrl) + 删除段评URL(deleteUrl) 标志:发现已启用 标志:发现已禁用 show read title addition area diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 84e8ec0df..7251b06bb 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -1013,6 +1013,17 @@ 选中所选区间 返回时提示放入书架 阅读未放入书架的书籍在返回时提示放入书架 + Review + 段评URL(reviewUrl) + 段评发布者头像(avatarRule) + 段评内容(contentRule) + 段评发布时间(postTimeRule) + 段评回复URL(reviewQuoteUrl) + 点踩URL(voteUpUrl) + 点赞URL(voteDownUrl) + 发送回复URL(postReviewUrl) + 发送回复段评URL(postQuoteUrl) + 删除段评URL(deleteUrl) 标志:发现已启用 标志:发现已禁用 show read title addition area diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index f74ba349d..9ec80a4b1 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -1010,6 +1010,17 @@ 选中所选区间 返回时提示放入书架 阅读未放入书架的书籍在返回时提示放入书架 + 段评 + 段评URL(reviewUrl) + 段评发布者头像(avatarRule) + 段评内容(contentRule) + 段评发布时间(postTimeRule) + 段评回复URL(reviewQuoteUrl) + 点踩URL(voteUpUrl) + 点赞URL(voteDownUrl) + 发送回复URL(postReviewUrl) + 发送回复段评URL(postQuoteUrl) + 删除段评URL(deleteUrl) 标志:发现已启用 标志:发现已禁用 展示顶部工具栏附加区域 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 480bad8c9..b004c5dda 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -1012,6 +1012,17 @@ 选中所选区间 返回时提示放入书架 阅读未放入书架的书籍在返回时提示放入书架 + 段评 + 段评URL(reviewUrl) + 段评发布者头像(avatarRule) + 段评内容(contentRule) + 段评发布时间(postTimeRule) + 段评回复URL(reviewQuoteUrl) + 点踩URL(voteUpUrl) + 点赞URL(voteDownUrl) + 发送回复URL(postReviewUrl) + 发送回复段评URL(postQuoteUrl) + 删除段评URL(deleteUrl) 标志:发现已启用 标志:发现已禁用 展示顶部工具栏附加区域 diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 8515e27fe..acc0a7721 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1012,6 +1012,17 @@ 选中所选区间 返回时提示放入书架 阅读未放入书架的书籍在返回时提示放入书架 + 段评 + 段评URL(reviewUrl) + 段评发布者头像(avatarRule) + 段评内容(contentRule) + 段评发布时间(postTimeRule) + 段评回复URL(reviewQuoteUrl) + 点踩URL(voteUpUrl) + 点赞URL(voteDownUrl) + 发送回复URL(postReviewUrl) + 发送回复段评URL(postQuoteUrl) + 删除段评URL(deleteUrl) 标志:发现已启用 标志:发现已禁用 展示顶部工具栏附加区域 diff --git a/app/src/main/res/values/non_translat.xml b/app/src/main/res/values/non_translat.xml index 14d5c13bd..2b6917391 100644 --- a/app/src/main/res/values/non_translat.xml +++ b/app/src/main/res/values/non_translat.xml @@ -1,25 +1,25 @@ - bookshelf_px - 开源阅读 - kunfei.ge@gmail.com + bookshelf_px + 开源阅读 + kunfei.ge@gmail.com - https://alanskycn.gitee.io/teachme/ - https://github.com/gedoor/legado - https://github.com/gedoor/legado/graphs/contributors - https://gedoor.github.io - https://github.com/gedoor/legado/blob/master/LICENSE - https://github.com/gedoor/legado/releases/latest - https://api.github.com/repos/gedoor/legado/releases/latest - https://t.me/legado_channels - https://discord.gg/qDE52P5xGW + https://alanskycn.gitee.io/teachme/ + https://github.com/gedoor/legado + https://github.com/gedoor/legado/graphs/contributors + https://gedoor.github.io + https://github.com/gedoor/legado/blob/master/LICENSE + https://github.com/gedoor/legado/releases/latest + https://api.github.com/repos/gedoor/legado/releases/latest + https://t.me/legado_channels + https://discord.gg/qDE52P5xGW - http://%1$s:%2$d - GitHub - 【%s】 - 🔒%s - 🔓%s + http://%1$s:%2$d + GitHub + 【%s】 + 🔒%s + 🔓%s - + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1664e0621..fe3f6c4b0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1013,6 +1013,17 @@ 选中所选区间 返回时提示放入书架 阅读未放入书架的书籍在返回时提示放入书架 + Review + 段评发布者头像(avatarRule) + 段评URL(reviewUrl) + 段评内容(contentRule) + 段评发布时间(postTimeRule) + 段评回复URL(reviewQuoteUrl) + 点踩URL(voteUpUrl) + 点赞URL(voteDownUrl) + 发送回复URL(postReviewUrl) + 发送回复段评URL(postQuoteUrl) + 删除段评URL(deleteUrl) 标志:发现已启用 标志:发现已禁用 show read title addition area