mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 03:29:28 +01:00
Merge pull request #4846 from nextcloud/event_conversations
Event conversations
This commit is contained in:
commit
52f58372b8
@ -0,0 +1,719 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 14,
|
||||
"identityHash": "506abc931eb3b657cafe6ad1b25f635d",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"fieldPath": "username",
|
||||
"columnName": "username",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "baseUrl",
|
||||
"columnName": "baseUrl",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "token",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "displayName",
|
||||
"columnName": "displayName",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "pushConfigurationState",
|
||||
"columnName": "pushConfigurationState",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "capabilities",
|
||||
"columnName": "capabilities",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "serverVersion",
|
||||
"columnName": "serverVersion",
|
||||
"affinity": "TEXT",
|
||||
"defaultValue": "''"
|
||||
},
|
||||
{
|
||||
"fieldPath": "clientCertificate",
|
||||
"columnName": "clientCertificate",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "externalSignalingServer",
|
||||
"columnName": "externalSignalingServer",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "current",
|
||||
"columnName": "current",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "scheduledForDeletion",
|
||||
"columnName": "scheduledForDeletion",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": true,
|
||||
"columnNames": [
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"fieldPath": "value",
|
||||
"columnName": "value",
|
||||
"affinity": "TEXT"
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"autoGenerate": false,
|
||||
"columnNames": [
|
||||
"accountIdentifier",
|
||||
"key"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"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, `objectId` 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, `hasArchived` 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"
|
||||
},
|
||||
{
|
||||
"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": "objectId",
|
||||
"columnName": "objectId",
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"fieldPath": "remoteToken",
|
||||
"columnName": "remoteToken",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "sessionId",
|
||||
"columnName": "sessionId",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "status",
|
||||
"columnName": "status",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "statusClearAt",
|
||||
"columnName": "statusClearAt",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "statusIcon",
|
||||
"columnName": "statusIcon",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "statusMessage",
|
||||
"columnName": "statusMessage",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"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
|
||||
},
|
||||
{
|
||||
"fieldPath": "hasArchived",
|
||||
"columnName": "hasArchived",
|
||||
"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, `isTemporary` 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, `referenceId` TEXT, `sendingFailed` INTEGER NOT NULL, `silent` INTEGER NOT NULL, `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": "isTemporary",
|
||||
"columnName": "isTemporary",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditActorDisplayName",
|
||||
"columnName": "lastEditActorDisplayName",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditActorId",
|
||||
"columnName": "lastEditActorId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditActorType",
|
||||
"columnName": "lastEditActorType",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "lastEditTimestamp",
|
||||
"columnName": "lastEditTimestamp",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "renderMarkdown",
|
||||
"columnName": "markdown",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageParameters",
|
||||
"columnName": "messageParameters",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "messageType",
|
||||
"columnName": "messageType",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "parentMessageId",
|
||||
"columnName": "parent",
|
||||
"affinity": "INTEGER"
|
||||
},
|
||||
{
|
||||
"fieldPath": "reactions",
|
||||
"columnName": "reactions",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "reactionsSelf",
|
||||
"columnName": "reactionsSelf",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "referenceId",
|
||||
"columnName": "referenceId",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"fieldPath": "sendingFailed",
|
||||
"columnName": "sendingFailed",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "silent",
|
||||
"columnName": "silent",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"fieldPath": "token",
|
||||
"columnName": "token",
|
||||
"affinity": "TEXT"
|
||||
},
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"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, '506abc931eb3b657cafe6ad1b25f635d')"
|
||||
]
|
||||
}
|
||||
}
|
@ -215,6 +215,7 @@ class ChatBlocksDaoTest {
|
||||
lobbyState = ConversationEnums.LobbyState.LOBBY_STATE_MODERATORS_ONLY,
|
||||
lobbyTimer = 0,
|
||||
objectType = ConversationEnums.ObjectType.FILE,
|
||||
objectId = "",
|
||||
statusIcon = null,
|
||||
description = "",
|
||||
displayName = "",
|
||||
|
@ -211,6 +211,7 @@ class ChatMessagesDaoTest {
|
||||
lobbyState = ConversationEnums.LobbyState.LOBBY_STATE_MODERATORS_ONLY,
|
||||
lobbyTimer = 0,
|
||||
objectType = ConversationEnums.ObjectType.FILE,
|
||||
objectId = "",
|
||||
statusIcon = null,
|
||||
description = "",
|
||||
displayName = "",
|
||||
|
@ -38,12 +38,14 @@ import android.view.Gravity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import android.widget.AbsListView
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupMenu
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
@ -51,6 +53,7 @@ import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.PickVisualMediaRequest
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
@ -82,6 +85,7 @@ import coil.request.CachePolicy
|
||||
import coil.request.ImageRequest
|
||||
import coil.target.Target
|
||||
import coil.transform.CircleCropTransformation
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.nextcloud.android.common.ui.color.ColorUtil
|
||||
import com.nextcloud.android.common.ui.theme.utils.ColorRole
|
||||
@ -127,6 +131,7 @@ import com.nextcloud.talk.databinding.ActivityChatBinding
|
||||
import com.nextcloud.talk.events.UserMentionClickEvent
|
||||
import com.nextcloud.talk.events.WebSocketCommunicationEvent
|
||||
import com.nextcloud.talk.extensions.loadAvatarOrImagePreview
|
||||
import com.nextcloud.talk.jobs.DeleteConversationWorker
|
||||
import com.nextcloud.talk.jobs.DownloadFileToCacheWorker
|
||||
import com.nextcloud.talk.jobs.ShareOperationWorker
|
||||
import com.nextcloud.talk.jobs.UploadAndShareFilesWorker
|
||||
@ -210,13 +215,18 @@ import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.HttpURLConnection
|
||||
import java.text.SimpleDateFormat
|
||||
import java.time.Instant
|
||||
import java.time.ZoneId
|
||||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.ExecutionException
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.set
|
||||
import kotlin.math.roundToInt
|
||||
import androidx.activity.result.contract.ActivityResultContracts.PickVisualMedia
|
||||
import com.nextcloud.talk.conversationinfo.viewmodel.ConversationInfoViewModel
|
||||
import com.nextcloud.talk.models.json.participants.Participant
|
||||
|
||||
@AutoInjector(NextcloudTalkApplication::class)
|
||||
class ChatActivity :
|
||||
@ -254,6 +264,8 @@ class ChatActivity :
|
||||
lateinit var networkMonitor: NetworkMonitor
|
||||
|
||||
lateinit var chatViewModel: ChatViewModel
|
||||
|
||||
lateinit var conversationInfoViewModel: ConversationInfoViewModel
|
||||
lateinit var messageInputViewModel: MessageInputViewModel
|
||||
|
||||
private val startSelectContactForResult = registerForActivityResult(
|
||||
@ -324,6 +336,7 @@ class ChatActivity :
|
||||
|
||||
private var conversationVoiceCallMenuItem: MenuItem? = null
|
||||
private var conversationVideoMenuItem: MenuItem? = null
|
||||
private var eventConversationMenuItem: MenuItem? = null
|
||||
|
||||
var webSocketInstance: WebSocketInstance? = null
|
||||
var signalingMessageSender: SignalingMessageSender? = null
|
||||
@ -418,6 +431,8 @@ class ChatActivity :
|
||||
|
||||
chatViewModel = ViewModelProvider(this, viewModelFactory)[ChatViewModel::class.java]
|
||||
|
||||
conversationInfoViewModel = ViewModelProvider(this, viewModelFactory)[ConversationInfoViewModel::class.java]
|
||||
|
||||
val urlForChatting = ApiUtils.getUrlForChat(chatApiVersion, conversationUser?.baseUrl, roomToken)
|
||||
val credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser!!.token)
|
||||
chatViewModel.initData(
|
||||
@ -567,6 +582,7 @@ class ChatActivity :
|
||||
participantPermissions = ParticipantPermissions(spreedCapabilities, currentConversation!!)
|
||||
|
||||
invalidateOptionsMenu()
|
||||
isEventConversation()
|
||||
checkShowCallButtons()
|
||||
checkLobbyState()
|
||||
updateRoomTimerHandler()
|
||||
@ -600,6 +616,7 @@ class ChatActivity :
|
||||
loadAvatarForStatusBar()
|
||||
setupSwipeToReply()
|
||||
setActionBarTitle()
|
||||
isEventConversation()
|
||||
checkShowCallButtons()
|
||||
checkLobbyState()
|
||||
if (currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL &&
|
||||
@ -1889,6 +1906,17 @@ class ChatActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun isEventConversation() {
|
||||
if (currentConversation?.objectType == ConversationEnums.ObjectType.EVENT) {
|
||||
if (eventConversationMenuItem != null) {
|
||||
eventConversationMenuItem?.icon?.alpha = FULLY_OPAQUE_INT
|
||||
eventConversationMenuItem?.isEnabled = true
|
||||
}
|
||||
} else {
|
||||
eventConversationMenuItem?.isEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun isReadOnlyConversation(): Boolean =
|
||||
currentConversation?.conversationReadOnlyState != null &&
|
||||
currentConversation?.conversationReadOnlyState ==
|
||||
@ -2849,12 +2877,19 @@ class ChatActivity :
|
||||
super.onCreateOptionsMenu(menu)
|
||||
menuInflater.inflate(R.menu.menu_conversation, menu)
|
||||
|
||||
if (currentConversation?.objectType == ConversationEnums.ObjectType.EVENT) {
|
||||
eventConversationMenuItem = menu.findItem(R.id.conversation_event)
|
||||
} else {
|
||||
menu.removeItem(R.id.conversation_event)
|
||||
}
|
||||
|
||||
if (conversationUser?.userId == "?") {
|
||||
menu.removeItem(R.id.conversation_info)
|
||||
} else {
|
||||
loadAvatarForStatusBar()
|
||||
setActionBarTitle()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -2871,12 +2906,6 @@ class ChatActivity :
|
||||
searchItem.isVisible = CapabilitiesUtil.isUnifiedSearchAvailable(spreedCapabilities) &&
|
||||
currentConversation!!.remoteServer.isNullOrEmpty()
|
||||
|
||||
if (currentConversation!!.remoteServer != null ||
|
||||
!CapabilitiesUtil.isSharedItemsAvailable(spreedCapabilities)
|
||||
) {
|
||||
menu.removeItem(R.id.shared_items)
|
||||
}
|
||||
|
||||
if (CapabilitiesUtil.isAbleToCall(spreedCapabilities)) {
|
||||
conversationVoiceCallMenuItem = menu.findItem(R.id.conversation_voice_call)
|
||||
conversationVideoMenuItem = menu.findItem(R.id.conversation_video_call)
|
||||
@ -2909,7 +2938,6 @@ class ChatActivity :
|
||||
menu.removeItem(R.id.conversation_voice_call)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -2940,9 +2968,205 @@ class ChatActivity :
|
||||
true
|
||||
}
|
||||
|
||||
R.id.conversation_event -> {
|
||||
val anchorView = findViewById<View>(R.id.conversation_event)
|
||||
showPopupWindow(anchorView)
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun showPopupWindow(anchorView: View) {
|
||||
val popupView = layoutInflater.inflate(R.layout.item_event_schedule, null)
|
||||
|
||||
val titleTextView = popupView.findViewById<TextView>(R.id.event_scheduled)
|
||||
val subtitleTextView = popupView.findViewById<TextView>(R.id.meetingTime)
|
||||
|
||||
val popupWindow = PopupWindow(
|
||||
popupView,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
true
|
||||
)
|
||||
|
||||
popupWindow.isOutsideTouchable = true
|
||||
popupWindow.isFocusable = true
|
||||
popupWindow.showAsDropDown(anchorView, 0, -anchorView.height)
|
||||
|
||||
val meetingStatus = showEventSchedule()
|
||||
subtitleTextView.text = meetingStatus
|
||||
|
||||
deleteEventConversation(meetingStatus, popupWindow, popupView)
|
||||
archiveEventConversation(meetingStatus, popupWindow, popupView)
|
||||
}
|
||||
|
||||
private fun deleteEventConversation(meetingStatus: String, popupWindow: PopupWindow, popupView: View) {
|
||||
val deleteConversation = popupView.findViewById<TextView>(R.id.delete_conversation)
|
||||
if (meetingStatus == context.resources.getString(R.string.nc_meeting_ended) &&
|
||||
currentConversation?.canDeleteConversation == true
|
||||
) {
|
||||
deleteConversation.visibility = View.VISIBLE
|
||||
|
||||
deleteConversation.setOnClickListener {
|
||||
val dialogBuilder = MaterialAlertDialogBuilder(it.context)
|
||||
.setIcon(
|
||||
viewThemeUtils.dialog
|
||||
.colorMaterialAlertDialogIcon(context, R.drawable.ic_delete_black_24dp)
|
||||
)
|
||||
.setTitle(R.string.nc_delete_call)
|
||||
.setMessage(R.string.nc_delete_conversation_more)
|
||||
.setPositiveButton(R.string.nc_delete) { _, _ ->
|
||||
currentConversation?.let { conversation ->
|
||||
deleteConversation(conversation)
|
||||
}
|
||||
}
|
||||
.setNegativeButton(R.string.nc_cancel) { _, _ ->
|
||||
}
|
||||
|
||||
viewThemeUtils.dialog
|
||||
.colorMaterialAlertDialogBackground(it.context, dialogBuilder)
|
||||
val dialog = dialogBuilder.show()
|
||||
viewThemeUtils.platform.colorTextButtons(
|
||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE),
|
||||
dialog.getButton(AlertDialog.BUTTON_NEGATIVE)
|
||||
)
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
} else {
|
||||
deleteConversation.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun archiveEventConversation(meetingStatus: String, popupWindow: PopupWindow, popupView: View) {
|
||||
val archiveConversation = popupView.findViewById<TextView>(R.id.archive_conversation)
|
||||
val unarchiveConversation = popupView.findViewById<TextView>(R.id.unarchive_conversation)
|
||||
if (meetingStatus == context.resources.getString(R.string.nc_meeting_ended) &&
|
||||
(
|
||||
Participant.ParticipantType.MODERATOR == currentConversation?.participantType ||
|
||||
Participant.ParticipantType.OWNER == currentConversation?.participantType
|
||||
)
|
||||
) {
|
||||
if (currentConversation?.hasArchived == false) {
|
||||
unarchiveConversation.visibility = View.GONE
|
||||
archiveConversation.visibility = View.VISIBLE
|
||||
archiveConversation.setOnClickListener {
|
||||
this.lifecycleScope.launch {
|
||||
conversationInfoViewModel.archiveConversation(conversationUser!!, currentConversation?.token!!)
|
||||
Snackbar.make(
|
||||
binding.root,
|
||||
String.format(
|
||||
context.resources.getString(R.string.archived_conversation),
|
||||
currentConversation?.displayName
|
||||
),
|
||||
Snackbar.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
} else {
|
||||
unarchiveConversation.visibility = View.VISIBLE
|
||||
archiveConversation.visibility = View.GONE
|
||||
unarchiveConversation.setOnClickListener {
|
||||
this.lifecycleScope.launch {
|
||||
conversationInfoViewModel.unarchiveConversation(
|
||||
conversationUser!!,
|
||||
currentConversation?.token!!
|
||||
)
|
||||
Snackbar.make(
|
||||
binding.root,
|
||||
String.format(
|
||||
context.resources.getString(R.string.unarchived_conversation),
|
||||
currentConversation?.displayName
|
||||
),
|
||||
Snackbar.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
archiveConversation.visibility = View.GONE
|
||||
unarchiveConversation.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteConversation(conversation: ConversationModel) {
|
||||
val data = Data.Builder()
|
||||
data.putLong(
|
||||
KEY_INTERNAL_USER_ID,
|
||||
conversationUser?.id!!
|
||||
)
|
||||
data.putString(KEY_ROOM_TOKEN, conversation.token)
|
||||
|
||||
val deleteConversationWorker =
|
||||
OneTimeWorkRequest.Builder(DeleteConversationWorker::class.java).setInputData(data.build()).build()
|
||||
WorkManager.getInstance().enqueue(deleteConversationWorker)
|
||||
|
||||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(deleteConversationWorker.id)
|
||||
.observeForever { workInfo: WorkInfo? ->
|
||||
if (workInfo != null) {
|
||||
when (workInfo.state) {
|
||||
WorkInfo.State.SUCCEEDED -> {
|
||||
val successMessage = String.format(
|
||||
context.resources.getString(R.string.deleted_conversation),
|
||||
conversation.displayName
|
||||
)
|
||||
Snackbar.make(binding.root, successMessage, Snackbar.LENGTH_LONG).show()
|
||||
finish()
|
||||
}
|
||||
|
||||
WorkInfo.State.FAILED -> {
|
||||
val errorMessage = context.resources.getString(R.string.nc_common_error_sorry)
|
||||
Snackbar.make(binding.root, errorMessage, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showEventSchedule(): String {
|
||||
val meetingTimeStamp = currentConversation?.objectId ?: ""
|
||||
val status = getMeetingSchedule(meetingTimeStamp)
|
||||
return status
|
||||
}
|
||||
|
||||
private fun getMeetingSchedule(meetingTimeStamp: String): String {
|
||||
val timestamps = meetingTimeStamp.split("#")
|
||||
if (timestamps.size != 2) return context.resources.getString(R.string.nc_invalid_time)
|
||||
|
||||
val startEpoch = timestamps[ZERO_INDEX].toLong()
|
||||
val endEpoch = timestamps[ONE_INDEX].toLong()
|
||||
|
||||
val startDateTime = Instant.ofEpochSecond(startEpoch).atZone(ZoneId.systemDefault())
|
||||
val endDateTime = Instant.ofEpochSecond(endEpoch).atZone(ZoneId.systemDefault())
|
||||
val currentTime = ZonedDateTime.now(ZoneId.systemDefault())
|
||||
|
||||
return when {
|
||||
currentTime.isBefore(startDateTime) -> {
|
||||
val isToday = startDateTime.toLocalDate().isEqual(currentTime.toLocalDate())
|
||||
val isTomorrow = startDateTime.toLocalDate().isEqual(currentTime.toLocalDate().plusDays(1))
|
||||
when {
|
||||
isToday -> String.format(
|
||||
context.resources.getString(R.string.nc_today_meeting),
|
||||
startDateTime.format(DateTimeFormatter.ofPattern("HH:mm"))
|
||||
)
|
||||
|
||||
isTomorrow -> String.format(
|
||||
context.resources.getString(R.string.nc_tomorrow_meeting),
|
||||
startDateTime.format(DateTimeFormatter.ofPattern("HH:mm"))
|
||||
)
|
||||
else -> startDateTime.format(DateTimeFormatter.ofPattern("MMM d, yyyy, HH:mm"))
|
||||
}
|
||||
}
|
||||
currentTime.isAfter(endDateTime) -> context.resources.getString(R.string.nc_meeting_ended)
|
||||
else -> context.resources.getString(R.string.nc_ongoing_meeting)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSharedItems() {
|
||||
val intent = Intent(this, SharedItemsActivity::class.java)
|
||||
intent.putExtra(KEY_CONVERSATION_NAME, currentConversation?.displayName)
|
||||
@ -3800,5 +4024,7 @@ class ChatActivity :
|
||||
const val VOICE_MESSAGE_PLAY_ADD_THRESHOLD = 0.1
|
||||
const val VOICE_MESSAGE_MARK_PLAYED_FACTOR = 20
|
||||
const val OUT_OF_OFFICE_ALPHA = 76
|
||||
const val ZERO_INDEX = 0
|
||||
const val ONE_INDEX = 1
|
||||
}
|
||||
}
|
||||
|
@ -185,6 +185,11 @@ class ConversationInfoEditActivity : BaseActivity() {
|
||||
binding.conversationDescription.isEnabled = false
|
||||
}
|
||||
|
||||
if (conversation?.objectType == ConversationEnums.ObjectType.EVENT) {
|
||||
binding.conversationName.isEnabled = false
|
||||
binding.conversationDescription.isEnabled = false
|
||||
}
|
||||
|
||||
loadConversationAvatar()
|
||||
}
|
||||
|
||||
@ -271,7 +276,9 @@ class ConversationInfoEditActivity : BaseActivity() {
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.save) {
|
||||
saveConversationNameAndDescription()
|
||||
if (conversation?.objectType != ConversationEnums.ObjectType.EVENT) {
|
||||
saveConversationNameAndDescription()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
@ -210,6 +210,7 @@ class ConversationsListActivity :
|
||||
private var conversationItemsWithHeader: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
private val searchableConversationItems: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
private var filterableConversationItems: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
private var nearFutureEventConversationItems: MutableList<AbstractFlexibleItem<*>> = ArrayList()
|
||||
private var searchItem: MenuItem? = null
|
||||
private var chooseAccountItem: MenuItem? = null
|
||||
private var searchView: SearchView? = null
|
||||
@ -519,16 +520,29 @@ class ConversationsListActivity :
|
||||
// Update Conversations
|
||||
conversationItems.clear()
|
||||
conversationItemsWithHeader.clear()
|
||||
nearFutureEventConversationItems.clear()
|
||||
|
||||
for (conversation in list) {
|
||||
if (!futureEvent(conversation)) {
|
||||
addToNearFutureEventConversationItems(conversation)
|
||||
}
|
||||
addToConversationItems(conversation)
|
||||
}
|
||||
|
||||
sortConversations(conversationItems)
|
||||
sortConversations(conversationItemsWithHeader)
|
||||
sortConversations(nearFutureEventConversationItems)
|
||||
|
||||
// Filter Conversations
|
||||
if (!hasFilterEnabled()) filterableConversationItems = conversationItems
|
||||
filterConversation()
|
||||
adapter?.updateDataSet(filterableConversationItems, false)
|
||||
if (!hasFilterEnabled() && searchBehaviorSubject.value == false) {
|
||||
adapter?.updateDataSet(nearFutureEventConversationItems, false)
|
||||
} else {
|
||||
// Filter Conversations
|
||||
if (!hasFilterEnabled()) {
|
||||
filterableConversationItems = conversationItems
|
||||
}
|
||||
filterConversation()
|
||||
adapter?.updateDataSet(filterableConversationItems, false)
|
||||
}
|
||||
Handler().postDelayed({ checkToShowUnreadBubble() }, UNREAD_BUBBLE_DELAY.toLong())
|
||||
|
||||
// Fetch Open Conversations
|
||||
@ -550,6 +564,26 @@ class ConversationsListActivity :
|
||||
return false
|
||||
}
|
||||
|
||||
private fun futureEvent(conversation: ConversationModel): Boolean {
|
||||
if (!conversation.objectId.contains("#")) {
|
||||
return false
|
||||
}
|
||||
return conversation.objectType == ConversationEnums.ObjectType.EVENT &&
|
||||
(conversation.objectId.split("#")[0].toLong() - (System.currentTimeMillis() / LONG_1000)) >
|
||||
AGE_THRESHOLD_FOR_EVENT_CONVERSATIONS
|
||||
}
|
||||
|
||||
fun showOnlyNearFutureEvents() {
|
||||
sortConversations(nearFutureEventConversationItems)
|
||||
adapter?.updateDataSet(nearFutureEventConversationItems, false)
|
||||
adapter?.smoothScrollToPosition(0)
|
||||
}
|
||||
|
||||
private fun addToNearFutureEventConversationItems(conversation: ConversationModel) {
|
||||
val conversationItem = ConversationItem(conversation, currentUser!!, this, null, viewThemeUtils)
|
||||
nearFutureEventConversationItems.add(conversationItem)
|
||||
}
|
||||
|
||||
fun filterConversation() {
|
||||
val accountId = UserIdUtils.getIdForUser(currentUser)
|
||||
filterState[FilterConversationFragment.UNREAD] = (
|
||||
@ -837,15 +871,19 @@ class ConversationsListActivity :
|
||||
|
||||
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
|
||||
adapter?.setHeadersShown(false)
|
||||
searchBehaviorSubject.onNext(false)
|
||||
if (!hasFilterEnabled()) filterableConversationItems = conversationItemsWithHeader
|
||||
adapter?.updateDataSet(filterableConversationItems, false)
|
||||
if (!hasFilterEnabled()) {
|
||||
adapter?.updateDataSet(nearFutureEventConversationItems, false)
|
||||
} else {
|
||||
filterableConversationItems = conversationItems
|
||||
}
|
||||
adapter?.hideAllHeaders()
|
||||
if (searchHelper != null) {
|
||||
// cancel any pending searches
|
||||
searchHelper!!.cancelSearch()
|
||||
}
|
||||
binding.swipeRefreshLayoutView.isRefreshing = false
|
||||
searchBehaviorSubject.onNext(false)
|
||||
binding.swipeRefreshLayoutView.isEnabled = true
|
||||
searchView!!.onActionViewCollapsed()
|
||||
|
||||
@ -2114,5 +2152,7 @@ class ConversationsListActivity :
|
||||
const val NOTIFICATION_WARNING_DATE_NOT_SET = 0L
|
||||
const val OFFSET_HEIGHT_DIVIDER: Int = 3
|
||||
const val ROOM_TYPE_ONE_ONE = "1"
|
||||
private const val AGE_THRESHOLD_FOR_EVENT_CONVERSATIONS: Long = 57600
|
||||
const val LONG_1000: Long = 1000
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ fun ConversationModel.asEntity() =
|
||||
unreadMention = unreadMention,
|
||||
lastMessage = lastMessage?.let { LoganSquare.serialize(lastMessage) },
|
||||
objectType = objectType,
|
||||
objectId = objectId,
|
||||
notificationLevel = notificationLevel,
|
||||
conversationReadOnlyState = conversationReadOnlyState,
|
||||
lobbyState = lobbyState,
|
||||
@ -85,6 +86,7 @@ fun ConversationEntity.asModel() =
|
||||
lastMessage = lastMessage?.let
|
||||
{ LoganSquare.parse(lastMessage, ChatMessageJson::class.java) },
|
||||
objectType = objectType,
|
||||
objectId = objectId,
|
||||
notificationLevel = notificationLevel,
|
||||
conversationReadOnlyState = conversationReadOnlyState,
|
||||
lobbyState = lobbyState,
|
||||
@ -135,6 +137,7 @@ fun Conversation.asEntity(accountId: Long) =
|
||||
unreadMention = unreadMention,
|
||||
lastMessage = lastMessage?.let { LoganSquare.serialize(lastMessage) },
|
||||
objectType = objectType,
|
||||
objectId = objectId,
|
||||
notificationLevel = notificationLevel,
|
||||
conversationReadOnlyState = conversationReadOnlyState,
|
||||
lobbyState = lobbyState,
|
||||
|
@ -78,6 +78,7 @@ data class ConversationEntity(
|
||||
@ColumnInfo(name = "notificationCalls") var notificationCalls: Int = 0,
|
||||
@ColumnInfo(name = "notificationLevel") var notificationLevel: ConversationEnums.NotificationLevel,
|
||||
@ColumnInfo(name = "objectType") var objectType: ConversationEnums.ObjectType,
|
||||
@ColumnInfo(name = "objectId") var objectId: String,
|
||||
@ColumnInfo(name = "participantType") var participantType: Participant.ParticipantType,
|
||||
@ColumnInfo(name = "permissions") var permissions: Int = 0,
|
||||
@ColumnInfo(name = "readOnly") var conversationReadOnlyState: ConversationEnums.ConversationReadOnlyState,
|
||||
|
@ -55,6 +55,13 @@ object Migrations {
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_13_14 = object : Migration(13, 14) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
Log.i("Migrations", "Migrating 13 to 14")
|
||||
addObjectId(db)
|
||||
}
|
||||
}
|
||||
|
||||
fun migrateToRoom(db: SupportSQLiteDatabase) {
|
||||
db.execSQL(
|
||||
"CREATE TABLE User_new (" +
|
||||
@ -265,6 +272,17 @@ object Migrations {
|
||||
}
|
||||
}
|
||||
|
||||
fun addObjectId(db: SupportSQLiteDatabase) {
|
||||
try {
|
||||
db.execSQL(
|
||||
"ALTER TABLE Conversations " +
|
||||
"ADD COLUMN objectId TEXT NOT NULL DEFAULT '';"
|
||||
)
|
||||
} catch (e: SQLException) {
|
||||
Log.i("Migrations", "Something went wrong when adding column objectId to table Conversations")
|
||||
}
|
||||
}
|
||||
|
||||
fun addTempMessagesSupport(db: SupportSQLiteDatabase) {
|
||||
try {
|
||||
db.execSQL(
|
||||
|
@ -49,9 +49,9 @@ import java.util.Locale
|
||||
ChatMessageEntity::class,
|
||||
ChatBlockEntity::class
|
||||
],
|
||||
version = 13,
|
||||
version = 14,
|
||||
autoMigrations = [
|
||||
AutoMigration(from = 9, to = 11)
|
||||
AutoMigration(from = 9, to = 10)
|
||||
],
|
||||
exportSchema = true
|
||||
)
|
||||
@ -115,7 +115,8 @@ abstract class TalkDatabase : RoomDatabase() {
|
||||
Migrations.MIGRATION_8_9,
|
||||
Migrations.MIGRATION_10_11,
|
||||
Migrations.MIGRATION_11_12,
|
||||
Migrations.MIGRATION_12_13
|
||||
Migrations.MIGRATION_12_13,
|
||||
Migrations.MIGRATION_13_14
|
||||
)
|
||||
.allowMainThreadQueries()
|
||||
.addCallback(
|
||||
|
@ -33,6 +33,7 @@ class ConversationModel(
|
||||
var unreadMention: Boolean = false,
|
||||
var lastMessage: ChatMessageJson? = null,
|
||||
var objectType: ConversationEnums.ObjectType,
|
||||
var objectId: String = "",
|
||||
var notificationLevel: ConversationEnums.NotificationLevel,
|
||||
var conversationReadOnlyState: ConversationEnums.ConversationReadOnlyState,
|
||||
var lobbyState: ConversationEnums.LobbyState,
|
||||
@ -66,6 +67,7 @@ class ConversationModel(
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@Suppress("LongMethod")
|
||||
fun mapToConversationModel(conversation: Conversation, user: User): ConversationModel {
|
||||
return ConversationModel(
|
||||
internalId = user.id!!.toString() + "@" + conversation.token,
|
||||
@ -88,6 +90,7 @@ class ConversationModel(
|
||||
unreadMention = conversation.unreadMention,
|
||||
lastMessage = conversation.lastMessage,
|
||||
objectType = conversation.objectType.let { ConversationEnums.ObjectType.valueOf(it.name) },
|
||||
objectId = conversation.objectId,
|
||||
notificationLevel = conversation.notificationLevel.let {
|
||||
ConversationEnums.NotificationLevel.valueOf(
|
||||
it.name
|
||||
|
@ -79,6 +79,9 @@ data class Conversation(
|
||||
@JsonField(name = ["objectType"], typeConverter = ConversationObjectTypeConverter::class)
|
||||
var objectType: ConversationEnums.ObjectType = ConversationEnums.ObjectType.DEFAULT,
|
||||
|
||||
@JsonField(name = ["objectId"])
|
||||
var objectId: String = "",
|
||||
|
||||
@JsonField(name = ["notificationLevel"], typeConverter = EnumNotificationLevelConverter::class)
|
||||
var notificationLevel: ConversationEnums.NotificationLevel = ConversationEnums.NotificationLevel.DEFAULT,
|
||||
|
||||
|
@ -43,6 +43,7 @@ class ConversationEnums {
|
||||
DEFAULT,
|
||||
SHARE_PASSWORD,
|
||||
FILE,
|
||||
ROOM
|
||||
ROOM,
|
||||
EVENT
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ class ConversationObjectTypeConverter : StringBasedTypeConverter<ConversationEnu
|
||||
"share:password" -> ConversationEnums.ObjectType.SHARE_PASSWORD
|
||||
"room" -> ConversationEnums.ObjectType.ROOM
|
||||
"file" -> ConversationEnums.ObjectType.FILE
|
||||
"event" -> ConversationEnums.ObjectType.EVENT
|
||||
else -> ConversationEnums.ObjectType.DEFAULT
|
||||
}
|
||||
}
|
||||
@ -28,6 +29,7 @@ class ConversationObjectTypeConverter : StringBasedTypeConverter<ConversationEnu
|
||||
ConversationEnums.ObjectType.SHARE_PASSWORD -> "share:password"
|
||||
ConversationEnums.ObjectType.ROOM -> "room"
|
||||
ConversationEnums.ObjectType.FILE -> "file"
|
||||
ConversationEnums.ObjectType.EVENT -> "event"
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
package com.nextcloud.talk.ui.dialog
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
@ -99,6 +100,14 @@ class FilterConversationFragment : DialogFragment() {
|
||||
}
|
||||
|
||||
binding.buttonClose.setOnClickListener {
|
||||
val noFiltersActive = !(
|
||||
filterState[MENTION] == true ||
|
||||
filterState[UNREAD] == true ||
|
||||
filterState[ARCHIVE] == true
|
||||
)
|
||||
if (noFiltersActive) {
|
||||
(requireActivity() as ConversationsListActivity).showOnlyNearFutureEvents()
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
@ -130,6 +139,18 @@ class FilterConversationFragment : DialogFragment() {
|
||||
(requireActivity() as ConversationsListActivity).filterConversation()
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
super.onDismiss(dialog)
|
||||
val noFiltersActive = !(
|
||||
filterState[MENTION] == true ||
|
||||
filterState[UNREAD] == true ||
|
||||
filterState[ARCHIVE] == true
|
||||
)
|
||||
if (noFiltersActive) {
|
||||
(requireActivity() as ConversationsListActivity).showOnlyNearFutureEvents()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val FILTER_STATE_ARG = "FILTER_STATE_ARG"
|
||||
|
||||
|
19
app/src/main/res/drawable/baseline_calendar_today_24.xml
Normal file
19
app/src/main/res/drawable/baseline_calendar_today_24.xml
Normal file
@ -0,0 +1,19 @@
|
||||
<!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2025 Sowjanya Kota <sowjanya.kch@gmail.com>
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="20dp"
|
||||
android:tint="#000000"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24"
|
||||
android:width="20dp">
|
||||
|
||||
<path android:fillColor="@android:color/white"
|
||||
android:pathData="M20,3h-1L19,1h-2v2L7,3L7,1L5,1v2L4,3c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,5c0,-1.1 -0.9,-2 -2,-2zM20,21L4,21L4,8h16v13z"/>
|
||||
|
||||
</vector>
|
60
app/src/main/res/layout/item_event_schedule.xml
Normal file
60
app/src/main/res/layout/item_event_schedule.xml
Normal file
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Nextcloud Talk - Android Client
|
||||
~
|
||||
~ SPDX-FileCopyrightText: 2025 Sowjanya Kota <sowjanya.kota@gmail.com>
|
||||
~ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
-->
|
||||
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:background="@color/popup_menu_color">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/event_scheduled"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/nc_event_schedule"
|
||||
android:textStyle="bold"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/meetingTime"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="16sp"
|
||||
tools:text="Meeting at 8:00 pm"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/delete_conversation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/nc_delete_call"
|
||||
android:textColor="@android:color/holo_red_dark"
|
||||
android:visibility = "gone"
|
||||
android:textSize="18sp"
|
||||
android:paddingTop="24dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/archive_conversation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/archive_conversation"
|
||||
android:visibility = "gone"
|
||||
android:textSize="18sp"
|
||||
android:paddingTop="24dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/unarchive_conversation"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/unarchive_conversation"
|
||||
android:visibility = "gone"
|
||||
android:textSize="18sp"
|
||||
android:paddingTop="24dp"/>
|
||||
</LinearLayout>
|
@ -8,36 +8,45 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/conversation_event"
|
||||
android:icon="@drawable/baseline_calendar_today_24"
|
||||
android:orderInCategory="0"
|
||||
android:title="@string/nc_event_conversation_menu"
|
||||
app:showAsAction="ifRoom">
|
||||
|
||||
</item>
|
||||
|
||||
<item
|
||||
android:id="@+id/conversation_voice_call"
|
||||
android:icon="@drawable/ic_call_white_24dp"
|
||||
android:orderInCategory="0"
|
||||
android:orderInCategory="1"
|
||||
android:title="@string/nc_conversation_menu_voice_call"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/conversation_video_call"
|
||||
android:icon="@drawable/ic_videocam_white_24px"
|
||||
android:orderInCategory="1"
|
||||
android:orderInCategory="2"
|
||||
android:title="@string/nc_conversation_menu_video_call"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/conversation_search"
|
||||
android:icon="@drawable/ic_search_white_24dp"
|
||||
android:orderInCategory="2"
|
||||
android:orderInCategory="3"
|
||||
android:title="@string/nc_search"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/conversation_info"
|
||||
android:orderInCategory="3"
|
||||
android:orderInCategory="4"
|
||||
android:title="@string/nc_conversation_menu_conversation_info"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/shared_items"
|
||||
android:orderInCategory="4"
|
||||
android:orderInCategory="5"
|
||||
android:title="@string/nc_shared_items"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
@ -12,6 +12,7 @@
|
||||
<color name="colorPrimaryDark">#006AA3</color>
|
||||
<color name="colorAccent">@color/colorPrimary</color>
|
||||
<color name="disabled_text">#ff6F6F6F</color>
|
||||
<color name="popup_menu_color">#FF37474F</color>
|
||||
|
||||
<!-- App bar -->
|
||||
<color name="appbar">#1E1E1E</color>
|
||||
|
@ -13,6 +13,8 @@
|
||||
<color name="disabled_text">#ff888888</color>
|
||||
<color name="textColorOnPrimaryBackground">#ffffff</color> <!-- white/black depending on primary color -->
|
||||
<color name="nc_login_text_color">#B3FFFFFF</color>
|
||||
<color name="popup_menu_color">#FF607D8B</color>
|
||||
|
||||
|
||||
<!-- App bar -->
|
||||
<color name="appbar">@android:color/white</color>
|
||||
|
@ -242,8 +242,14 @@ How to translate with transifex:
|
||||
<string name="nc_rename">Rename conversation</string>
|
||||
<string name="nc_rename_confirm">Rename</string>
|
||||
<string name="nc_delete_call">Delete conversation</string>
|
||||
<string name="nc_event_schedule">Schedule</string>
|
||||
<string name="nc_delete">Delete</string>
|
||||
<string name="nc_delete_all">Delete all</string>
|
||||
<string name="nc_ongoing_meeting">Ongoing meeting</string>
|
||||
<string name="nc_meeting_ended">Meeting ended</string>
|
||||
<string name="nc_invalid_time">Invalid time</string>
|
||||
<string name="nc_today_meeting">Today at %1$s</string>
|
||||
<string name="nc_tomorrow_meeting">Tomorrow at %1$s</string>
|
||||
<string name="nc_delete_conversation_more">If you delete the conversation, it will also be deleted for all other participants.</string>
|
||||
|
||||
<string name="nc_new_conversation">New conversation</string>
|
||||
@ -403,6 +409,7 @@ How to translate with transifex:
|
||||
<string name="nc_date_header_today">Today</string>
|
||||
<string name="nc_conversation_menu_voice_call">Voice call</string>
|
||||
<string name="nc_conversation_menu_video_call">Video call</string>
|
||||
<string name="nc_event_conversation_menu">Event conversation menu</string>
|
||||
<string name="nc_conversation_menu_conversation_info">Conversation info</string>
|
||||
<string name="nc_new_messages">Unread messages</string>
|
||||
<string name="nc_sent_a_gif" formatted="true">%1$s sent a GIF.</string>
|
||||
@ -843,4 +850,5 @@ How to translate with transifex:
|
||||
<string name="no_conversations_archived">No archived conversations</string>
|
||||
<string name="archived_conversation">Archived %1$s</string>
|
||||
<string name="unarchived_conversation">Unarchived %1$s</string>
|
||||
<string name="conversation_archived">Conversation is archived</string>
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user