From 3febae577afc9bb275cc64eae9061c6d5d21aeda Mon Sep 17 00:00:00 2001 From: David Perez Date: Fri, 23 Feb 2024 17:12:02 -0600 Subject: [PATCH] Update the way we handle push notifications (#1061) --- .../AuthRequestNotificationManagerImpl.kt | 2 +- .../data/platform/manager/PushManagerImpl.kt | 17 +- .../manager/model/BitwardenNotification.kt | 7 +- .../manager/model/NotificationPayload.kt | 30 +-- .../data/platform/manager/PushManagerTest.kt | 172 +++++++++--------- 5 files changed, 113 insertions(+), 115 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/manager/AuthRequestNotificationManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/manager/AuthRequestNotificationManagerImpl.kt index 6f462f0529..403a9ea88b 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/manager/AuthRequestNotificationManagerImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/manager/AuthRequestNotificationManagerImpl.kt @@ -41,7 +41,6 @@ class AuthRequestNotificationManagerImpl( @SuppressLint("MissingPermission") private fun handlePasswordlessRequestData(data: PasswordlessRequestData) { val notificationManager = NotificationManagerCompat.from(context) - if (notificationManager.areNotificationsEnabled(NOTIFICATION_CHANNEL_ID)) return // Construct the channel, calling this more than once is safe notificationManager.createNotificationChannel( NotificationChannelCompat @@ -52,6 +51,7 @@ class AuthRequestNotificationManagerImpl( .setName(context.getString(R.string.pending_log_in_requests)) .build(), ) + if (!notificationManager.areNotificationsEnabled(NOTIFICATION_CHANNEL_ID)) return // Create the notification val builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setContentIntent(createContentIntent(data)) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/PushManagerImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/PushManagerImpl.kt index 7cbaa77500..15ee443c16 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/PushManagerImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/PushManagerImpl.kt @@ -27,7 +27,6 @@ import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.serialization.json.Json -import kotlinx.serialization.json.decodeFromJsonElement import java.time.Clock import java.time.ZoneOffset import java.time.ZonedDateTime @@ -128,7 +127,7 @@ class PushManagerImpl @Inject constructor( -> { if (settingsDiskSource.getApprovePasswordlessLoginsEnabled(userId) == true) { val payload: NotificationPayload.PasswordlessRequestNotification = - json.decodeFromJsonElement(notification.payload) + json.decodeFromString(string = notification.payload) mutablePasswordlessRequestSharedFlow.tryEmit( PasswordlessRequestData( loginRequestId = payload.id, @@ -140,7 +139,7 @@ class PushManagerImpl @Inject constructor( NotificationType.LOG_OUT -> { val payload: NotificationPayload.UserNotification = - json.decodeFromJsonElement(notification.payload) + json.decodeFromString(notification.payload) mutableLogoutSharedFlow.tryEmit( NotificationLogoutData(payload.userId), ) @@ -150,7 +149,7 @@ class PushManagerImpl @Inject constructor( NotificationType.SYNC_CIPHER_UPDATE, -> { val payload: NotificationPayload.SyncCipherNotification = - json.decodeFromJsonElement(notification.payload) + json.decodeFromString(notification.payload) if (!isLoggedIn(userId) || !payload.userMatchesNotification(userId)) return mutableSyncCipherUpsertSharedFlow.tryEmit( SyncCipherUpsertData( @@ -167,7 +166,7 @@ class PushManagerImpl @Inject constructor( NotificationType.SYNC_LOGIN_DELETE, -> { val payload: NotificationPayload.SyncCipherNotification = - json.decodeFromJsonElement(notification.payload) + json.decodeFromString(notification.payload) if (!isLoggedIn(userId) || !payload.userMatchesNotification(userId)) return mutableSyncCipherDeleteSharedFlow.tryEmit( SyncCipherDeleteData(payload.id), @@ -185,7 +184,7 @@ class PushManagerImpl @Inject constructor( NotificationType.SYNC_FOLDER_UPDATE, -> { val payload: NotificationPayload.SyncFolderNotification = - json.decodeFromJsonElement(notification.payload) + json.decodeFromString(notification.payload) if (!isLoggedIn(userId) || !payload.userMatchesNotification(userId)) return mutableSyncFolderUpsertSharedFlow.tryEmit( SyncFolderUpsertData( @@ -198,7 +197,7 @@ class PushManagerImpl @Inject constructor( NotificationType.SYNC_FOLDER_DELETE -> { val payload: NotificationPayload.SyncFolderNotification = - json.decodeFromJsonElement(notification.payload) + json.decodeFromString(notification.payload) if (!isLoggedIn(userId) || !payload.userMatchesNotification(userId)) return mutableSyncFolderDeleteSharedFlow.tryEmit( @@ -215,7 +214,7 @@ class PushManagerImpl @Inject constructor( NotificationType.SYNC_SEND_UPDATE, -> { val payload: NotificationPayload.SyncSendNotification = - json.decodeFromJsonElement(notification.payload) + json.decodeFromString(notification.payload) if (!isLoggedIn(userId) || !payload.userMatchesNotification(userId)) return mutableSyncSendUpsertSharedFlow.tryEmit( SyncSendUpsertData( @@ -228,7 +227,7 @@ class PushManagerImpl @Inject constructor( NotificationType.SYNC_SEND_DELETE -> { val payload: NotificationPayload.SyncSendNotification = - json.decodeFromJsonElement(notification.payload) + json.decodeFromString(notification.payload) if (!isLoggedIn(userId) || !payload.userMatchesNotification(userId)) return mutableSyncSendDeleteSharedFlow.tryEmit( SyncSendDeleteData(payload.id), diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/BitwardenNotification.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/BitwardenNotification.kt index 8181d7febb..7a5bfb7a59 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/BitwardenNotification.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/BitwardenNotification.kt @@ -2,19 +2,18 @@ package com.x8bit.bitwarden.data.platform.manager.model import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonElement /** * Represents a Bitwarden push notification. * * @property contextId The context ID. This is mainly used to check if the push notification * originated from this app. - * @property notificationType The type of notication. + * @property notificationType The type of notification. * @property payload Data associated with the push notification. */ @Serializable data class BitwardenNotification( - @SerialName("contextId") val contextId: String, + @SerialName("contextId") val contextId: String?, @SerialName("type") val notificationType: NotificationType, - @SerialName("payload") val payload: JsonElement, + @SerialName("payload") val payload: String, ) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/NotificationPayload.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/NotificationPayload.kt index 2eefc909a8..8df0567a0f 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/NotificationPayload.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/manager/model/NotificationPayload.kt @@ -20,12 +20,12 @@ sealed class NotificationPayload { */ @Serializable data class SyncCipherNotification( - @SerialName("id") val id: String, - @SerialName("userId") override val userId: String?, - @SerialName("organizationId") val organizationId: String?, - @SerialName("collectionIds") val collectionIds: List?, + @SerialName("Id") val id: String, + @SerialName("UserId") override val userId: String?, + @SerialName("OrganizationId") val organizationId: String?, + @SerialName("CollectionIds") val collectionIds: List?, @Contextual - @SerialName("revisionDate") val revisionDate: ZonedDateTime, + @SerialName("RevisionDate") val revisionDate: ZonedDateTime, ) : NotificationPayload() /** @@ -33,10 +33,10 @@ sealed class NotificationPayload { */ @Serializable data class SyncFolderNotification( - @SerialName("id") val id: String, - @SerialName("userId") override val userId: String, + @SerialName("Id") val id: String, + @SerialName("UserId") override val userId: String, @Contextual - @SerialName("revisionDate") val revisionDate: ZonedDateTime, + @SerialName("RevisionDate") val revisionDate: ZonedDateTime, ) : NotificationPayload() /** @@ -44,9 +44,9 @@ sealed class NotificationPayload { */ @Serializable data class UserNotification( - @SerialName("userId") override val userId: String, + @SerialName("UserId") override val userId: String, @Contextual - @SerialName("date") val date: ZonedDateTime, + @SerialName("Date") val date: ZonedDateTime, ) : NotificationPayload() /** @@ -54,10 +54,10 @@ sealed class NotificationPayload { */ @Serializable data class SyncSendNotification( - @SerialName("id") val id: String, - @SerialName("userId") override val userId: String, + @SerialName("Id") val id: String, + @SerialName("UserId") override val userId: String, @Contextual - @SerialName("revisionDate") val revisionDate: ZonedDateTime, + @SerialName("RevisionDate") val revisionDate: ZonedDateTime, ) : NotificationPayload() /** @@ -65,7 +65,7 @@ sealed class NotificationPayload { */ @Serializable data class PasswordlessRequestNotification( - @SerialName("userId") override val userId: String, - @SerialName("id") val id: String, + @SerialName("UserId") override val userId: String, + @SerialName("Id") val id: String, ) : NotificationPayload() } diff --git a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/PushManagerTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/PushManagerTest.kt index 6d2fc9db1a..ae65d6679d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/PushManagerTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/platform/manager/PushManagerTest.kt @@ -844,10 +844,10 @@ private const val AUTH_REQUEST_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 15, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\" + }" } """ @@ -855,10 +855,10 @@ private const val AUTH_REQUEST_RESPONSE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 16, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\" + }" } """ @@ -870,10 +870,10 @@ private const val LOGOUT_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 11, - "payload": { - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "date": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"Date\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -881,13 +881,13 @@ private const val SYNC_CIPHER_CREATE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 1, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "organizationId": "6a41d965-ed95-4eae-98c3-5f1ec609c2c1", - "collectionIds": [], - "revisionDate": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"OrganizationId\": \"6a41d965-ed95-4eae-98c3-5f1ec609c2c1\", + \"CollectionIds\": [], + \"RevisionDate\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -895,13 +895,13 @@ private const val SYNC_CIPHER_DELETE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 9, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "organizationId": "6a41d965-ed95-4eae-98c3-5f1ec609c2c1", - "collectionIds": [], - "revisionDate": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"OrganizationId\": \"6a41d965-ed95-4eae-98c3-5f1ec609c2c1\", + \"CollectionIds\": [], + \"RevisionDate\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -909,13 +909,13 @@ private const val SYNC_CIPHER_UPDATE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 0, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "organizationId": "6a41d965-ed95-4eae-98c3-5f1ec609c2c1", - "collectionIds": [], - "revisionDate": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"OrganizationId\": \"6a41d965-ed95-4eae-98c3-5f1ec609c2c1\", + \"CollectionIds\": [], + \"RevisionDate\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -923,10 +923,10 @@ private const val SYNC_CIPHERS_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 4, - "payload": { - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "date": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"Date\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -934,11 +934,11 @@ private const val SYNC_FOLDER_CREATE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 7, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "revisionDate": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"RevisionDate\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -946,11 +946,11 @@ private const val SYNC_FOLDER_DELETE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 3, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "revisionDate": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"RevisionDate\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -958,11 +958,11 @@ private const val SYNC_FOLDER_UPDATE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 8, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "revisionDate": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"RevisionDate\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -970,13 +970,13 @@ private const val SYNC_LOGIN_DELETE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 2, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "organizationId": "6a41d965-ed95-4eae-98c3-5f1ec609c2c1", - "collectionIds": [], - "revisionDate": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"OrganizationId\": \"6a41d965-ed95-4eae-98c3-5f1ec609c2c1\", + \"CollectionIds\": [], + \"RevisionDate\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -984,10 +984,10 @@ private const val SYNC_ORG_KEYS_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 6, - "payload": { - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "date": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"Date\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -995,11 +995,11 @@ private const val SYNC_SEND_CREATE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 12, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "revisionDate": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"RevisionDate\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -1007,11 +1007,11 @@ private const val SYNC_SEND_DELETE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 14, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "revisionDate": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"RevisionDate\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -1019,11 +1019,11 @@ private const val SYNC_SEND_UPDATE_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 13, - "payload": { - "id": "aab5cdcc-f4a7-4e65-bf6d-5e0eab052321", - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "revisionDate": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"Id\": \"aab5cdcc-f4a7-4e65-bf6d-5e0eab052321\", + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"RevisionDate\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -1031,10 +1031,10 @@ private const val SYNC_SETTINGS_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 10, - "payload": { - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "date": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"Date\": \"2023-10-27T12:00:00.000Z\" + }" } """ @@ -1042,9 +1042,9 @@ private const val SYNC_VAULT_NOTIFICATION_JSON = """ { "contextId": "801f459d-8e51-47d0-b072-3f18c9f66f64", "type": 5, - "payload": { - "userId": "078966a2-93c2-4618-ae2a-0a2394c88d37", - "date": "2023-10-27T12:00:00.000Z" - } + "payload": "{ + \"UserId\": \"078966a2-93c2-4618-ae2a-0a2394c88d37\", + \"Date\": \"2023-10-27T12:00:00.000Z\" + }" } """