From 94dde9f433c31c1473480facdfeb17f1eb209fcc Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Tue, 10 Jan 2023 11:05:03 +0100 Subject: [PATCH] initial set of Room entities --- build.gradle | 24 + .../1.json | 1165 +++++++++++++++++ .../database/ConversationsDatabase.java | 65 + .../android/database/Converters.java | 30 + .../database/entity/AccountEntity.java | 35 + .../database/entity/BlockedItemEntity.java | 30 + .../android/database/entity/ChatEntity.java | 35 + .../android/database/entity/DiscoEntity.java | 27 + .../database/entity/DiscoExtensionEntity.java | 24 + .../entity/DiscoExtensionFieldEntity.java | 26 + .../DiscoExtensionFieldValueEntity.java | 26 + .../database/entity/DiscoFeatureEntity.java | 26 + .../database/entity/DiscoIdentityEntity.java | 28 + .../database/entity/MessageEntity.java | 38 + .../database/entity/MessagePartEntity.java | 33 + .../database/entity/MessageVersionEntity.java | 39 + .../database/entity/PresenceEntity.java | 53 + .../database/entity/ReactionEntity.java | 35 + .../database/entity/RosterItemEntity.java | 37 + .../entity/RosterItemGroupEntity.java | 26 + .../android/database/model/ChatType.java | 8 + .../android/database/model/Connection.java | 14 + .../android/database/model/Modification.java | 8 + .../android/database/model/PartType.java | 6 + .../android/database/model/PresenceShow.java | 8 + .../android/database/model/PresenceType.java | 7 + .../android/database/model/Proxy.java | 22 + .../android/database/model/Subscription.java | 8 + 28 files changed, 1883 insertions(+) create mode 100644 schemas/im.conversations.android.database.ConversationsDatabase/1.json create mode 100644 src/main/java/im/conversations/android/database/ConversationsDatabase.java create mode 100644 src/main/java/im/conversations/android/database/Converters.java create mode 100644 src/main/java/im/conversations/android/database/entity/AccountEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/BlockedItemEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/ChatEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/DiscoEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/DiscoExtensionEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldValueEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/DiscoFeatureEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/DiscoIdentityEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/MessageEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/MessagePartEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/MessageVersionEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/PresenceEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/ReactionEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/RosterItemEntity.java create mode 100644 src/main/java/im/conversations/android/database/entity/RosterItemGroupEntity.java create mode 100644 src/main/java/im/conversations/android/database/model/ChatType.java create mode 100644 src/main/java/im/conversations/android/database/model/Connection.java create mode 100644 src/main/java/im/conversations/android/database/model/Modification.java create mode 100644 src/main/java/im/conversations/android/database/model/PartType.java create mode 100644 src/main/java/im/conversations/android/database/model/PresenceShow.java create mode 100644 src/main/java/im/conversations/android/database/model/PresenceType.java create mode 100644 src/main/java/im/conversations/android/database/model/Proxy.java create mode 100644 src/main/java/im/conversations/android/database/model/Subscription.java diff --git a/build.gradle b/build.gradle index 26a8a2bac..5784f3e89 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,22 @@ spotless { } dependencies { + + // Conversations 3.0 dependencies + + def room_version = "2.4.3" + + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8' + + implementation "androidx.room:room-runtime:$room_version" + annotationProcessor "androidx.room:room-compiler:$room_version" + implementation "androidx.room:room-guava:$room_version" + + + + // legacy dependencies. Ideally everything below should be carefully reviewed and eventually moved up + + implementation 'androidx.viewpager:viewpager:1.0.0' playstoreImplementation('com.google.firebase:firebase-messaging:23.1.1') { @@ -110,6 +126,13 @@ android { def appName = "Conversations" resValue "string", "app_name", appName buildConfigField "String", "APP_NAME", "\"$appName\"" + + javaCompileOptions { + annotationProcessorOptions { + arguments += ["room.schemaLocation": "$projectDir/schemas".toString()] + } + } + } splits { @@ -128,6 +151,7 @@ android { } compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } diff --git a/schemas/im.conversations.android.database.ConversationsDatabase/1.json b/schemas/im.conversations.android.database.ConversationsDatabase/1.json new file mode 100644 index 000000000..2af6825e8 --- /dev/null +++ b/schemas/im.conversations.android.database.ConversationsDatabase/1.json @@ -0,0 +1,1165 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "c78cb993428558b863fd91c46b608926", + "entities": [ + { + "tableName": "account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `address` TEXT NOT NULL, `resource` TEXT, `randomSeed` BLOB, `enabled` INTEGER NOT NULL, `rosterVersion` TEXT, `hostname` TEXT, `port` INTEGER, `directTls` INTEGER, `proxytype` TEXT, `proxyhostname` TEXT, `proxyport` INTEGER)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resource", + "columnName": "resource", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "randomSeed", + "columnName": "randomSeed", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "rosterVersion", + "columnName": "rosterVersion", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "connection.hostname", + "columnName": "hostname", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "connection.port", + "columnName": "port", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "connection.directTls", + "columnName": "directTls", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "proxy.type", + "columnName": "proxytype", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "proxy.hostname", + "columnName": "proxyhostname", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "proxy.port", + "columnName": "proxyport", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_account_address", + "unique": true, + "columnNames": [ + "address" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_account_address` ON `${TABLE_NAME}` (`address`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "blocked", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_blocked_accountId_address", + "unique": true, + "columnNames": [ + "accountId", + "address" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_blocked_accountId_address` ON `${TABLE_NAME}` (`accountId`, `address`)" + } + ], + "foreignKeys": [ + { + "table": "account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "chat", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `type` TEXT, `archived` INTEGER NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "archived", + "columnName": "archived", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_chat_accountId_address", + "unique": true, + "columnNames": [ + "accountId", + "address" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_chat_accountId_address` ON `${TABLE_NAME}` (`accountId`, `address`)" + } + ], + "foreignKeys": [ + { + "table": "account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "disco", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `capsHash` BLOB, `caps2Hash` BLOB, `caps2Algorithm` TEXT, `accountId` INTEGER NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "capsHash", + "columnName": "capsHash", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "caps2Hash", + "columnName": "caps2Hash", + "affinity": "BLOB", + "notNull": false + }, + { + "fieldPath": "caps2Algorithm", + "columnName": "caps2Algorithm", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_disco_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_disco_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "disco_ext", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `discoId` INTEGER NOT NULL, FOREIGN KEY(`discoId`) REFERENCES `disco`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "discoId", + "columnName": "discoId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_disco_ext_discoId", + "unique": false, + "columnNames": [ + "discoId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_disco_ext_discoId` ON `${TABLE_NAME}` (`discoId`)" + } + ], + "foreignKeys": [ + { + "table": "disco", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "discoId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "disco_ext_field", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `extensionId` INTEGER NOT NULL, `field` TEXT, FOREIGN KEY(`extensionId`) REFERENCES `disco_ext`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "extensionId", + "columnName": "extensionId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "field", + "columnName": "field", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_disco_ext_field_extensionId", + "unique": false, + "columnNames": [ + "extensionId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_disco_ext_field_extensionId` ON `${TABLE_NAME}` (`extensionId`)" + } + ], + "foreignKeys": [ + { + "table": "disco_ext", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "extensionId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "disco_ext_field_value", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `fieldId` INTEGER NOT NULL, `value` TEXT, FOREIGN KEY(`fieldId`) REFERENCES `disco_ext`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "fieldId", + "columnName": "fieldId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_disco_ext_field_value_fieldId", + "unique": false, + "columnNames": [ + "fieldId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_disco_ext_field_value_fieldId` ON `${TABLE_NAME}` (`fieldId`)" + } + ], + "foreignKeys": [ + { + "table": "disco_ext", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "fieldId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "disco_feature", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `discoId` INTEGER NOT NULL, `feature` TEXT NOT NULL, FOREIGN KEY(`discoId`) REFERENCES `disco`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "discoId", + "columnName": "discoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "feature", + "columnName": "feature", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_disco_feature_discoId", + "unique": false, + "columnNames": [ + "discoId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_disco_feature_discoId` ON `${TABLE_NAME}` (`discoId`)" + } + ], + "foreignKeys": [ + { + "table": "disco", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "discoId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "disco_identity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `discoId` INTEGER NOT NULL, `category` TEXT, `type` TEXT, `name` TEXT, FOREIGN KEY(`discoId`) REFERENCES `disco`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "discoId", + "columnName": "discoId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_disco_identity_discoId", + "unique": false, + "columnNames": [ + "discoId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_disco_identity_discoId` ON `${TABLE_NAME}` (`discoId`)" + } + ], + "foreignKeys": [ + { + "table": "disco", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "discoId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "message", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `chatId` INTEGER NOT NULL, `receivedAt` INTEGER, `sentAt` INTEGER, `bareTo` TEXT, `toResource` TEXT, `bareFrom` TEXT, `fromResource` TEXT, `occupantId` TEXT, `messageId` TEXT, `stanzaId` TEXT, FOREIGN KEY(`chatId`) REFERENCES `chat`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "chatId", + "columnName": "chatId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "receivedAt", + "columnName": "receivedAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "sentAt", + "columnName": "sentAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bareTo", + "columnName": "bareTo", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "toResource", + "columnName": "toResource", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "bareFrom", + "columnName": "bareFrom", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fromResource", + "columnName": "fromResource", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "occupantId", + "columnName": "occupantId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "messageId", + "columnName": "messageId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "stanzaId", + "columnName": "stanzaId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_message_chatId", + "unique": false, + "columnNames": [ + "chatId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_chatId` ON `${TABLE_NAME}` (`chatId`)" + } + ], + "foreignKeys": [ + { + "table": "chat", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "chatId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "message_part", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `messageVersionId` INTEGER NOT NULL, `language` TEXT, `type` TEXT, `body` TEXT, `url` TEXT, FOREIGN KEY(`messageVersionId`) REFERENCES `message_version`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "messageVersionId", + "columnName": "messageVersionId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "language", + "columnName": "language", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "body", + "columnName": "body", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_message_part_messageVersionId", + "unique": false, + "columnNames": [ + "messageVersionId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_part_messageVersionId` ON `${TABLE_NAME}` (`messageVersionId`)" + } + ], + "foreignKeys": [ + { + "table": "message_version", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "messageVersionId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "message_version", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `messageEntityId` INTEGER NOT NULL, `messageId` TEXT, `stanzaId` TEXT, `modification` TEXT, `modifiedBy` TEXT, `modifiedByResource` TEXT, `occupantId` TEXT, `receivedAt` INTEGER, FOREIGN KEY(`messageId`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "messageEntityId", + "columnName": "messageEntityId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "messageId", + "columnName": "messageId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "stanzaId", + "columnName": "stanzaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "modification", + "columnName": "modification", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "modifiedBy", + "columnName": "modifiedBy", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "modifiedByResource", + "columnName": "modifiedByResource", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "occupantId", + "columnName": "occupantId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "receivedAt", + "columnName": "receivedAt", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_message_version_messageId", + "unique": false, + "columnNames": [ + "messageId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_version_messageId` ON `${TABLE_NAME}` (`messageId`)" + } + ], + "foreignKeys": [ + { + "table": "message", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "messageId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "presence", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `resource` TEXT, `type` TEXT, `show` TEXT, `status` TEXT, `vCardPhoto` TEXT, `occupantId` TEXT, `mucUserAffiliation` TEXT, `mucUserRole` TEXT, `mucUserJid` TEXT, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "resource", + "columnName": "resource", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show", + "columnName": "show", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "status", + "columnName": "status", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "vCardPhoto", + "columnName": "vCardPhoto", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "occupantId", + "columnName": "occupantId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mucUserAffiliation", + "columnName": "mucUserAffiliation", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mucUserRole", + "columnName": "mucUserRole", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mucUserJid", + "columnName": "mucUserJid", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_presence_accountId_address_resource", + "unique": true, + "columnNames": [ + "accountId", + "address", + "resource" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_presence_accountId_address_resource` ON `${TABLE_NAME}` (`accountId`, `address`, `resource`)" + } + ], + "foreignKeys": [ + { + "table": "account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "message_reaction", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `messageEntityId` INTEGER NOT NULL, `stanzaId` TEXT, `messageId` TEXT, `reactionBy` TEXT, `reactionByResource` TEXT, `occupantId` TEXT, `receivedAt` INTEGER, `reaction` TEXT, FOREIGN KEY(`messageEntityId`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "messageEntityId", + "columnName": "messageEntityId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "stanzaId", + "columnName": "stanzaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "messageId", + "columnName": "messageId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "reactionBy", + "columnName": "reactionBy", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "reactionByResource", + "columnName": "reactionByResource", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "occupantId", + "columnName": "occupantId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "receivedAt", + "columnName": "receivedAt", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_message_reaction_messageEntityId", + "unique": false, + "columnNames": [ + "messageEntityId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_message_reaction_messageEntityId` ON `${TABLE_NAME}` (`messageEntityId`)" + } + ], + "foreignKeys": [ + { + "table": "message", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "messageEntityId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "roster", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `subscription` TEXT, `ask` INTEGER NOT NULL, `name` TEXT, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "subscription", + "columnName": "subscription", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "ask", + "columnName": "ask", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_roster_accountId_address", + "unique": true, + "columnNames": [ + "accountId", + "address" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_roster_accountId_address` ON `${TABLE_NAME}` (`accountId`, `address`)" + } + ], + "foreignKeys": [ + { + "table": "account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "roster_group", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `rosterItemId` INTEGER NOT NULL, `name` TEXT, FOREIGN KEY(`rosterItemId`) REFERENCES `roster`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "rosterItemId", + "columnName": "rosterItemId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [ + { + "name": "index_roster_group_rosterItemId", + "unique": false, + "columnNames": [ + "rosterItemId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_roster_group_rosterItemId` ON `${TABLE_NAME}` (`rosterItemId`)" + } + ], + "foreignKeys": [ + { + "table": "roster", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "rosterItemId" + ], + "referencedColumns": [ + "id" + ] + } + ] + } + ], + "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, 'c78cb993428558b863fd91c46b608926')" + ] + } +} \ No newline at end of file diff --git a/src/main/java/im/conversations/android/database/ConversationsDatabase.java b/src/main/java/im/conversations/android/database/ConversationsDatabase.java new file mode 100644 index 000000000..9a7b2600f --- /dev/null +++ b/src/main/java/im/conversations/android/database/ConversationsDatabase.java @@ -0,0 +1,65 @@ +package im.conversations.android.database; + +import android.content.Context; +import androidx.room.Database; +import androidx.room.Room; +import androidx.room.RoomDatabase; +import androidx.room.TypeConverters; +import im.conversations.android.database.entity.AccountEntity; +import im.conversations.android.database.entity.BlockedItemEntity; +import im.conversations.android.database.entity.ChatEntity; +import im.conversations.android.database.entity.DiscoEntity; +import im.conversations.android.database.entity.DiscoExtensionEntity; +import im.conversations.android.database.entity.DiscoExtensionFieldEntity; +import im.conversations.android.database.entity.DiscoExtensionFieldValueEntity; +import im.conversations.android.database.entity.DiscoFeatureEntity; +import im.conversations.android.database.entity.DiscoIdentityEntity; +import im.conversations.android.database.entity.MessageEntity; +import im.conversations.android.database.entity.MessagePartEntity; +import im.conversations.android.database.entity.MessageVersionEntity; +import im.conversations.android.database.entity.PresenceEntity; +import im.conversations.android.database.entity.ReactionEntity; +import im.conversations.android.database.entity.RosterItemEntity; +import im.conversations.android.database.entity.RosterItemGroupEntity; + +@Database( + entities = { + AccountEntity.class, + BlockedItemEntity.class, + ChatEntity.class, + DiscoEntity.class, + DiscoExtensionEntity.class, + DiscoExtensionFieldEntity.class, + DiscoExtensionFieldValueEntity.class, + DiscoFeatureEntity.class, + DiscoIdentityEntity.class, + MessageEntity.class, + MessagePartEntity.class, + MessageVersionEntity.class, + PresenceEntity.class, + ReactionEntity.class, + RosterItemEntity.class, + RosterItemGroupEntity.class + }, + version = 1) +@TypeConverters(Converters.class) +public abstract class ConversationsDatabase extends RoomDatabase { + + private static volatile ConversationsDatabase INSTANCE = null; + + public static ConversationsDatabase getInstance(final Context context) { + if (INSTANCE != null) { + return INSTANCE; + } + synchronized (ConversationsDatabase.class) { + if (INSTANCE != null) { + return INSTANCE; + } + final Context application = context.getApplicationContext(); + INSTANCE = + Room.databaseBuilder(application, ConversationsDatabase.class, "conversations") + .build(); + return INSTANCE; + } + } +} diff --git a/src/main/java/im/conversations/android/database/Converters.java b/src/main/java/im/conversations/android/database/Converters.java new file mode 100644 index 000000000..1e9e140cf --- /dev/null +++ b/src/main/java/im/conversations/android/database/Converters.java @@ -0,0 +1,30 @@ +package im.conversations.android.database; + +import androidx.room.TypeConverter; +import eu.siacs.conversations.xmpp.Jid; +import java.time.Instant; + +public final class Converters { + + private Converters() {} + + @TypeConverter + public static Instant toInstant(final Long timestamp) { + return timestamp == null ? null : Instant.ofEpochMilli(timestamp); + } + + @TypeConverter + public static Long fromInstant(final Instant instant) { + return instant == null ? null : instant.getEpochSecond() * 1000; + } + + @TypeConverter + public static Jid toJid(final String input) { + return input == null ? null : Jid.ofEscaped(input); + } + + @TypeConverter + public static String fromJid(final Jid jid) { + return jid == null ? null : jid.toEscapedString(); + } +} diff --git a/src/main/java/im/conversations/android/database/entity/AccountEntity.java b/src/main/java/im/conversations/android/database/entity/AccountEntity.java new file mode 100644 index 000000000..aef2c1b5c --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/AccountEntity.java @@ -0,0 +1,35 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Embedded; +import androidx.room.Entity; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import im.conversations.android.database.model.Connection; +import im.conversations.android.database.model.Proxy; + +@Entity( + tableName = "account", + indices = { + @Index( + value = {"address"}, + unique = true) + }) +public class AccountEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public String address; + public String resource; + public byte[] randomSeed; + + public boolean enabled; + + public String rosterVersion; + + @Embedded public Connection connection; + + @Embedded(prefix = "proxy") + public Proxy proxy; +} diff --git a/src/main/java/im/conversations/android/database/entity/BlockedItemEntity.java b/src/main/java/im/conversations/android/database/entity/BlockedItemEntity.java new file mode 100644 index 000000000..f2ba56922 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/BlockedItemEntity.java @@ -0,0 +1,30 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; + +@Entity( + tableName = "blocked", + foreignKeys = + @ForeignKey( + entity = AccountEntity.class, + parentColumns = {"id"}, + childColumns = {"accountId"}, + onDelete = ForeignKey.CASCADE), + indices = { + @Index( + value = {"accountId", "address"}, + unique = true) + }) +public class BlockedItemEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long accountId; + + @NonNull public String address; +} diff --git a/src/main/java/im/conversations/android/database/entity/ChatEntity.java b/src/main/java/im/conversations/android/database/entity/ChatEntity.java new file mode 100644 index 000000000..188ddf5a8 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/ChatEntity.java @@ -0,0 +1,35 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import im.conversations.android.database.model.ChatType; + +@Entity( + tableName = "chat", + foreignKeys = + @ForeignKey( + entity = AccountEntity.class, + parentColumns = {"id"}, + childColumns = {"accountId"}, + onDelete = ForeignKey.CASCADE), + indices = { + @Index( + value = {"accountId", "address"}, + unique = true) + }) +public class ChatEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long accountId; + + @NonNull public String address; + + public ChatType type; + + public boolean archived; +} diff --git a/src/main/java/im/conversations/android/database/entity/DiscoEntity.java b/src/main/java/im/conversations/android/database/entity/DiscoEntity.java new file mode 100644 index 000000000..5a597a9c9 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/DiscoEntity.java @@ -0,0 +1,27 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; + +@Entity( + tableName = "disco", + foreignKeys = + @ForeignKey( + entity = AccountEntity.class, + parentColumns = {"id"}, + childColumns = {"accountId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = {"accountId"})}) +public class DiscoEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + public byte[] capsHash; + public byte[] caps2Hash; + public String caps2Algorithm; + @NonNull Long accountId; +} diff --git a/src/main/java/im/conversations/android/database/entity/DiscoExtensionEntity.java b/src/main/java/im/conversations/android/database/entity/DiscoExtensionEntity.java new file mode 100644 index 000000000..777c63ee5 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/DiscoExtensionEntity.java @@ -0,0 +1,24 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; + +@Entity( + tableName = "disco_ext", + foreignKeys = + @ForeignKey( + entity = DiscoEntity.class, + parentColumns = {"id"}, + childColumns = {"discoId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = {"discoId"})}) +public class DiscoExtensionEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long discoId; +} diff --git a/src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldEntity.java b/src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldEntity.java new file mode 100644 index 000000000..5a63b7916 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldEntity.java @@ -0,0 +1,26 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; + +@Entity( + tableName = "disco_ext_field", + foreignKeys = + @ForeignKey( + entity = DiscoExtensionEntity.class, + parentColumns = {"id"}, + childColumns = {"extensionId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = {"extensionId"})}) +public class DiscoExtensionFieldEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long extensionId; + + public String field; +} diff --git a/src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldValueEntity.java b/src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldValueEntity.java new file mode 100644 index 000000000..b2847c236 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldValueEntity.java @@ -0,0 +1,26 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; + +@Entity( + tableName = "disco_ext_field_value", + foreignKeys = + @ForeignKey( + entity = DiscoExtensionEntity.class, + parentColumns = {"id"}, + childColumns = {"fieldId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = {"fieldId"})}) +public class DiscoExtensionFieldValueEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long fieldId; + + public String value; +} diff --git a/src/main/java/im/conversations/android/database/entity/DiscoFeatureEntity.java b/src/main/java/im/conversations/android/database/entity/DiscoFeatureEntity.java new file mode 100644 index 000000000..6366ca6a5 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/DiscoFeatureEntity.java @@ -0,0 +1,26 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; + +@Entity( + tableName = "disco_feature", + foreignKeys = + @ForeignKey( + entity = DiscoEntity.class, + parentColumns = {"id"}, + childColumns = {"discoId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = {"discoId"})}) +public class DiscoFeatureEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long discoId; + + @NonNull public String feature; +} diff --git a/src/main/java/im/conversations/android/database/entity/DiscoIdentityEntity.java b/src/main/java/im/conversations/android/database/entity/DiscoIdentityEntity.java new file mode 100644 index 000000000..e3a000486 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/DiscoIdentityEntity.java @@ -0,0 +1,28 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; + +@Entity( + tableName = "disco_identity", + foreignKeys = + @ForeignKey( + entity = DiscoEntity.class, + parentColumns = {"id"}, + childColumns = {"discoId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = {"discoId"})}) +public class DiscoIdentityEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long discoId; + + public String category; + public String type; + public String name; +} diff --git a/src/main/java/im/conversations/android/database/entity/MessageEntity.java b/src/main/java/im/conversations/android/database/entity/MessageEntity.java new file mode 100644 index 000000000..48e037860 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/MessageEntity.java @@ -0,0 +1,38 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import java.time.Instant; + +@Entity( + tableName = "message", + foreignKeys = + @ForeignKey( + entity = ChatEntity.class, + parentColumns = {"id"}, + childColumns = {"chatId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = "chatId")}) +public class MessageEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long chatId; + + public Instant receivedAt; + public Instant sentAt; + + public String bareTo; + public String toResource; + public String bareFrom; + public String fromResource; + + public String occupantId; + + public String messageId; + public String stanzaId; +} diff --git a/src/main/java/im/conversations/android/database/entity/MessagePartEntity.java b/src/main/java/im/conversations/android/database/entity/MessagePartEntity.java new file mode 100644 index 000000000..160f0b2ef --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/MessagePartEntity.java @@ -0,0 +1,33 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import im.conversations.android.database.model.PartType; + +@Entity( + tableName = "message_part", + foreignKeys = + @ForeignKey( + entity = MessageVersionEntity.class, + parentColumns = {"id"}, + childColumns = {"messageVersionId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = "messageVersionId")}) +public class MessagePartEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long messageVersionId; + + public String language; + + public PartType type; + + public String body; + + public String url; +} diff --git a/src/main/java/im/conversations/android/database/entity/MessageVersionEntity.java b/src/main/java/im/conversations/android/database/entity/MessageVersionEntity.java new file mode 100644 index 000000000..afec5b14c --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/MessageVersionEntity.java @@ -0,0 +1,39 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import im.conversations.android.database.model.Modification; +import java.time.Instant; + +@Entity( + tableName = "message_version", + foreignKeys = + @ForeignKey( + entity = MessageEntity.class, + parentColumns = {"id"}, + childColumns = {"messageId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = "messageId")}) +public class MessageVersionEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public long messageEntityId; + public String messageId; + public String stanzaId; + public Modification modification; + public String modifiedBy; + public String modifiedByResource; + public String occupantId; + Instant receivedAt; + + // the version order is determined by the receivedAt + // the actual display time and display order comes from the parent MessageEntity + // the original has a receivedAt = null and stanzaId = null and inherits it's timestamp from + // it's parent + +} diff --git a/src/main/java/im/conversations/android/database/entity/PresenceEntity.java b/src/main/java/im/conversations/android/database/entity/PresenceEntity.java new file mode 100644 index 000000000..102cf39a6 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/PresenceEntity.java @@ -0,0 +1,53 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import eu.siacs.conversations.entities.MucOptions; +import eu.siacs.conversations.xmpp.Jid; +import im.conversations.android.database.model.PresenceShow; +import im.conversations.android.database.model.PresenceType; + +@Entity( + tableName = "presence", + foreignKeys = + @ForeignKey( + entity = AccountEntity.class, + parentColumns = {"id"}, + childColumns = {"accountId"}, + onDelete = ForeignKey.CASCADE), + indices = { + @Index( + value = {"accountId", "address", "resource"}, + unique = true) + }) +public class PresenceEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long accountId; + + @NonNull public String address; + + @Nullable public String resource; + + @Nullable public PresenceType type; + + @Nullable public PresenceShow show; + + @Nullable public String status; + + @Nullable public String vCardPhoto; + + @Nullable public String occupantId; + + @Nullable public MucOptions.Affiliation mucUserAffiliation; + + @Nullable public MucOptions.Role mucUserRole; + + @Nullable public Jid mucUserJid; +} diff --git a/src/main/java/im/conversations/android/database/entity/ReactionEntity.java b/src/main/java/im/conversations/android/database/entity/ReactionEntity.java new file mode 100644 index 000000000..85b463449 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/ReactionEntity.java @@ -0,0 +1,35 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import java.time.Instant; + +@Entity( + tableName = "message_reaction", + foreignKeys = + @ForeignKey( + entity = MessageEntity.class, + parentColumns = {"id"}, + childColumns = {"messageEntityId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = "messageEntityId")}) +public class ReactionEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long messageEntityId; + + public String stanzaId; + public String messageId; + public String reactionBy; + public String reactionByResource; + public String occupantId; + + public Instant receivedAt; + + public String reaction; +} diff --git a/src/main/java/im/conversations/android/database/entity/RosterItemEntity.java b/src/main/java/im/conversations/android/database/entity/RosterItemEntity.java new file mode 100644 index 000000000..399ad6933 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/RosterItemEntity.java @@ -0,0 +1,37 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; +import im.conversations.android.database.model.Subscription; + +@Entity( + tableName = "roster", + foreignKeys = + @ForeignKey( + entity = AccountEntity.class, + parentColumns = {"id"}, + childColumns = {"accountId"}, + onDelete = ForeignKey.CASCADE), + indices = { + @Index( + value = {"accountId", "address"}, + unique = true) + }) +public class RosterItemEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long accountId; + + @NonNull public String address; + + public Subscription subscription; + + public boolean ask; + + public String name; +} diff --git a/src/main/java/im/conversations/android/database/entity/RosterItemGroupEntity.java b/src/main/java/im/conversations/android/database/entity/RosterItemGroupEntity.java new file mode 100644 index 000000000..e9caa6912 --- /dev/null +++ b/src/main/java/im/conversations/android/database/entity/RosterItemGroupEntity.java @@ -0,0 +1,26 @@ +package im.conversations.android.database.entity; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Index; +import androidx.room.PrimaryKey; + +@Entity( + tableName = "roster_group", + foreignKeys = + @ForeignKey( + entity = RosterItemEntity.class, + parentColumns = {"id"}, + childColumns = {"rosterItemId"}, + onDelete = ForeignKey.CASCADE), + indices = {@Index(value = "rosterItemId")}) +public class RosterItemGroupEntity { + + @PrimaryKey(autoGenerate = true) + public Long id; + + @NonNull public Long rosterItemId; + + public String name; +} diff --git a/src/main/java/im/conversations/android/database/model/ChatType.java b/src/main/java/im/conversations/android/database/model/ChatType.java new file mode 100644 index 000000000..dbccadda1 --- /dev/null +++ b/src/main/java/im/conversations/android/database/model/ChatType.java @@ -0,0 +1,8 @@ +package im.conversations.android.database.model; + +public enum ChatType { + INDIVIDUAL, + MUC, + MUC_PM, + MULTICAST +} diff --git a/src/main/java/im/conversations/android/database/model/Connection.java b/src/main/java/im/conversations/android/database/model/Connection.java new file mode 100644 index 000000000..23b461062 --- /dev/null +++ b/src/main/java/im/conversations/android/database/model/Connection.java @@ -0,0 +1,14 @@ +package im.conversations.android.database.model; + +public class Connection { + + public final String hostname; + public final int port; + public final boolean directTls; + + public Connection(final String hostname, final int port, final boolean directTls) { + this.hostname = hostname; + this.port = port; + this.directTls = directTls; + } +} diff --git a/src/main/java/im/conversations/android/database/model/Modification.java b/src/main/java/im/conversations/android/database/model/Modification.java new file mode 100644 index 000000000..c2c3bf7a0 --- /dev/null +++ b/src/main/java/im/conversations/android/database/model/Modification.java @@ -0,0 +1,8 @@ +package im.conversations.android.database.model; + +public enum Modification { + ORIGINAl, + EDIT, // XEP-0308: Last Message Correction + RETRACTION, // XEP-0424: Message Retraction + MODERATION // XEP-0425: Message Moderation +} diff --git a/src/main/java/im/conversations/android/database/model/PartType.java b/src/main/java/im/conversations/android/database/model/PartType.java new file mode 100644 index 000000000..2f4f11d68 --- /dev/null +++ b/src/main/java/im/conversations/android/database/model/PartType.java @@ -0,0 +1,6 @@ +package im.conversations.android.database.model; + +public enum PartType { + TEXT, + FILE +} diff --git a/src/main/java/im/conversations/android/database/model/PresenceShow.java b/src/main/java/im/conversations/android/database/model/PresenceShow.java new file mode 100644 index 000000000..9ef056429 --- /dev/null +++ b/src/main/java/im/conversations/android/database/model/PresenceShow.java @@ -0,0 +1,8 @@ +package im.conversations.android.database.model; + +public enum PresenceShow { + CHAT, + AWAY, + XA, + DND +} diff --git a/src/main/java/im/conversations/android/database/model/PresenceType.java b/src/main/java/im/conversations/android/database/model/PresenceType.java new file mode 100644 index 000000000..81104c2fc --- /dev/null +++ b/src/main/java/im/conversations/android/database/model/PresenceType.java @@ -0,0 +1,7 @@ +package im.conversations.android.database.model; + +public enum PresenceType { + UNAVAILABLE, + ERROR, + SUBSCRIBE +} diff --git a/src/main/java/im/conversations/android/database/model/Proxy.java b/src/main/java/im/conversations/android/database/model/Proxy.java new file mode 100644 index 000000000..2fc0aef1a --- /dev/null +++ b/src/main/java/im/conversations/android/database/model/Proxy.java @@ -0,0 +1,22 @@ +package im.conversations.android.database.model; + +import com.google.common.base.Preconditions; + +public class Proxy { + + public final Type type; + public final String hostname; + public final int port; + + public Proxy(final Type type, final String hostname, final int port) { + Preconditions.checkNotNull(type); + Preconditions.checkNotNull(hostname); + this.type = type; + this.hostname = hostname; + this.port = port; + } + + public enum Type { + SOCKS5 + } +} diff --git a/src/main/java/im/conversations/android/database/model/Subscription.java b/src/main/java/im/conversations/android/database/model/Subscription.java new file mode 100644 index 000000000..3002b0377 --- /dev/null +++ b/src/main/java/im/conversations/android/database/model/Subscription.java @@ -0,0 +1,8 @@ +package im.conversations.android.database.model; + +public enum Subscription { + NONE, + TO, + FROM, + BOTH +}