diff --git a/api/src/main/java/com/readrops/api/localfeed/atom/ATOMItemAdapter.kt b/api/src/main/java/com/readrops/api/localfeed/atom/ATOMItemAdapter.kt index a7ea08a3..aeccdf69 100644 --- a/api/src/main/java/com/readrops/api/localfeed/atom/ATOMItemAdapter.kt +++ b/api/src/main/java/com/readrops/api/localfeed/atom/ATOMItemAdapter.kt @@ -4,7 +4,7 @@ import com.gitlab.mvysny.konsumexml.Konsumer import com.gitlab.mvysny.konsumexml.Names import com.gitlab.mvysny.konsumexml.allChildrenAutoIgnore import com.readrops.api.localfeed.XmlAdapter -import com.readrops.api.utils.* +import com.readrops.api.utils.DateUtils import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.extensions.nonNullText import com.readrops.api.utils.extensions.nullableText @@ -22,7 +22,7 @@ class ATOMItemAdapter : XmlAdapter { konsumer.allChildrenAutoIgnore(names) { when (tagName) { "title" -> title = nonNullText() - "id" -> guid = nullableText() + "id" -> remoteId = nullableText() "updated" -> pubDate = DateUtils.parse(nullableText()) "link" -> parseLink(this, this@apply) "author" -> allChildrenAutoIgnore("name") { author = nullableText() } @@ -35,7 +35,7 @@ class ATOMItemAdapter : XmlAdapter { validateItem(item) if (item.pubDate == null) item.pubDate = LocalDateTime.now() - if (item.guid == null) item.guid = item.link + if (item.remoteId == null) item.remoteId = item.link item } catch (e: Exception) { diff --git a/api/src/main/java/com/readrops/api/localfeed/json/JSONItemsAdapter.kt b/api/src/main/java/com/readrops/api/localfeed/json/JSONItemsAdapter.kt index 75e43c6c..f0b67055 100644 --- a/api/src/main/java/com/readrops/api/localfeed/json/JSONItemsAdapter.kt +++ b/api/src/main/java/com/readrops/api/localfeed/json/JSONItemsAdapter.kt @@ -33,7 +33,7 @@ class JSONItemsAdapter : JsonAdapter>() { while (hasNext()) { with(item) { when (selectName(names)) { - 0 -> guid = nextNonEmptyString() + 0 -> remoteId = nextNonEmptyString() 1 -> link = nextNonEmptyString() 2 -> title = nextNonEmptyString() 3 -> contentHtml = nextNullableString() diff --git a/api/src/main/java/com/readrops/api/localfeed/rss1/RSS1ItemAdapter.kt b/api/src/main/java/com/readrops/api/localfeed/rss1/RSS1ItemAdapter.kt index 9f838dcf..4eeaa3b1 100644 --- a/api/src/main/java/com/readrops/api/localfeed/rss1/RSS1ItemAdapter.kt +++ b/api/src/main/java/com/readrops/api/localfeed/rss1/RSS1ItemAdapter.kt @@ -5,7 +5,7 @@ import com.gitlab.mvysny.konsumexml.Names import com.gitlab.mvysny.konsumexml.allChildrenAutoIgnore import com.readrops.api.localfeed.XmlAdapter import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX -import com.readrops.api.utils.* +import com.readrops.api.utils.DateUtils import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.extensions.nonNullText import com.readrops.api.utils.extensions.nullableText @@ -40,7 +40,7 @@ class RSS1ItemAdapter : XmlAdapter { if (item.pubDate == null) item.pubDate = LocalDateTime.now() if (item.link == null) item.link = about ?: throw ParseException("RSS1 link or about element is required") - item.guid = item.link + item.remoteId = item.link if (authors.filterNotNull().isNotEmpty()) item.author = authors.filterNotNull() .joinToString(limit = AUTHORS_MAX) diff --git a/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2ItemAdapter.kt b/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2ItemAdapter.kt index fc36d3ba..05a59b0b 100644 --- a/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2ItemAdapter.kt +++ b/api/src/main/java/com/readrops/api/localfeed/rss2/RSS2ItemAdapter.kt @@ -1,9 +1,13 @@ package com.readrops.api.localfeed.rss2 -import com.gitlab.mvysny.konsumexml.* +import com.gitlab.mvysny.konsumexml.Konsumer +import com.gitlab.mvysny.konsumexml.KonsumerException +import com.gitlab.mvysny.konsumexml.Names +import com.gitlab.mvysny.konsumexml.allChildrenAutoIgnore import com.readrops.api.localfeed.XmlAdapter import com.readrops.api.localfeed.XmlAdapter.Companion.AUTHORS_MAX -import com.readrops.api.utils.* +import com.readrops.api.utils.ApiUtils +import com.readrops.api.utils.DateUtils import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.extensions.nonNullText import com.readrops.api.utils.extensions.nullableText @@ -29,7 +33,7 @@ class RSS2ItemAdapter : XmlAdapter { "dc:creator" -> creators += nullableText() "pubDate" -> pubDate = DateUtils.parse(nullableText()) "dc:date" -> pubDate = DateUtils.parse(nullableText()) - "guid" -> guid = nullableText() + "guid" -> remoteId = nullableText() "description" -> description = nullableTextRecursively() "content:encoded" -> content = nullableTextRecursively() "enclosure" -> parseEnclosure(this, item = this@apply) @@ -81,7 +85,7 @@ class RSS2ItemAdapter : XmlAdapter { validateItem(this) if (pubDate == null) pubDate = LocalDateTime.now() - if (guid == null) guid = link + if (remoteId == null) remoteId = link if (author == null && creators.filterNotNull().isNotEmpty()) author = creators.filterNotNull().joinToString(limit = AUTHORS_MAX) } diff --git a/api/src/main/java/com/readrops/api/services/nextcloudnews/adapters/NextcloudNewsItemsAdapter.kt b/api/src/main/java/com/readrops/api/services/nextcloudnews/adapters/NextcloudNewsItemsAdapter.kt index dd98f628..d49dcd8a 100644 --- a/api/src/main/java/com/readrops/api/services/nextcloudnews/adapters/NextcloudNewsItemsAdapter.kt +++ b/api/src/main/java/com/readrops/api/services/nextcloudnews/adapters/NextcloudNewsItemsAdapter.kt @@ -1,11 +1,11 @@ package com.readrops.api.services.nextcloudnews.adapters import android.annotation.SuppressLint -import com.readrops.db.entities.Item import com.readrops.api.utils.ApiUtils import com.readrops.api.utils.exceptions.ParseException import com.readrops.api.utils.extensions.nextNonEmptyString import com.readrops.api.utils.extensions.nextNullableString +import com.readrops.db.entities.Item import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonReader import com.squareup.moshi.JsonWriter @@ -49,7 +49,6 @@ class NextcloudNewsItemsAdapter : JsonAdapter>() { 8 -> feedRemoteId = reader.nextInt().toString() 9 -> isRead = !reader.nextBoolean() // the negation is important here 10 -> isStarred = reader.nextBoolean() - 11 -> guid = reader.nextNullableString() else -> reader.skipValue() } } @@ -73,6 +72,6 @@ class NextcloudNewsItemsAdapter : JsonAdapter>() { companion object { val NAMES: JsonReader.Options = JsonReader.Options.of("id", "url", "title", "author", - "pubDate", "body", "enclosureMime", "enclosureLink", "feedId", "unread", "starred", "guidHash") + "pubDate", "body", "enclosureMime", "enclosureLink", "feedId", "unread", "starred") } } \ No newline at end of file diff --git a/api/src/test/java/com/readrops/api/localfeed/atom/ATOMAdapterTest.kt b/api/src/test/java/com/readrops/api/localfeed/atom/ATOMAdapterTest.kt index 4e345fcc..5a4556e3 100644 --- a/api/src/test/java/com/readrops/api/localfeed/atom/ATOMAdapterTest.kt +++ b/api/src/test/java/com/readrops/api/localfeed/atom/ATOMAdapterTest.kt @@ -9,7 +9,6 @@ import junit.framework.TestCase.assertEquals import org.junit.Assert.assertThrows import org.junit.Assert.assertTrue import org.junit.Test -import java.lang.Exception class ATOMAdapterTest { @@ -37,7 +36,7 @@ class ATOMAdapterTest { assertEquals(pubDate, DateUtils.parse("2020-09-06T21:09:59Z")) assertEquals(author, "Shinokuni") assertEquals(description, "Summary") - assertEquals(guid, "tag:github.com,2008:Grit::Commit/c15f093a1bc4211e85f8d1817c9073e307afe5ac") + assertEquals(remoteId, "tag:github.com,2008:Grit::Commit/c15f093a1bc4211e85f8d1817c9073e307afe5ac") TestCase.assertNotNull(content) } } diff --git a/api/src/test/java/com/readrops/api/localfeed/json/JSONFeedAdapterTest.kt b/api/src/test/java/com/readrops/api/localfeed/json/JSONFeedAdapterTest.kt index ea00b757..c5cf8f09 100644 --- a/api/src/test/java/com/readrops/api/localfeed/json/JSONFeedAdapterTest.kt +++ b/api/src/test/java/com/readrops/api/localfeed/json/JSONFeedAdapterTest.kt @@ -9,7 +9,6 @@ import com.squareup.moshi.Moshi import com.squareup.moshi.Types import junit.framework.TestCase import junit.framework.TestCase.assertEquals -import junit.framework.TestCase.assertTrue import okio.Buffer import org.junit.Assert.assertThrows import org.junit.Test @@ -40,7 +39,7 @@ class JSONFeedAdapterTest { with(items[0]) { assertEquals(items.size, 10) - assertEquals(guid, "http://flyingmeat.com/blog/archives/2017/9/acorn_and_10.13.html") + assertEquals(remoteId, "http://flyingmeat.com/blog/archives/2017/9/acorn_and_10.13.html") assertEquals(title, "Acorn and 10.13") assertEquals(link, "http://flyingmeat.com/blog/archives/2017/9/acorn_and_10.13.html") assertEquals(pubDate, DateUtils.parse("2017-09-25T14:27:27-07:00")) diff --git a/api/src/test/java/com/readrops/api/localfeed/rss1/RSS1AdapterTest.kt b/api/src/test/java/com/readrops/api/localfeed/rss1/RSS1AdapterTest.kt index ae266fa9..312d38a9 100644 --- a/api/src/test/java/com/readrops/api/localfeed/rss1/RSS1AdapterTest.kt +++ b/api/src/test/java/com/readrops/api/localfeed/rss1/RSS1AdapterTest.kt @@ -35,7 +35,7 @@ class RSS1AdapterTest { assertEquals(title, "Google Expands its Flutter Development Kit To Windows Apps") assertEquals(link!!.trim(), "https://developers.slashdot.org/story/20/09/23/1616231/google-expands-" + "its-flutter-development-kit-to-windows-apps?utm_source=rss1.0mainlinkanon&utm_medium=feed") - assertEquals(guid!!.trim(), "https://developers.slashdot.org/story/20/09/23/1616231/google-expands-" + + assertEquals(remoteId!!.trim(), "https://developers.slashdot.org/story/20/09/23/1616231/google-expands-" + "its-flutter-development-kit-to-windows-apps?utm_source=rss1.0mainlinkanon&utm_medium=feed") assertEquals(pubDate, DateUtils.parse("2020-09-23T16:15:00+00:00")) assertEquals(author, "msmash") diff --git a/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2AdapterTest.kt b/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2AdapterTest.kt index 5d03a336..a1100296 100644 --- a/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2AdapterTest.kt +++ b/api/src/test/java/com/readrops/api/localfeed/rss2/RSS2AdapterTest.kt @@ -36,7 +36,7 @@ class RSS2AdapterTest { assertEquals(pubDate, DateUtils.parse("Tue, 25 Aug 2020 17:15:49 +0000")) assertEquals(author, "Author 1") assertEquals(description, "Comments") - assertEquals(guid, "https://www.bbc.com/news/world-africa-53887947") + assertEquals(remoteId, "https://www.bbc.com/news/world-africa-53887947") } } @@ -55,7 +55,7 @@ class RSS2AdapterTest { val stream = TestUtils.loadResource("localfeed/rss2/rss_items_other_namespaces.xml") val item = adapter.fromXml(stream.konsumeXml()).second[0] - assertEquals(item.guid, "guid") + assertEquals(item.remoteId, "guid") assertEquals(item.author, "creator 1, creator 2, creator 3, creator 4") assertEquals(item.pubDate, DateUtils.parse("2020-08-05T14:03:48Z")) assertEquals(item.content, "content:encoded") diff --git a/api/src/test/java/com/readrops/api/services/nextcloudnews/adapters/NextcloudNewsItemsAdapterTest.kt b/api/src/test/java/com/readrops/api/services/nextcloudnews/adapters/NextcloudNewsItemsAdapterTest.kt index c68dbd1c..f7bd6ed6 100644 --- a/api/src/test/java/com/readrops/api/services/nextcloudnews/adapters/NextcloudNewsItemsAdapterTest.kt +++ b/api/src/test/java/com/readrops/api/services/nextcloudnews/adapters/NextcloudNewsItemsAdapterTest.kt @@ -25,7 +25,6 @@ class NextcloudNewsItemsAdapterTest { with(item) { assertEquals(remoteId, "3443") - assertEquals(guid, "3059047a572cd9cd5d0bf645faffd077") assertEquals(link, "http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/") assertEquals(title, "Plasma-nm after the solid sprint") assertEquals(author, "Jan Grulich (grulja)") diff --git a/app/src/androidTest/java/com/readrops/app/LocalRSSRepositoryTest.kt b/app/src/androidTest/java/com/readrops/app/LocalRSSRepositoryTest.kt index 5ce136c8..8b87f364 100644 --- a/app/src/androidTest/java/com/readrops/app/LocalRSSRepositoryTest.kt +++ b/app/src/androidTest/java/com/readrops/app/LocalRSSRepositoryTest.kt @@ -90,7 +90,7 @@ class LocalRSSRepositoryTest : KoinTest { assertTrue { result.first.items.isNotEmpty() } assertTrue { - database.itemDao().itemExists(result.first.items.first().guid!!, account.id) + database.itemDao().itemExists(result.first.items.first().remoteId!!, account.id) } } @@ -110,7 +110,7 @@ class LocalRSSRepositoryTest : KoinTest { assertTrue { result.first.items.isNotEmpty() } assertTrue { - database.itemDao().itemExists(result.first.items.first().guid!!, account.id) + database.itemDao().itemExists(result.first.items.first().remoteId!!, account.id) } } } \ No newline at end of file diff --git a/app/src/main/java/com/readrops/app/repositories/LocalRSSRepository.kt b/app/src/main/java/com/readrops/app/repositories/LocalRSSRepository.kt index 5e0a34af..5088e179 100644 --- a/app/src/main/java/com/readrops/app/repositories/LocalRSSRepository.kt +++ b/app/src/main/java/com/readrops/app/repositories/LocalRSSRepository.kt @@ -87,7 +87,7 @@ class LocalRSSRepository( val newItems = mutableListOf() for (item in items) { - if (!database.itemDao().itemExists(item.guid!!, feed.accountId)) { + if (!database.itemDao().itemExists(item.remoteId!!, feed.accountId)) { if (item.description != null) { item.cleanDescription = Jsoup.parse(item.description).text() } diff --git a/db/schemas/com.readrops.db.Database/4.json b/db/schemas/com.readrops.db.Database/4.json index d0286e79..493eecab 100644 --- a/db/schemas/com.readrops.db.Database/4.json +++ b/db/schemas/com.readrops.db.Database/4.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 4, - "identityHash": "5043c0aff2ed15f8cbe250e35bb9129e", + "identityHash": "7059fa306ee4013c51c8a521e04e3e31", "entities": [ { "tableName": "Feed", @@ -153,7 +153,7 @@ }, { "tableName": "Item", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `description` TEXT, `clean_description` TEXT, `link` TEXT, `image_link` TEXT, `author` TEXT, `pub_date` INTEGER, `content` TEXT, `feed_id` INTEGER NOT NULL, `guid` TEXT, `read_time` REAL NOT NULL, `read` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `read_it_later` INTEGER NOT NULL, `remoteId` TEXT, FOREIGN KEY(`feed_id`) REFERENCES `Feed`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `description` TEXT, `clean_description` TEXT, `link` TEXT, `image_link` TEXT, `author` TEXT, `pub_date` INTEGER, `content` TEXT, `feed_id` INTEGER NOT NULL, `read_time` REAL NOT NULL, `read` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `read_it_later` INTEGER NOT NULL, `remoteId` TEXT, FOREIGN KEY(`feed_id`) REFERENCES `Feed`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { "fieldPath": "id", @@ -215,12 +215,6 @@ "affinity": "INTEGER", "notNull": true }, - { - "fieldPath": "guid", - "columnName": "guid", - "affinity": "TEXT", - "notNull": false - }, { "fieldPath": "readTime", "columnName": "read_time", @@ -267,15 +261,6 @@ ], "orders": [], "createSql": "CREATE INDEX IF NOT EXISTS `index_Item_feed_id` ON `${TABLE_NAME}` (`feed_id`)" - }, - { - "name": "index_Item_guid", - "unique": false, - "columnNames": [ - "guid" - ], - "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_Item_guid` ON `${TABLE_NAME}` (`guid`)" } ], "foreignKeys": [ @@ -547,7 +532,7 @@ "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, '5043c0aff2ed15f8cbe250e35bb9129e')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7059fa306ee4013c51c8a521e04e3e31')" ] } } \ No newline at end of file diff --git a/db/src/androidTest/java/com/readrops/db/MigrationsTest.kt b/db/src/androidTest/java/com/readrops/db/MigrationsTest.kt index c16a9347..bef94a6e 100644 --- a/db/src/androidTest/java/com/readrops/db/MigrationsTest.kt +++ b/db/src/androidTest/java/com/readrops/db/MigrationsTest.kt @@ -3,6 +3,7 @@ package com.readrops.db import androidx.room.testing.MigrationTestHelper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry +import junit.framework.Assert.assertEquals import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -39,4 +40,18 @@ class MigrationsTest { close() } } + + @Test + fun migrate3to4() { + helper.createDatabase(dbName, 3).apply { + execSQL("Insert Into Account(account_type, last_modified, current_account, notifications_enabled) Values(0, 0, 0, 0)") + execSQL("Insert Into Feed(text_color, background_color, account_id, notification_enabled) Values(0, 0, 3, 0)") + execSQL("Insert Into Item(title, feed_id, read_time, read, starred, read_it_later, guid) values(\"test\", 12, 0, 0, 0, 0, \"guid\")") + } + + helper.runMigrationsAndValidate(dbName, 4, true, MigrationFrom3To4).apply { + val remoteId = compileStatement("Select remoteId From Item").simpleQueryForString() + assertEquals("guid", remoteId) + } + } } \ No newline at end of file diff --git a/db/src/main/java/com/readrops/db/Database.kt b/db/src/main/java/com/readrops/db/Database.kt index 2d5b8aaf..88db7b1d 100644 --- a/db/src/main/java/com/readrops/db/Database.kt +++ b/db/src/main/java/com/readrops/db/Database.kt @@ -1,6 +1,5 @@ package com.readrops.db -import androidx.room.AutoMigration import androidx.room.Database import androidx.room.RoomDatabase import androidx.room.TypeConverters @@ -21,10 +20,8 @@ import com.readrops.db.entities.account.Account @Database( entities = [Feed::class, Item::class, Folder::class, Account::class, - ItemStateChange::class, ItemState::class], version = 4, - autoMigrations = [ - AutoMigration(3, 4) - ] + ItemStateChange::class, ItemState::class], + version = 4 ) @TypeConverters(Converters::class) abstract class Database : RoomDatabase() { @@ -57,7 +54,7 @@ object MigrationFrom2To3 : Migration(2, 3) { db.execSQL("""CREATE TABLE IF NOT EXISTS `ItemStateChange` (`id` INTEGER NOT NULL, `read_change` INTEGER NOT NULL, `star_change` INTEGER NOT NULL, `account_id` INTEGER NOT NULL, PRIMARY KEY(`id`), FOREIGN KEY(`account_id`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )""") db.execSQL("""CREATE TABLE IF NOT EXISTS `ItemState` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `read` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `remote_id` TEXT NOT NULL, `account_id` INTEGER NOT NULL, FOREIGN KEY(`account_id`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )""") - // removing read_changed and adding starred fields. Table is recreated to keep field order + // removing read_changed and adding starred fields db.execSQL("""CREATE TABLE IF NOT EXISTS `Item_MERGE_TABLE` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `description` TEXT, `clean_description` TEXT, `link` TEXT, `image_link` TEXT, `author` TEXT, `pub_date` INTEGER, `content` TEXT, `feed_id` INTEGER NOT NULL, `guid` TEXT, `read_time` REAL NOT NULL, `read` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `read_it_later` INTEGER NOT NULL, `remoteId` TEXT, FOREIGN KEY(`feed_id`) REFERENCES `Feed`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )""") db.execSQL("""INSERT INTO `Item_MERGE_TABLE` (`id`,`title`,`description`,`clean_description`,`link`,`image_link`,`author`,`pub_date`,`content`,`feed_id`,`guid`,`read_time`,`read`,`read_it_later`,`remoteId`,`starred`) SELECT `Item`.`id`,`Item`.`title`,`Item`.`description`,`Item`.`clean_description`,`Item`.`link`,`Item`.`image_link`,`Item`.`author`,`Item`.`pub_date`,`Item`.`content`,`Item`.`feed_id`,`Item`.`guid`,`Item`.`read_time`,`Item`.`read`,`Item`.`read_it_later`,`Item`.`remoteId`,0 FROM `Item`""") db.execSQL("""DROP TABLE IF EXISTS `Item`""") @@ -65,4 +62,25 @@ object MigrationFrom2To3 : Migration(2, 3) { db.execSQL("""CREATE INDEX IF NOT EXISTS `index_Item_feed_id` ON `Item` (`feed_id`)""") db.execSQL("""CREATE INDEX IF NOT EXISTS `index_Item_guid` ON `Item` (`guid`)""") } -} \ No newline at end of file +} + +object MigrationFrom3To4 : Migration(3, 4) { + + override fun migrate(db: SupportSQLiteDatabase) { + // add unique index to ItemState.(account_id, remote_id) + db.execSQL("CREATE TABLE IF NOT EXISTS `_new_ItemState` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `read` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `remote_id` TEXT NOT NULL, `account_id` INTEGER NOT NULL, FOREIGN KEY(`account_id`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )") + db.execSQL("INSERT INTO `_new_ItemState` (`id`,`read`,`starred`,`remote_id`,`account_id`) SELECT `id`,`read`,`starred`,`remote_id`,`account_id` FROM `ItemState`") + db.execSQL("DROP TABLE `ItemState`") + db.execSQL("ALTER TABLE `_new_ItemState` RENAME TO `ItemState`") + db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS `index_ItemState_remote_id_account_id` ON `ItemState` (`remote_id`, `account_id`)") + + // remove guid, use remoteId local accounts + db.execSQL("Update Item set remoteId = guid Where remoteId is NULL") + db.execSQL("CREATE TABLE IF NOT EXISTS `_new_Item` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT, `description` TEXT, `clean_description` TEXT, `link` TEXT, `image_link` TEXT, `author` TEXT, `pub_date` INTEGER, `content` TEXT, `feed_id` INTEGER NOT NULL, `read_time` REAL NOT NULL, `read` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `read_it_later` INTEGER NOT NULL, `remoteId` TEXT, FOREIGN KEY(`feed_id`) REFERENCES `Feed`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )") + db.execSQL("INSERT INTO `_new_Item`(`id`, `title`, `description`, `clean_description`, `link`, `image_link`, `author`, `pub_date`, `content`, `feed_id`, `read_time`, `read`, `starred`, `read_it_later`, `remoteId`) SELECT `id`, `title`, `description`, `clean_description`, `link`, `image_link`, `author`, `pub_date`, `content`, `feed_id`, `read_time`, `read`, `starred`, `read_it_later`, `remoteId` FROM `Item`") + db.execSQL("DROP TABLE IF EXISTS `Item`") + db.execSQL("ALTER TABLE `_new_Item` RENAME TO `Item`") + db.execSQL("CREATE INDEX IF NOT EXISTS `index_Item_feed_id` ON `Item` (`feed_id`)") + } + +} diff --git a/db/src/main/java/com/readrops/db/DbModule.kt b/db/src/main/java/com/readrops/db/DbModule.kt index 3a6e5baf..4487f52f 100644 --- a/db/src/main/java/com/readrops/db/DbModule.kt +++ b/db/src/main/java/com/readrops/db/DbModule.kt @@ -7,7 +7,7 @@ val dbModule = module { single(createdAtStart = true) { Room.databaseBuilder(get(), Database::class.java, "readrops-db") - .addMigrations(MigrationFrom1To2, MigrationFrom2To3) + .addMigrations(MigrationFrom1To2, MigrationFrom2To3, MigrationFrom3To4) .build() } } \ No newline at end of file diff --git a/db/src/main/java/com/readrops/db/dao/ItemDao.kt b/db/src/main/java/com/readrops/db/dao/ItemDao.kt index a2b314af..0a918fc7 100644 --- a/db/src/main/java/com/readrops/db/dao/ItemDao.kt +++ b/db/src/main/java/com/readrops/db/dao/ItemDao.kt @@ -64,6 +64,6 @@ abstract class ItemDao : BaseDao { abstract fun selectFeedUnreadItemsCount(query: SupportSQLiteQuery): Flow> - @Query("Select case When :guid In (Select guid From Item Inner Join Feed on Item.feed_id = Feed.id and account_id = :accountId) Then 1 else 0 end") - abstract suspend fun itemExists(guid: String, accountId: Int): Boolean + @Query("Select case When :remoteId In (Select Item.remoteId From Item Inner Join Feed on Item.feed_id = Feed.id and account_id = :accountId) Then 1 else 0 end") + abstract suspend fun itemExists(remoteId: String, accountId: Int): Boolean } \ No newline at end of file diff --git a/db/src/main/java/com/readrops/db/entities/Item.kt b/db/src/main/java/com/readrops/db/entities/Item.kt index 5b11cb7c..78839c0f 100644 --- a/db/src/main/java/com/readrops/db/entities/Item.kt +++ b/db/src/main/java/com/readrops/db/entities/Item.kt @@ -7,26 +7,33 @@ import androidx.room.Ignore import androidx.room.PrimaryKey import org.joda.time.LocalDateTime -@Entity(foreignKeys = [ForeignKey(entity = Feed::class, parentColumns = ["id"], - childColumns = ["feed_id"], onDelete = ForeignKey.CASCADE)]) +@Entity( + foreignKeys = [ + ForeignKey( + entity = Feed::class, + parentColumns = ["id"], + childColumns = ["feed_id"], + onDelete = ForeignKey.CASCADE + ) + ] +) data class Item( - @PrimaryKey(autoGenerate = true) var id: Int = 0, - var title: String? = null, - var description: String? = null, - @ColumnInfo(name = "clean_description") var cleanDescription: String? = null, - var link: String? = null, - @ColumnInfo(name = "image_link") var imageLink: String? = null, - var author: String? = null, - @ColumnInfo(name = "pub_date") var pubDate: LocalDateTime? = null, - var content: String? = null, - @ColumnInfo(name = "feed_id", index = true) var feedId: Int = 0, - @ColumnInfo(index = true) var guid: String? = null, - @ColumnInfo(name = "read_time") var readTime: Double = 0.0, - @ColumnInfo(name = "read") var isRead: Boolean = false, - @ColumnInfo(name = "starred") var isStarred: Boolean = false, - @ColumnInfo(name = "read_it_later") var isReadItLater: Boolean = false, - var remoteId: String? = null, - @Ignore var feedRemoteId: String? = null, + @PrimaryKey(autoGenerate = true) var id: Int = 0, + var title: String? = null, + var description: String? = null, + @ColumnInfo(name = "clean_description") var cleanDescription: String? = null, + var link: String? = null, + @ColumnInfo(name = "image_link") var imageLink: String? = null, + var author: String? = null, + @ColumnInfo(name = "pub_date") var pubDate: LocalDateTime? = null, + var content: String? = null, + @ColumnInfo(name = "feed_id", index = true) var feedId: Int = 0, + @ColumnInfo(name = "read_time") var readTime: Double = 0.0, + @ColumnInfo(name = "read") var isRead: Boolean = false, + @ColumnInfo(name = "starred") var isStarred: Boolean = false, + @ColumnInfo(name = "read_it_later") var isReadItLater: Boolean = false, + var remoteId: String? = null, + @Ignore var feedRemoteId: String? = null, ) : Comparable { val text