Browse Source

bump database version to 11 + add migration

comment in openHelperFactory

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
Marcel Hibbe 10 months ago
parent
commit
8885b999ca

+ 719 - 0
app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/11.json

@@ -0,0 +1,719 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 11,
+    "identityHash": "bc802cadfdef41d3eb94ffbb0729eb89",
+    "entities": [
+      {
+        "tableName": "User",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userId` TEXT, `username` TEXT, `baseUrl` TEXT, `token` TEXT, `displayName` TEXT, `pushConfigurationState` TEXT, `capabilities` TEXT, `serverVersion` TEXT DEFAULT '', `clientCertificate` TEXT, `externalSignalingServer` TEXT, `current` INTEGER NOT NULL, `scheduledForDeletion` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "userId",
+            "columnName": "userId",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "username",
+            "columnName": "username",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "baseUrl",
+            "columnName": "baseUrl",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "token",
+            "columnName": "token",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "displayName",
+            "columnName": "displayName",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "pushConfigurationState",
+            "columnName": "pushConfigurationState",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "capabilities",
+            "columnName": "capabilities",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "serverVersion",
+            "columnName": "serverVersion",
+            "affinity": "TEXT",
+            "notNull": false,
+            "defaultValue": "''"
+          },
+          {
+            "fieldPath": "clientCertificate",
+            "columnName": "clientCertificate",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "externalSignalingServer",
+            "columnName": "externalSignalingServer",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "current",
+            "columnName": "current",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "scheduledForDeletion",
+            "columnName": "scheduledForDeletion",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "ArbitraryStorage",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountIdentifier` INTEGER NOT NULL, `key` TEXT NOT NULL, `object` TEXT, `value` TEXT, PRIMARY KEY(`accountIdentifier`, `key`))",
+        "fields": [
+          {
+            "fieldPath": "accountIdentifier",
+            "columnName": "accountIdentifier",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "key",
+            "columnName": "key",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "storageObject",
+            "columnName": "object",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "value",
+            "columnName": "value",
+            "affinity": "TEXT",
+            "notNull": false
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "accountIdentifier",
+            "key"
+          ]
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "Conversations",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `token` TEXT NOT NULL, `displayName` TEXT NOT NULL, `actorId` TEXT NOT NULL, `actorType` TEXT NOT NULL, `avatarVersion` TEXT NOT NULL, `callFlag` INTEGER NOT NULL, `callRecording` INTEGER NOT NULL, `callStartTime` INTEGER NOT NULL, `canDeleteConversation` INTEGER NOT NULL, `canLeaveConversation` INTEGER NOT NULL, `canStartCall` INTEGER NOT NULL, `description` TEXT NOT NULL, `hasCall` INTEGER NOT NULL, `hasPassword` INTEGER NOT NULL, `isCustomAvatar` INTEGER NOT NULL, `isFavorite` INTEGER NOT NULL, `lastActivity` INTEGER NOT NULL, `lastCommonReadMessage` INTEGER NOT NULL, `lastMessage` TEXT, `lastPing` INTEGER NOT NULL, `lastReadMessage` INTEGER NOT NULL, `lobbyState` TEXT NOT NULL, `lobbyTimer` INTEGER NOT NULL, `messageExpiration` INTEGER NOT NULL, `name` TEXT NOT NULL, `notificationCalls` INTEGER NOT NULL, `notificationLevel` TEXT NOT NULL, `objectType` TEXT NOT NULL, `participantType` TEXT NOT NULL, `permissions` INTEGER NOT NULL, `readOnly` TEXT NOT NULL, `recordingConsent` INTEGER NOT NULL, `remoteServer` TEXT, `remoteToken` TEXT, `sessionId` TEXT NOT NULL, `status` TEXT, `statusClearAt` INTEGER, `statusIcon` TEXT, `statusMessage` TEXT, `type` TEXT NOT NULL, `unreadMention` INTEGER NOT NULL, `unreadMentionDirect` INTEGER NOT NULL, `unreadMessages` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`accountId`) REFERENCES `User`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )",
+        "fields": [
+          {
+            "fieldPath": "internalId",
+            "columnName": "internalId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "accountId",
+            "columnName": "accountId",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "token",
+            "columnName": "token",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "displayName",
+            "columnName": "displayName",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "actorId",
+            "columnName": "actorId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "actorType",
+            "columnName": "actorType",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "avatarVersion",
+            "columnName": "avatarVersion",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "callFlag",
+            "columnName": "callFlag",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "callRecording",
+            "columnName": "callRecording",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "callStartTime",
+            "columnName": "callStartTime",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "canDeleteConversation",
+            "columnName": "canDeleteConversation",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "canLeaveConversation",
+            "columnName": "canLeaveConversation",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "canStartCall",
+            "columnName": "canStartCall",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "description",
+            "columnName": "description",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "hasCall",
+            "columnName": "hasCall",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "hasPassword",
+            "columnName": "hasPassword",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "hasCustomAvatar",
+            "columnName": "isCustomAvatar",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "favorite",
+            "columnName": "isFavorite",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lastActivity",
+            "columnName": "lastActivity",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lastCommonReadMessage",
+            "columnName": "lastCommonReadMessage",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lastMessage",
+            "columnName": "lastMessage",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lastPing",
+            "columnName": "lastPing",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lastReadMessage",
+            "columnName": "lastReadMessage",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lobbyState",
+            "columnName": "lobbyState",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lobbyTimer",
+            "columnName": "lobbyTimer",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "messageExpiration",
+            "columnName": "messageExpiration",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "name",
+            "columnName": "name",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "notificationCalls",
+            "columnName": "notificationCalls",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "notificationLevel",
+            "columnName": "notificationLevel",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "objectType",
+            "columnName": "objectType",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "participantType",
+            "columnName": "participantType",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "permissions",
+            "columnName": "permissions",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "conversationReadOnlyState",
+            "columnName": "readOnly",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "recordingConsentRequired",
+            "columnName": "recordingConsent",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "remoteServer",
+            "columnName": "remoteServer",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "remoteToken",
+            "columnName": "remoteToken",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "sessionId",
+            "columnName": "sessionId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "status",
+            "columnName": "status",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "statusClearAt",
+            "columnName": "statusClearAt",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "statusIcon",
+            "columnName": "statusIcon",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "statusMessage",
+            "columnName": "statusMessage",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "type",
+            "columnName": "type",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unreadMention",
+            "columnName": "unreadMention",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unreadMentionDirect",
+            "columnName": "unreadMentionDirect",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "unreadMessages",
+            "columnName": "unreadMessages",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "internalId"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_Conversations_accountId",
+            "unique": false,
+            "columnNames": [
+              "accountId"
+            ],
+            "orders": [],
+            "createSql": "CREATE INDEX IF NOT EXISTS `index_Conversations_accountId` ON `${TABLE_NAME}` (`accountId`)"
+          }
+        ],
+        "foreignKeys": [
+          {
+            "table": "User",
+            "onDelete": "CASCADE",
+            "onUpdate": "CASCADE",
+            "columns": [
+              "accountId"
+            ],
+            "referencedColumns": [
+              "id"
+            ]
+          }
+        ]
+      },
+      {
+        "tableName": "ChatMessages",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `token` TEXT NOT NULL, `id` INTEGER NOT NULL, `internalConversationId` TEXT NOT NULL, `actorDisplayName` TEXT NOT NULL, `message` TEXT NOT NULL, `actorId` TEXT NOT NULL, `actorType` TEXT NOT NULL, `deleted` INTEGER NOT NULL, `expirationTimestamp` INTEGER NOT NULL, `isReplyable` INTEGER NOT NULL, `lastEditActorDisplayName` TEXT, `lastEditActorId` TEXT, `lastEditActorType` TEXT, `lastEditTimestamp` INTEGER, `markdown` INTEGER, `messageParameters` TEXT, `messageType` TEXT NOT NULL, `parent` INTEGER, `reactions` TEXT, `reactionsSelf` TEXT, `systemMessage` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) ON UPDATE CASCADE ON DELETE CASCADE )",
+        "fields": [
+          {
+            "fieldPath": "internalId",
+            "columnName": "internalId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "accountId",
+            "columnName": "accountId",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "token",
+            "columnName": "token",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "internalConversationId",
+            "columnName": "internalConversationId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "actorDisplayName",
+            "columnName": "actorDisplayName",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "message",
+            "columnName": "message",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "actorId",
+            "columnName": "actorId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "actorType",
+            "columnName": "actorType",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "deleted",
+            "columnName": "deleted",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "expirationTimestamp",
+            "columnName": "expirationTimestamp",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "replyable",
+            "columnName": "isReplyable",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "lastEditActorDisplayName",
+            "columnName": "lastEditActorDisplayName",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lastEditActorId",
+            "columnName": "lastEditActorId",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lastEditActorType",
+            "columnName": "lastEditActorType",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "lastEditTimestamp",
+            "columnName": "lastEditTimestamp",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "renderMarkdown",
+            "columnName": "markdown",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "messageParameters",
+            "columnName": "messageParameters",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "messageType",
+            "columnName": "messageType",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "parentMessageId",
+            "columnName": "parent",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "reactions",
+            "columnName": "reactions",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "reactionsSelf",
+            "columnName": "reactionsSelf",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "systemMessageType",
+            "columnName": "systemMessage",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "timestamp",
+            "columnName": "timestamp",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": false,
+          "columnNames": [
+            "internalId"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_ChatMessages_internalId",
+            "unique": true,
+            "columnNames": [
+              "internalId"
+            ],
+            "orders": [],
+            "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_ChatMessages_internalId` ON `${TABLE_NAME}` (`internalId`)"
+          },
+          {
+            "name": "index_ChatMessages_internalConversationId",
+            "unique": false,
+            "columnNames": [
+              "internalConversationId"
+            ],
+            "orders": [],
+            "createSql": "CREATE INDEX IF NOT EXISTS `index_ChatMessages_internalConversationId` ON `${TABLE_NAME}` (`internalConversationId`)"
+          }
+        ],
+        "foreignKeys": [
+          {
+            "table": "Conversations",
+            "onDelete": "CASCADE",
+            "onUpdate": "CASCADE",
+            "columns": [
+              "internalConversationId"
+            ],
+            "referencedColumns": [
+              "internalId"
+            ]
+          }
+        ]
+      },
+      {
+        "tableName": "ChatBlocks",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `internalConversationId` TEXT NOT NULL, `accountId` INTEGER, `token` TEXT, `oldestMessageId` INTEGER NOT NULL, `newestMessageId` INTEGER NOT NULL, `hasHistory` INTEGER NOT NULL, FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) ON UPDATE CASCADE ON DELETE CASCADE )",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "internalConversationId",
+            "columnName": "internalConversationId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "accountId",
+            "columnName": "accountId",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "token",
+            "columnName": "token",
+            "affinity": "TEXT",
+            "notNull": false
+          },
+          {
+            "fieldPath": "oldestMessageId",
+            "columnName": "oldestMessageId",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "newestMessageId",
+            "columnName": "newestMessageId",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "hasHistory",
+            "columnName": "hasHistory",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "autoGenerate": true,
+          "columnNames": [
+            "id"
+          ]
+        },
+        "indices": [
+          {
+            "name": "index_ChatBlocks_internalConversationId",
+            "unique": false,
+            "columnNames": [
+              "internalConversationId"
+            ],
+            "orders": [],
+            "createSql": "CREATE INDEX IF NOT EXISTS `index_ChatBlocks_internalConversationId` ON `${TABLE_NAME}` (`internalConversationId`)"
+          }
+        ],
+        "foreignKeys": [
+          {
+            "table": "Conversations",
+            "onDelete": "CASCADE",
+            "onUpdate": "CASCADE",
+            "columns": [
+              "internalConversationId"
+            ],
+            "referencedColumns": [
+              "internalId"
+            ]
+          }
+        ]
+      }
+    ],
+    "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, 'bc802cadfdef41d3eb94ffbb0729eb89')"
+    ]
+  }
+}

+ 124 - 0
app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt

@@ -33,6 +33,13 @@ object Migrations {
         }
     }
 
+    val MIGRATION_10_11 = object : Migration(10, 11) {
+        override fun migrate(db: SupportSQLiteDatabase) {
+            Log.i("Migrations", "Migrating 10 to 11")
+            migrateToOfflineSupport(db)
+        }
+    }
+
     fun migrateToRoom(db: SupportSQLiteDatabase) {
         db.execSQL(
             "CREATE TABLE User_new (" +
@@ -51,6 +58,7 @@ object Migrations {
                 "PRIMARY KEY(id)" +
                 ")"
         )
+
         db.execSQL(
             "CREATE TABLE ArbitraryStorage_new (" +
                 "accountIdentifier INTEGER NOT NULL, " +
@@ -110,4 +118,120 @@ object Migrations {
         // Change the table name to the correct one
         db.execSQL("ALTER TABLE ArbitraryStorage_dualPK RENAME TO ArbitraryStorage")
     }
+
+    fun migrateToOfflineSupport(db: SupportSQLiteDatabase) {
+        db.execSQL(
+            "CREATE TABLE IF NOT EXISTS Conversations (" +
+                "`internalId` TEXT NOT NULL, " +
+                "`accountId` INTEGER NOT NULL, " +
+                "`token` TEXT NOT NULL, " +
+                "`displayName` TEXT NOT NULL, " +
+                "`actorId` TEXT NOT NULL, " +
+                "`actorType` TEXT NOT NULL, " +
+                "`avatarVersion` TEXT NOT NULL, " +
+                "`callFlag` INTEGER NOT NULL, " +
+                "`callRecording` INTEGER NOT NULL, " +
+                "`callStartTime` INTEGER NOT NULL, " +
+                "`canDeleteConversation` INTEGER NOT NULL, " +
+                "`canLeaveConversation` INTEGER NOT NULL, " +
+                "`canStartCall` INTEGER NOT NULL, " +
+                "`description` TEXT NOT NULL, " +
+                "`hasCall` INTEGER NOT NULL, " +
+                "`hasPassword` INTEGER NOT NULL, " +
+                "`isCustomAvatar` INTEGER NOT NULL, " +
+                "`isFavorite` INTEGER NOT NULL, " +
+                "`lastActivity` INTEGER NOT NULL, " +
+                "`lastCommonReadMessage` INTEGER NOT NULL, " +
+                "`lastMessage` TEXT, " +
+                "`lastPing` INTEGER NOT NULL, " +
+                "`lastReadMessage` INTEGER NOT NULL, " +
+                "`lobbyState` TEXT NOT NULL, " +
+                "`lobbyTimer` INTEGER NOT NULL, " +
+                "`messageExpiration` INTEGER NOT NULL, " +
+                "`name` TEXT NOT NULL, " +
+                "`notificationCalls` INTEGER NOT NULL, " +
+                "`notificationLevel` TEXT NOT NULL, " +
+                "`objectType` TEXT NOT NULL, " +
+                "`participantType` TEXT NOT NULL, " +
+                "`permissions` INTEGER NOT NULL, " +
+                "`readOnly` TEXT NOT NULL, " +
+                "`recordingConsent` INTEGER NOT NULL, " +
+                "`remoteServer` TEXT, " +
+                "`remoteToken` TEXT, " +
+                "`sessionId` TEXT NOT NULL, " +
+                "`status` TEXT, " +
+                "`statusClearAt` INTEGER, " +
+                "`statusIcon` TEXT, " +
+                "`statusMessage` TEXT, " +
+                "`type` TEXT NOT NULL, " +
+                "`unreadMention` INTEGER NOT NULL, " +
+                "`unreadMentionDirect` INTEGER NOT NULL, " +
+                "`unreadMessages` INTEGER NOT NULL, " +
+                "PRIMARY KEY(`internalId`), " +
+                "FOREIGN KEY(`accountId`) REFERENCES `User`(`id`) " +
+                "ON UPDATE CASCADE ON DELETE CASCADE " +
+                ")"
+        )
+
+        db.execSQL(
+            "CREATE INDEX IF NOT EXISTS `index_Conversations_accountId` ON `Conversations` (`accountId`)"
+        )
+
+        db.execSQL(
+            "CREATE TABLE IF NOT EXISTS ChatMessages (" +
+                "`internalId` TEXT NOT NULL, " +
+                "`accountId` INTEGER NOT NULL, " +
+                "`token` TEXT NOT NULL, " +
+                "`id` INTEGER NOT NULL, " +
+                "`internalConversationId` TEXT NOT NULL, " +
+                "`actorDisplayName` TEXT NOT NULL, " +
+                "`message` TEXT NOT NULL, " +
+                "`actorId` TEXT NOT NULL, " +
+                "`actorType` TEXT NOT NULL, " +
+                "`deleted` INTEGER NOT NULL, " +
+                "`expirationTimestamp` INTEGER NOT NULL, " +
+                "`isReplyable` INTEGER NOT NULL, " +
+                "`lastEditActorDisplayName` TEXT, " +
+                "`lastEditActorId` TEXT, " +
+                "`lastEditActorType` TEXT, " +
+                "`lastEditTimestamp` INTEGER, " +
+                "`markdown` INTEGER, " +
+                "`messageParameters` TEXT, " +
+                "`messageType` TEXT NOT NULL, " +
+                "`parent` INTEGER, " +
+                "`reactions` TEXT, " +
+                "`reactionsSelf` TEXT, " +
+                "`systemMessage` TEXT NOT NULL, " +
+                "`timestamp` INTEGER NOT NULL, " +
+                "PRIMARY KEY(`internalId`), " +
+                "FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) " +
+                "ON UPDATE CASCADE ON DELETE CASCADE " +
+                ")"
+        )
+
+        db.execSQL(
+            "CREATE UNIQUE INDEX IF NOT EXISTS `index_ChatMessages_internalId` ON `ChatMessages` (`internalId`)"
+        )
+
+        db.execSQL(
+            "CREATE INDEX IF NOT EXISTS `index_ChatMessages_internalConversationId` ON `ChatMessages` (`internalConversationId`)"
+        )
+
+        db.execSQL(
+            "CREATE TABLE IF NOT EXISTS ChatBlocks (" +
+                "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
+                "`internalConversationId` TEXT NOT NULL, " +
+                "`accountId` INTEGER, `token` TEXT, " +
+                "`oldestMessageId` INTEGER NOT NULL, " +
+                "`newestMessageId` INTEGER NOT NULL, " +
+                "`hasHistory` INTEGER NOT NULL, " +
+                "FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) " +
+                "ON UPDATE CASCADE ON DELETE CASCADE " +
+                ")"
+        )
+
+        db.execSQL(
+            "CREATE INDEX IF NOT EXISTS `index_ChatBlocks_internalConversationId` ON `ChatBlocks` (`internalConversationId`)"
+        )
+    }
 }

+ 9 - 4
app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt

@@ -49,9 +49,9 @@ import java.util.Locale
         ChatMessageEntity::class,
         ChatBlockEntity::class
     ],
-    version = 10,
+    version = 11,
     autoMigrations = [
-        AutoMigration(from = 9, to = 10)
+        AutoMigration(from = 9, to = 10),
     ],
     exportSchema = true
 )
@@ -108,8 +108,13 @@ abstract class TalkDatabase : RoomDatabase() {
             return Room
                 .databaseBuilder(context.applicationContext, TalkDatabase::class.java, dbName)
                 // comment out openHelperFactory to view the database entries in Android Studio for debugging
-                // .openHelperFactory(factory) // TODO: uncomment when offline support is production ready!!!!!!!
-                .addMigrations(Migrations.MIGRATION_6_8, Migrations.MIGRATION_7_8, Migrations.MIGRATION_8_9)
+                .openHelperFactory(factory)
+                .addMigrations(
+                    Migrations.MIGRATION_6_8,
+                    Migrations.MIGRATION_7_8,
+                    Migrations.MIGRATION_8_9,
+                    Migrations.MIGRATION_10_11
+                )
                 .allowMainThreadQueries()
                 .addCallback(
                     object : RoomDatabase.Callback() {