From c337d5087b7b6f39ea1f83cb0117ee6d3accf7f7 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 20 May 2025 11:13:08 +0200 Subject: [PATCH 01/10] add endpoints and add isSensitive parameter Signed-off-by: sowjanyakch --- .../java/com/nextcloud/talk/api/NcApiCoroutines.kt | 12 ++++++++++++ .../data/database/mappers/ConversationMapUtils.kt | 9 ++++++--- .../talk/data/database/model/ConversationEntity.kt | 3 ++- .../talk/models/domain/ConversationModel.kt | 7 +++++-- .../talk/models/json/conversations/Conversation.kt | 5 ++++- 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt index 876e66e00..6efa73b04 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt +++ b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt @@ -185,6 +185,18 @@ interface NcApiCoroutines { @Url url: String ): GenericOverall + @POST + suspend fun markConversationAsSensitive( + @Header("Authorization") authorization:String, + @Url url:String + ): GenericOverall + + @DELETE + suspend fun markConversationAsInsensitive( + @Header("Authorization") authorization:String, + @Url url:String + ): GenericOverall + @FormUrlEncoded @POST suspend fun notificationCalls( diff --git a/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt b/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt index 23151b7a4..66dc7c801 100644 --- a/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt @@ -61,7 +61,8 @@ fun ConversationModel.asEntity() = recordingConsentRequired = recordingConsentRequired, remoteServer = remoteServer, remoteToken = remoteToken, - hasArchived = hasArchived + hasArchived = hasArchived, + isSensitive = isSensitive ) fun ConversationEntity.asModel() = @@ -113,7 +114,8 @@ fun ConversationEntity.asModel() = recordingConsentRequired = recordingConsentRequired, remoteServer = remoteServer, remoteToken = remoteToken, - hasArchived = hasArchived + hasArchived = hasArchived, + isSensitive = isSensitive ) fun Conversation.asEntity(accountId: Long) = @@ -164,5 +166,6 @@ fun Conversation.asEntity(accountId: Long) = recordingConsentRequired = recordingConsentRequired, remoteServer = remoteServer, remoteToken = remoteToken, - hasArchived = hasArchived + hasArchived = hasArchived, + isSensitive = isSensitive ) diff --git a/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt b/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt index 106c7e7a8..2eeeea549 100644 --- a/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt @@ -94,7 +94,8 @@ data class ConversationEntity( @ColumnInfo(name = "unreadMention") var unreadMention: Boolean = false, @ColumnInfo(name = "unreadMentionDirect") var unreadMentionDirect: Boolean, @ColumnInfo(name = "unreadMessages") var unreadMessages: Int = 0, - @ColumnInfo(name = "hasArchived") var hasArchived: Boolean = false + @ColumnInfo(name = "hasArchived") var hasArchived: Boolean = false, + @ColumnInfo(name = "isSensitive") var isSensitive:Boolean = false // missing/not needed: attendeeId // missing/not needed: attendeePin // missing/not needed: attendeePermissions diff --git a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt index 5e1f845d3..4901994c4 100644 --- a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt +++ b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt @@ -61,9 +61,11 @@ class ConversationModel( var remoteServer: String? = null, var remoteToken: String? = null, var hasArchived: Boolean = false, + var isSensitive: Boolean = false, // attributes that don't come from API. This should be changed?! - var password: String? = null + var password: String? = null, + ) { companion object { @@ -125,7 +127,8 @@ class ConversationModel( recordingConsentRequired = conversation.recordingConsentRequired, remoteServer = conversation.remoteServer, remoteToken = conversation.remoteToken, - hasArchived = conversation.hasArchived + hasArchived = conversation.hasArchived, + isSensitive = conversation.isSensitive ) } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt index f16db4a78..147ff69de 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt @@ -165,5 +165,8 @@ data class Conversation( var remoteToken: String? = "", @JsonField(name = ["isArchived"]) - var hasArchived: Boolean = false + var hasArchived: Boolean = false, + + @JsonField(name = ["isSensitive"]) + var isSensitive: Boolean = false ) : Parcelable From b4de86b84eb831b38fc86dc73d2a37f8184fd5c1 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 20 May 2025 11:32:24 +0200 Subject: [PATCH 02/10] add database migration Signed-off-by: sowjanyakch --- .../data/database/model/ConversationEntity.kt | 2 +- .../talk/data/source/local/Migrations.kt | 19 +++++++++++++++++++ .../talk/data/source/local/TalkDatabase.kt | 5 +++-- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt b/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt index 2eeeea549..d37691981 100644 --- a/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt @@ -95,7 +95,7 @@ data class ConversationEntity( @ColumnInfo(name = "unreadMentionDirect") var unreadMentionDirect: Boolean, @ColumnInfo(name = "unreadMessages") var unreadMessages: Int = 0, @ColumnInfo(name = "hasArchived") var hasArchived: Boolean = false, - @ColumnInfo(name = "isSensitive") var isSensitive:Boolean = false + @ColumnInfo(name = "isSensitive") var isSensitive: Boolean = false // missing/not needed: attendeeId // missing/not needed: attendeePin // missing/not needed: attendeePermissions diff --git a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt index af8089edf..5bef18556 100644 --- a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt +++ b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt @@ -62,6 +62,13 @@ object Migrations { } } + val MIGRATION_14_15 = object : Migration(14, 15) { + override fun migrate(db: SupportSQLiteDatabase) { + Log.i("Migrations", "Migrating 14 to 15") + addisSensitive(db) + } + } + fun migrateToRoom(db: SupportSQLiteDatabase) { db.execSQL( "CREATE TABLE User_new (" + @@ -283,6 +290,18 @@ object Migrations { } } + + fun addisSensitive(db: SupportSQLiteDatabase) { + try { + db.execSQL( + "ALTER TABLE Conversations " + + "ADD COLUMN isSensitive INTEGER NOT NULL DEFAULT 0;" + ) + } catch (e: SQLException) { + Log.i("Migrations", "Something went wrong when adding column isSensitive to table Conversations") + } + } + fun addTempMessagesSupport(db: SupportSQLiteDatabase) { try { db.execSQL( diff --git a/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt b/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt index d86a9f4c0..96f192511 100644 --- a/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt +++ b/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt @@ -49,7 +49,7 @@ import java.util.Locale ChatMessageEntity::class, ChatBlockEntity::class ], - version = 14, + version = 15, autoMigrations = [ AutoMigration(from = 9, to = 10) ], @@ -116,7 +116,8 @@ abstract class TalkDatabase : RoomDatabase() { Migrations.MIGRATION_10_11, Migrations.MIGRATION_11_12, Migrations.MIGRATION_12_13, - Migrations.MIGRATION_13_14 + Migrations.MIGRATION_13_14, + Migrations.MIGRATION_14_15 ) .allowMainThreadQueries() .addCallback( From d899824ebcce91a98e62b08622954e1cff08bad3 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 20 May 2025 12:23:10 +0200 Subject: [PATCH 03/10] add layout and modify the variable name Signed-off-by: sowjanyakch --- .../15.json | 725 ++++++++++++++++++ .../database/mappers/ConversationMapUtils.kt | 6 +- .../data/database/model/ConversationEntity.kt | 2 +- .../talk/data/source/local/Migrations.kt | 4 +- .../talk/models/domain/ConversationModel.kt | 4 +- .../models/json/conversations/Conversation.kt | 2 +- .../res/layout/item_notification_settings.xml | 41 + app/src/main/res/values/strings.xml | 2 + 8 files changed, 777 insertions(+), 9 deletions(-) create mode 100644 app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/15.json diff --git a/app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/15.json b/app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/15.json new file mode 100644 index 000000000..f271e119f --- /dev/null +++ b/app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/15.json @@ -0,0 +1,725 @@ +{ + "formatVersion": 1, + "database": { + "version": 15, + "identityHash": "acac3fd21e35762b90f65f213be38ccd", + "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, `hasSensitive` 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 + }, + { + "fieldPath": "hasSensitive", + "columnName": "hasSensitive", + "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, 'acac3fd21e35762b90f65f213be38ccd')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt b/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt index 66dc7c801..5353bbf7c 100644 --- a/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt @@ -62,7 +62,7 @@ fun ConversationModel.asEntity() = remoteServer = remoteServer, remoteToken = remoteToken, hasArchived = hasArchived, - isSensitive = isSensitive + hasSensitive = hasSensitive ) fun ConversationEntity.asModel() = @@ -115,7 +115,7 @@ fun ConversationEntity.asModel() = remoteServer = remoteServer, remoteToken = remoteToken, hasArchived = hasArchived, - isSensitive = isSensitive + hasSensitive = hasSensitive ) fun Conversation.asEntity(accountId: Long) = @@ -167,5 +167,5 @@ fun Conversation.asEntity(accountId: Long) = remoteServer = remoteServer, remoteToken = remoteToken, hasArchived = hasArchived, - isSensitive = isSensitive + hasSensitive = hasSensitive ) diff --git a/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt b/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt index d37691981..dd71050bb 100644 --- a/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt @@ -95,7 +95,7 @@ data class ConversationEntity( @ColumnInfo(name = "unreadMentionDirect") var unreadMentionDirect: Boolean, @ColumnInfo(name = "unreadMessages") var unreadMessages: Int = 0, @ColumnInfo(name = "hasArchived") var hasArchived: Boolean = false, - @ColumnInfo(name = "isSensitive") var isSensitive: Boolean = false + @ColumnInfo(name = "hasSensitive") var hasSensitive: Boolean = false // missing/not needed: attendeeId // missing/not needed: attendeePin // missing/not needed: attendeePermissions diff --git a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt index 5bef18556..28d57e953 100644 --- a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt +++ b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt @@ -295,10 +295,10 @@ object Migrations { try { db.execSQL( "ALTER TABLE Conversations " + - "ADD COLUMN isSensitive INTEGER NOT NULL DEFAULT 0;" + "ADD COLUMN hasSensitive INTEGER NOT NULL DEFAULT 0;" ) } catch (e: SQLException) { - Log.i("Migrations", "Something went wrong when adding column isSensitive to table Conversations") + Log.i("Migrations", "Something went wrong when adding column hasSensitive to table Conversations") } } diff --git a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt index 4901994c4..fbfbde129 100644 --- a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt +++ b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt @@ -61,7 +61,7 @@ class ConversationModel( var remoteServer: String? = null, var remoteToken: String? = null, var hasArchived: Boolean = false, - var isSensitive: Boolean = false, + var hasSensitive: Boolean = false, // attributes that don't come from API. This should be changed?! var password: String? = null, @@ -128,7 +128,7 @@ class ConversationModel( remoteServer = conversation.remoteServer, remoteToken = conversation.remoteToken, hasArchived = conversation.hasArchived, - isSensitive = conversation.isSensitive + hasSensitive = conversation.hasSensitive ) } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt index 147ff69de..3bb542a75 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt @@ -168,5 +168,5 @@ data class Conversation( var hasArchived: Boolean = false, @JsonField(name = ["isSensitive"]) - var isSensitive: Boolean = false + var hasSensitive: Boolean = false ) : Parcelable diff --git a/app/src/main/res/layout/item_notification_settings.xml b/app/src/main/res/layout/item_notification_settings.xml index 6c8299912..1e439b11d 100644 --- a/app/src/main/res/layout/item_notification_settings.xml +++ b/app/src/main/res/layout/item_notification_settings.xml @@ -113,4 +113,45 @@ android:checked="true" android:clickable="false" /> + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6b23dff0a..a1acd71e2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -344,6 +344,8 @@ How to translate with transifex: Notify when mentioned Never notify Call notifications + Sensitive conversation + Hide message text Important conversation Notifications in this conversation will override Do Not Disturb settings From 98d5b3da728f62ba0421d9a8fc6d90c8d118b501 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 20 May 2025 12:57:58 +0200 Subject: [PATCH 04/10] modify repo, repo implementation and viewModel Signed-off-by: sowjanyakch --- .../viewmodel/ConversationInfoViewModel.kt | 43 +++++++++++++++++++ .../conversations/ConversationsRepository.kt | 4 ++ .../ConversationsRepositoryImpl.kt | 18 ++++++++ .../java/com/nextcloud/talk/utils/ApiUtils.kt | 4 ++ 4 files changed, 69 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt index 0874b17bd..a977a22f1 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt @@ -134,6 +134,14 @@ class ConversationInfoViewModel @Inject constructor( val getProfileViewState: LiveData get() = _getProfileViewState + private val _markConversationAsSensitiveResult = MutableLiveData(MarkConversationAsSensitiveViewState.None) + val markAsSensitiveResult: LiveData + get() = _markConversationAsSensitiveResult + + private val _markConversationAsInsensitiveResult = MutableLiveData(MarkConversationAsInsensitiveViewState.None) + val markAsInsensitiveResult: LiveData + get() = _markConversationAsInsensitiveResult + fun getRoom(user: User, token: String) { _viewState.value = GetRoomStartState chatNetworkDataSource.getRoom(user, token) @@ -356,6 +364,29 @@ class ConversationInfoViewModel @Inject constructor( } } + fun markConversationAsSensitive(credentials: String, baseUrl: String, roomToken: String) { + viewModelScope.launch { + try { + val response = conversationsRepository.markConversationAsSensitive(credentials, baseUrl, roomToken) + _markConversationAsSensitiveResult.value = MarkConversationAsSensitiveViewState.Success(response.ocs?.meta?.statusCode!!) + } catch (exception: Exception) { + _markConversationAsSensitiveResult.value = MarkConversationAsSensitiveViewState.Error(exception.message.toString()) + } + } + } + + + fun markConversationAsInsensitive(credentials: String, baseUrl: String, roomToken:String){ + viewModelScope.launch { + try { + val response = conversationsRepository.markConversationAsInsensitive(credentials, baseUrl, roomToken) + _markConversationAsInsensitiveResult.value = MarkConversationAsInsensitiveViewState.Success(response.ocs?.meta?.statusCode!!) + } catch (exception: Exception) { + _markConversationAsInsensitiveResult.value = MarkConversationAsInsensitiveViewState.Error(exception.message.toString()) + } + } + } + inner class GetRoomObserver : Observer { override fun onSubscribe(d: Disposable) { // unused atm @@ -405,6 +436,18 @@ class ConversationInfoViewModel @Inject constructor( data class Error(val exception: Exception) : ClearChatHistoryViewState() } + sealed class MarkConversationAsSensitiveViewState{ + data object None: MarkConversationAsSensitiveViewState() + data class Success (val statusCode: Int): MarkConversationAsSensitiveViewState() + data class Error (val message:String): MarkConversationAsSensitiveViewState() + } + + sealed class MarkConversationAsInsensitiveViewState{ + data object None: MarkConversationAsInsensitiveViewState() + data class Success (val statusCode: Int): MarkConversationAsInsensitiveViewState() + data class Error (val message:String): MarkConversationAsInsensitiveViewState() + } + sealed class SetConversationReadOnlyViewState { data object None : SetConversationReadOnlyViewState() data object Success : SetConversationReadOnlyViewState() diff --git a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt index 32a53e911..5bfbc0a9f 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt @@ -49,4 +49,8 @@ interface ConversationsRepository { suspend fun createRoom(credentials: String, url: String, body: CreateRoomRequest): RoomOverall suspend fun getProfile(credentials: String, url: String): Profile? + + suspend fun markConversationAsSensitive(credentials:String, baseUrl:String, roomToken:String) : GenericOverall + + suspend fun markConversationAsInsensitive(credentials: String, baseUrl:String, roomToken:String): GenericOverall } diff --git a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt index d6e38753d..e8d18ea3d 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt @@ -121,6 +121,24 @@ class ConversationsRepositoryImpl( return coroutineApi.getProfile(credentials, url).ocs?.data } + override suspend fun markConversationAsSensitive( + credentials: String, + baseUrl: String, + roomToken: String + ):GenericOverall { + val url = ApiUtils.getUrlForSensitiveConversation(baseUrl, roomToken) + return coroutineApi.markConversationAsSensitive(credentials,url) + } + + override suspend fun markConversationAsInsensitive( + credentials: String, + baseUrl: String, + roomToken: String + ): GenericOverall { + val url = ApiUtils.getUrlForSensitiveConversation(baseUrl, roomToken) + return coroutineApi.markConversationAsInsensitive(credentials,url) + } + override suspend fun banActor( credentials: String, url: String, diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt index d27608b56..5044557dd 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt @@ -477,6 +477,10 @@ object ApiUtils { return "$baseUrl$OCS_API_VERSION/apps/spreed/temp-user-avatar" } + fun getUrlForSensitiveConversation(baseUrl:String, roomToken: String): String{ + return "$baseUrl$OCS_API_VERSION/apps/spreed/api/v4/room/$roomToken/sensitive" + } + fun getUrlForUserFields(baseUrl: String): String { return "$baseUrl$OCS_API_VERSION/cloud/user/fields" } From 8f46531699a3371773946999d9dbbaab93288411 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 20 May 2025 13:39:54 +0200 Subject: [PATCH 05/10] add logic to conversationInfoActivity Signed-off-by: sowjanyakch --- .../com/nextcloud/talk/api/NcApiCoroutines.kt | 8 +-- .../ConversationInfoActivity.kt | 63 +++++++++++++++++++ .../viewmodel/ConversationInfoViewModel.kt | 28 +++++---- .../talk/data/source/local/Migrations.kt | 1 - .../talk/models/domain/ConversationModel.kt | 2 +- .../conversations/ConversationsRepository.kt | 4 +- .../ConversationsRepositoryImpl.kt | 6 +- .../java/com/nextcloud/talk/utils/ApiUtils.kt | 2 +- .../nextcloud/talk/utils/CapabilitiesUtil.kt | 3 +- .../res/layout/item_notification_settings.xml | 2 +- app/src/main/res/values/strings.xml | 2 + 11 files changed, 94 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt index 6efa73b04..168932dff 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt +++ b/app/src/main/java/com/nextcloud/talk/api/NcApiCoroutines.kt @@ -187,14 +187,14 @@ interface NcApiCoroutines { @POST suspend fun markConversationAsSensitive( - @Header("Authorization") authorization:String, - @Url url:String + @Header("Authorization") authorization: String, + @Url url: String ): GenericOverall @DELETE suspend fun markConversationAsInsensitive( - @Header("Authorization") authorization:String, - @Url url:String + @Header("Authorization") authorization: String, + @Url url: String ): GenericOverall @FormUrlEncoded diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt index 0e6731f3b..55a65bc1d 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -250,8 +250,49 @@ class ConversationInfoActivity : initBanActorObserver() initConversationReadOnlyObserver() initClearChatHistoryObserver() + initMarkConversationAsSensitiveObserver() + initMarkConversationAsInsensitiveObserver() } + private fun initMarkConversationAsSensitiveObserver() { + viewModel.markAsSensitiveResult.observe(this) { uiState -> + when (uiState) { + is ConversationInfoViewModel.MarkConversationAsSensitiveViewState.Success -> { + Snackbar.make( + binding.root, + context.getString(R.string.nc_mark_conversation_as_sensitive), + Snackbar.LENGTH_LONG + ).show() + } + is ConversationInfoViewModel.MarkConversationAsSensitiveViewState.Error -> { + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + Log.e(TAG, "failed to mark conversation as sensitive", uiState.exception) + } + else -> { + } + } + } + } + + private fun initMarkConversationAsInsensitiveObserver() { + viewModel.markAsInsensitiveResult.observe(this) { uiState -> + when (uiState) { + is ConversationInfoViewModel.MarkConversationAsInsensitiveViewState.Success -> { + Snackbar.make( + binding.root, + context.getString(R.string.nc_mark_conversation_as_insensitive), + Snackbar.LENGTH_LONG + ).show() + } + is ConversationInfoViewModel.MarkConversationAsInsensitiveViewState.Error -> { + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + Log.e(TAG, "failed to mark conversation as sensitive", uiState.exception) + } + else -> { + } + } + } + } private fun initClearChatHistoryObserver() { viewModel.clearChatHistoryViewState.observe(this) { uiState -> when (uiState) { @@ -1008,6 +1049,11 @@ class ConversationInfoActivity : viewModel.getRoom(conversationUser, conversationToken) } + binding.notificationSettingsView.notificationSettingsSensitiveConversation.setOnClickListener { + val isChecked = binding.notificationSettingsView.sensitiveConversationSwitch.isChecked + binding.notificationSettingsView.callNotificationsSwitch.isChecked = !isChecked + } + if (conversation!!.hasArchived) { binding.archiveConversationIcon .setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_eye, null)) @@ -1019,6 +1065,20 @@ class ConversationInfoActivity : binding.archiveConversationText.text = resources.getString(R.string.archive_conversation) binding.archiveConversationTextHint.text = resources.getString(R.string.archive_hint) } + + binding.notificationSettingsView.sensitiveConversationSwitch.setOnCheckedChangeListener { _, isChecked -> + binding.notificationSettingsView.callNotificationsSwitch.isChecked = !isChecked + if (isChecked) { + viewModel.markConversationAsSensitive(credentials, conversationUser.baseUrl!!, conversation?.token!!) + } else { + viewModel.markConversationAsInsensitive(credentials, conversationUser.baseUrl!!, conversation?.token!!) + } + } + if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.SENSITIVE_CONVERSATIONS)) { + binding.notificationSettingsView.notificationSettingsSensitiveConversation.visibility = VISIBLE + } else { + binding.notificationSettingsView.notificationSettingsSensitiveConversation.visibility = GONE + } if (ConversationUtils.isConversationReadOnlyAvailable(conversationCopy, spreedCapabilities)) { binding.lockConversation.visibility = VISIBLE binding.lockConversationSwitch.isChecked = databaseStorageModule!!.getBoolean("lock_switch", false) @@ -1704,6 +1764,7 @@ class ConversationInfoActivity : module.saveBoolean("call_notifications_switch", !isChecked) } } + binding.notificationSettingsView.conversationInfoMessageNotificationsDropdown .setOnItemClickListener { _, _, position, _ -> val value = resources.getStringArray(R.array.message_notification_levels_entry_values)[position] @@ -1716,6 +1777,8 @@ class ConversationInfoActivity : binding.notificationSettingsView.importantConversationSwitch.isChecked = module .getBoolean("important_conversation_switch", false) + binding.notificationSettingsView.sensitiveConversationSwitch.isChecked = conversation!!.hasSensitive + if (conversation!!.remoteServer.isNullOrEmpty()) { binding.notificationSettingsView.notificationSettingsCallNotifications.visibility = VISIBLE binding.notificationSettingsView.callNotificationsSwitch.isChecked = module diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt index a977a22f1..dfa61281c 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt @@ -135,10 +135,13 @@ class ConversationInfoViewModel @Inject constructor( get() = _getProfileViewState private val _markConversationAsSensitiveResult = MutableLiveData(MarkConversationAsSensitiveViewState.None) + private val _markConversationAsSensitiveResult = + MutableLiveData(MarkConversationAsSensitiveViewState.None) val markAsSensitiveResult: LiveData get() = _markConversationAsSensitiveResult - private val _markConversationAsInsensitiveResult = MutableLiveData(MarkConversationAsInsensitiveViewState.None) + private val _markConversationAsInsensitiveResult = + MutableLiveData(MarkConversationAsInsensitiveViewState.None) val markAsInsensitiveResult: LiveData get() = _markConversationAsInsensitiveResult @@ -370,19 +373,18 @@ class ConversationInfoViewModel @Inject constructor( val response = conversationsRepository.markConversationAsSensitive(credentials, baseUrl, roomToken) _markConversationAsSensitiveResult.value = MarkConversationAsSensitiveViewState.Success(response.ocs?.meta?.statusCode!!) } catch (exception: Exception) { - _markConversationAsSensitiveResult.value = MarkConversationAsSensitiveViewState.Error(exception.message.toString()) + _markConversationAsSensitiveResult.value = MarkConversationAsSensitiveViewState.Error(exception) } } } - - fun markConversationAsInsensitive(credentials: String, baseUrl: String, roomToken:String){ + fun markConversationAsInsensitive(credentials: String, baseUrl: String, roomToken: String) { viewModelScope.launch { try { val response = conversationsRepository.markConversationAsInsensitive(credentials, baseUrl, roomToken) _markConversationAsInsensitiveResult.value = MarkConversationAsInsensitiveViewState.Success(response.ocs?.meta?.statusCode!!) } catch (exception: Exception) { - _markConversationAsInsensitiveResult.value = MarkConversationAsInsensitiveViewState.Error(exception.message.toString()) + _markConversationAsInsensitiveResult.value = MarkConversationAsInsensitiveViewState.Error(exception) } } } @@ -436,16 +438,16 @@ class ConversationInfoViewModel @Inject constructor( data class Error(val exception: Exception) : ClearChatHistoryViewState() } - sealed class MarkConversationAsSensitiveViewState{ - data object None: MarkConversationAsSensitiveViewState() - data class Success (val statusCode: Int): MarkConversationAsSensitiveViewState() - data class Error (val message:String): MarkConversationAsSensitiveViewState() + sealed class MarkConversationAsSensitiveViewState { + data object None : MarkConversationAsSensitiveViewState() + data class Success(val statusCode: Int) : MarkConversationAsSensitiveViewState() + data class Error(val exception: Exception) : MarkConversationAsSensitiveViewState() } - sealed class MarkConversationAsInsensitiveViewState{ - data object None: MarkConversationAsInsensitiveViewState() - data class Success (val statusCode: Int): MarkConversationAsInsensitiveViewState() - data class Error (val message:String): MarkConversationAsInsensitiveViewState() + sealed class MarkConversationAsInsensitiveViewState { + data object None : MarkConversationAsInsensitiveViewState() + data class Success(val statusCode: Int) : MarkConversationAsInsensitiveViewState() + data class Error(val exception: Exception) : MarkConversationAsInsensitiveViewState() } sealed class SetConversationReadOnlyViewState { diff --git a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt index 28d57e953..ad2318b9f 100644 --- a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt +++ b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt @@ -290,7 +290,6 @@ object Migrations { } } - fun addisSensitive(db: SupportSQLiteDatabase) { try { db.execSQL( diff --git a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt index fbfbde129..6f87e672c 100644 --- a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt +++ b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt @@ -64,7 +64,7 @@ class ConversationModel( var hasSensitive: Boolean = false, // attributes that don't come from API. This should be changed?! - var password: String? = null, + var password: String? = null ) { diff --git a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt index 5bfbc0a9f..74366ed6e 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt @@ -50,7 +50,7 @@ interface ConversationsRepository { suspend fun getProfile(credentials: String, url: String): Profile? - suspend fun markConversationAsSensitive(credentials:String, baseUrl:String, roomToken:String) : GenericOverall + suspend fun markConversationAsSensitive(credentials: String, baseUrl: String, roomToken: String): GenericOverall - suspend fun markConversationAsInsensitive(credentials: String, baseUrl:String, roomToken:String): GenericOverall + suspend fun markConversationAsInsensitive(credentials: String, baseUrl: String, roomToken: String): GenericOverall } diff --git a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt index e8d18ea3d..8b06228a9 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt @@ -125,9 +125,9 @@ class ConversationsRepositoryImpl( credentials: String, baseUrl: String, roomToken: String - ):GenericOverall { + ): GenericOverall { val url = ApiUtils.getUrlForSensitiveConversation(baseUrl, roomToken) - return coroutineApi.markConversationAsSensitive(credentials,url) + return coroutineApi.markConversationAsSensitive(credentials, url) } override suspend fun markConversationAsInsensitive( @@ -136,7 +136,7 @@ class ConversationsRepositoryImpl( roomToken: String ): GenericOverall { val url = ApiUtils.getUrlForSensitiveConversation(baseUrl, roomToken) - return coroutineApi.markConversationAsInsensitive(credentials,url) + return coroutineApi.markConversationAsInsensitive(credentials, url) } override suspend fun banActor( diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt index 5044557dd..bc5903acc 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.kt @@ -477,7 +477,7 @@ object ApiUtils { return "$baseUrl$OCS_API_VERSION/apps/spreed/temp-user-avatar" } - fun getUrlForSensitiveConversation(baseUrl:String, roomToken: String): String{ + fun getUrlForSensitiveConversation(baseUrl: String, roomToken: String): String { return "$baseUrl$OCS_API_VERSION/apps/spreed/api/v4/room/$roomToken/sensitive" } diff --git a/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt b/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt index ef0f85f1e..e27bd75bf 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/CapabilitiesUtil.kt @@ -58,7 +58,8 @@ enum class SpreedFeatures(val value: String) { EDIT_MESSAGES_NOTE_TO_SELF("edit-messages-note-to-self"), ARCHIVE_CONVERSATIONS("archived-conversations-v2"), CONVERSATION_CREATION_ALL("conversation-creation-all"), - UNBIND_CONVERSATION("unbind-conversation") + UNBIND_CONVERSATION("unbind-conversation"), + SENSITIVE_CONVERSATIONS("sensitive-conversations") } @Suppress("TooManyFunctions") diff --git a/app/src/main/res/layout/item_notification_settings.xml b/app/src/main/res/layout/item_notification_settings.xml index 1e439b11d..535e22fa8 100644 --- a/app/src/main/res/layout/item_notification_settings.xml +++ b/app/src/main/res/layout/item_notification_settings.xml @@ -150,7 +150,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="@dimen/standard_margin" - android:checked="true" + android:checked="false" android:clickable="false" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a1acd71e2..dc77786f2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -239,6 +239,8 @@ How to translate with transifex: Delete all messages Do you really want to delete all messages in this conversation? All messages were deleted + Conversation marked as sensitive + Conversation unmarked as sensitive Rename conversation Rename Delete conversation From 61af44f3f423b8c75a0f305e2f14a0f5f78803a0 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 20 May 2025 14:02:02 +0200 Subject: [PATCH 06/10] api call Signed-off-by: sowjanyakch --- .../ConversationInfoActivity.kt | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt index 55a65bc1d..03f7a1d1e 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -1049,11 +1049,6 @@ class ConversationInfoActivity : viewModel.getRoom(conversationUser, conversationToken) } - binding.notificationSettingsView.notificationSettingsSensitiveConversation.setOnClickListener { - val isChecked = binding.notificationSettingsView.sensitiveConversationSwitch.isChecked - binding.notificationSettingsView.callNotificationsSwitch.isChecked = !isChecked - } - if (conversation!!.hasArchived) { binding.archiveConversationIcon .setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_eye, null)) @@ -1066,12 +1061,24 @@ class ConversationInfoActivity : binding.archiveConversationTextHint.text = resources.getString(R.string.archive_hint) } - binding.notificationSettingsView.sensitiveConversationSwitch.setOnCheckedChangeListener { _, isChecked -> - binding.notificationSettingsView.callNotificationsSwitch.isChecked = !isChecked + + binding.notificationSettingsView.sensitiveConversationSwitch.isChecked = conversation!!.hasSensitive + + binding.notificationSettingsView.notificationSettingsSensitiveConversation.setOnClickListener { + val isChecked = !binding.notificationSettingsView.sensitiveConversationSwitch.isChecked + binding.notificationSettingsView.sensitiveConversationSwitch.isChecked = isChecked if (isChecked) { - viewModel.markConversationAsSensitive(credentials, conversationUser.baseUrl!!, conversation?.token!!) + viewModel.markConversationAsSensitive( + credentials, + conversationUser.baseUrl!!, + conversation?.token!! + ) } else { - viewModel.markConversationAsInsensitive(credentials, conversationUser.baseUrl!!, conversation?.token!!) + viewModel.markConversationAsInsensitive( + credentials, + conversationUser.baseUrl!!, + conversation?.token!! + ) } } if (hasSpreedFeatureCapability(spreedCapabilities, SpreedFeatures.SENSITIVE_CONVERSATIONS)) { @@ -1777,8 +1784,6 @@ class ConversationInfoActivity : binding.notificationSettingsView.importantConversationSwitch.isChecked = module .getBoolean("important_conversation_switch", false) - binding.notificationSettingsView.sensitiveConversationSwitch.isChecked = conversation!!.hasSensitive - if (conversation!!.remoteServer.isNullOrEmpty()) { binding.notificationSettingsView.notificationSettingsCallNotifications.visibility = VISIBLE binding.notificationSettingsView.callNotificationsSwitch.isChecked = module From 59bfaa6cd2b4ce55f5da3824d8e989d4390554d3 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 20 May 2025 14:50:47 +0200 Subject: [PATCH 07/10] ktlintFormat Signed-off-by: sowjanyakch --- .../conversationinfo/ConversationInfoActivity.kt | 1 - .../viewmodel/ConversationInfoViewModel.kt | 14 ++++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt index 03f7a1d1e..bff0d194f 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -1061,7 +1061,6 @@ class ConversationInfoActivity : binding.archiveConversationTextHint.text = resources.getString(R.string.archive_hint) } - binding.notificationSettingsView.sensitiveConversationSwitch.isChecked = conversation!!.hasSensitive binding.notificationSettingsView.notificationSettingsSensitiveConversation.setOnClickListener { diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt index dfa61281c..fb9265d13 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt @@ -367,24 +367,30 @@ class ConversationInfoViewModel @Inject constructor( } } + @Suppress("Detekt.TooGenericExceptionCaught") fun markConversationAsSensitive(credentials: String, baseUrl: String, roomToken: String) { viewModelScope.launch { try { val response = conversationsRepository.markConversationAsSensitive(credentials, baseUrl, roomToken) - _markConversationAsSensitiveResult.value = MarkConversationAsSensitiveViewState.Success(response.ocs?.meta?.statusCode!!) + _markConversationAsSensitiveResult.value = + MarkConversationAsSensitiveViewState.Success(response.ocs?.meta?.statusCode!!) } catch (exception: Exception) { - _markConversationAsSensitiveResult.value = MarkConversationAsSensitiveViewState.Error(exception) + _markConversationAsSensitiveResult.value = + MarkConversationAsSensitiveViewState.Error(exception) } } } + @Suppress("Detekt.TooGenericExceptionCaught") fun markConversationAsInsensitive(credentials: String, baseUrl: String, roomToken: String) { viewModelScope.launch { try { val response = conversationsRepository.markConversationAsInsensitive(credentials, baseUrl, roomToken) - _markConversationAsInsensitiveResult.value = MarkConversationAsInsensitiveViewState.Success(response.ocs?.meta?.statusCode!!) + _markConversationAsInsensitiveResult.value = + MarkConversationAsInsensitiveViewState.Success(response.ocs?.meta?.statusCode!!) } catch (exception: Exception) { - _markConversationAsInsensitiveResult.value = MarkConversationAsInsensitiveViewState.Error(exception) + _markConversationAsInsensitiveResult.value = + MarkConversationAsInsensitiveViewState.Error(exception) } } } From d159a577ba522f6bf8a598034feb979b19aed36c Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Tue, 20 May 2025 18:48:05 +0200 Subject: [PATCH 08/10] improve UI Signed-off-by: sowjanyakch --- .../talk/conversationinfo/ConversationInfoActivity.kt | 2 +- app/src/main/res/layout/item_notification_settings.xml | 2 +- app/src/main/res/values/strings.xml | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt index bff0d194f..ff5acdbe4 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -266,7 +266,7 @@ class ConversationInfoActivity : } is ConversationInfoViewModel.MarkConversationAsSensitiveViewState.Error -> { Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - Log.e(TAG, "failed to mark conversation as sensitive", uiState.exception) + Log.e(TAG, "failed to mark conversation as insensitive", uiState.exception) } else -> { } diff --git a/app/src/main/res/layout/item_notification_settings.xml b/app/src/main/res/layout/item_notification_settings.xml index 535e22fa8..c5a958b7e 100644 --- a/app/src/main/res/layout/item_notification_settings.xml +++ b/app/src/main/res/layout/item_notification_settings.xml @@ -140,7 +140,7 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index dc77786f2..2b6820deb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -347,7 +347,8 @@ How to translate with transifex: Never notify Call notifications Sensitive conversation - Hide message text + Message preview will be disabled in conversation list and + notifications Important conversation Notifications in this conversation will override Do Not Disturb settings From f28bf0238083cb1252f726db31325a54c27d8427 Mon Sep 17 00:00:00 2001 From: sowjanyakch Date: Thu, 22 May 2025 09:11:25 +0200 Subject: [PATCH 09/10] merge conflict Signed-off-by: sowjanyakch --- .../talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt index fb9265d13..8f0226e6b 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt @@ -134,7 +134,6 @@ class ConversationInfoViewModel @Inject constructor( val getProfileViewState: LiveData get() = _getProfileViewState - private val _markConversationAsSensitiveResult = MutableLiveData(MarkConversationAsSensitiveViewState.None) private val _markConversationAsSensitiveResult = MutableLiveData(MarkConversationAsSensitiveViewState.None) val markAsSensitiveResult: LiveData From 523b8080d1d4284e374820df86fa3d67da7d7246 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 23 May 2025 10:13:37 +0200 Subject: [PATCH 10/10] Suppress strange ktlint warning about PropertyName i have absolutely no idea why "Property name should start with a lowercase letter and use camel case (cannot be auto-corrected)" is shown for these properties. It should be allowed for backing properties, just like for the others in this class?! Thus, for now the suppress Signed-off-by: Marcel Hibbe --- .../conversationinfo/viewmodel/ConversationInfoViewModel.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt index 8f0226e6b..b9008c91a 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/viewmodel/ConversationInfoViewModel.kt @@ -134,11 +134,13 @@ class ConversationInfoViewModel @Inject constructor( val getProfileViewState: LiveData get() = _getProfileViewState + @Suppress("PropertyName") private val _markConversationAsSensitiveResult = MutableLiveData(MarkConversationAsSensitiveViewState.None) val markAsSensitiveResult: LiveData get() = _markConversationAsSensitiveResult + @Suppress("PropertyName") private val _markConversationAsInsensitiveResult = MutableLiveData(MarkConversationAsInsensitiveViewState.None) val markAsInsensitiveResult: LiveData