diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 57187235c2..885c716638 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -36,6 +36,7 @@ import org.session.libsession.avatars.AvatarHelper; import org.session.libsession.database.MessageDataProvider; import org.session.libsession.messaging.MessagingModuleConfiguration; import org.session.libsession.messaging.sending_receiving.notifications.MessageNotifier; +import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI; import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2; import org.session.libsession.messaging.sending_receiving.pollers.Poller; import org.session.libsession.snode.SnodeModule; @@ -78,7 +79,6 @@ import org.thoughtcrime.securesms.notifications.DefaultMessageNotifier; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.notifications.OptimizedMessageNotifier; import org.thoughtcrime.securesms.notifications.PushManager; -import org.thoughtcrime.securesms.notifications.PushNotificationManager; import org.thoughtcrime.securesms.providers.BlobProvider; import org.thoughtcrime.securesms.service.ExpiringMessageManager; import org.thoughtcrime.securesms.service.KeyCachingService; @@ -440,7 +440,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO private static class ProviderInitializationException extends RuntimeException { } public void registerForPnIfNeeded(final Boolean force) { - pushManager.register(force); + pushManager.refresh(force); } private void setUpPollingIfNeeded() { @@ -514,7 +514,7 @@ public class ApplicationContext extends Application implements DefaultLifecycleO public void clearAllData(boolean isMigratingToV2KeyPair) { String token = TextSecurePreferences.getFCMToken(this); if (token != null && !token.isEmpty()) { - PushNotificationManager.unregister(token, this); + PushNotificationAPI.unregister(token); } if (firebaseInstanceIdJob != null && firebaseInstanceIdJob.isActive()) { firebaseInstanceIdJob.cancel(null); diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushManager.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushManager.kt index 9d4ef23fe9..d094644c07 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushManager.kt @@ -1,5 +1,5 @@ package org.thoughtcrime.securesms.notifications interface PushManager { - fun register(force: Boolean) + fun refresh(force: Boolean) } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushNotificationManager.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushNotificationManager.kt deleted file mode 100644 index 5a53f7af22..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushNotificationManager.kt +++ /dev/null @@ -1,121 +0,0 @@ -package org.thoughtcrime.securesms.notifications - -import android.content.Context -import nl.komponents.kovenant.Promise -import nl.komponents.kovenant.functional.map -import okhttp3.MediaType -import okhttp3.Request -import okhttp3.RequestBody -import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI -import org.session.libsession.snode.OnionRequestAPI -import org.session.libsession.snode.Version -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsignal.utilities.JsonUtil -import org.session.libsignal.utilities.Log -import org.session.libsignal.utilities.retryIfNeeded -import org.thoughtcrime.securesms.dependencies.DatabaseComponent - -object PushNotificationManager { - private val maxRetryCount = 4 - private val tokenExpirationInterval = 12 * 60 * 60 * 1000 - - private val server by lazy { - PushNotificationAPI.server - } - private val pnServerPublicKey by lazy { - PushNotificationAPI.serverPublicKey - } - - enum class ClosedGroupOperation { - Subscribe, Unsubscribe; - - val rawValue: String - get() { - return when (this) { - Subscribe -> "subscribe_closed_group" - Unsubscribe -> "unsubscribe_closed_group" - } - } - } - - @JvmStatic - fun unregister(token: String, context: Context) { - val parameters = mapOf( "token" to token ) - val url = "$server/unregister" - val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) - val request = Request.Builder().url(url).post(body) - retryIfNeeded(maxRetryCount) { - getResponseBody(request.build()).map { json -> - val code = json["code"] as? Int - if (code != null && code != 0) { - TextSecurePreferences.setIsUsingFCM(context, false) - } else { - Log.d("Loki", "Couldn't disable FCM due to error: ${json["message"] as? String ?: "null"}.") - } - }.fail { exception -> - Log.d("Loki", "Couldn't disable FCM due to error: ${exception}.") - } - } - // Unsubscribe from all closed groups - val allClosedGroupPublicKeys = DatabaseComponent.get(context).lokiAPIDatabase().getAllClosedGroupPublicKeys() - val userPublicKey = TextSecurePreferences.getLocalNumber(context)!! - allClosedGroupPublicKeys.iterator().forEach { closedGroup -> - performOperation(context, ClosedGroupOperation.Unsubscribe, closedGroup, userPublicKey) - } - } - - @JvmStatic - fun register(token: String, publicKey: String, context: Context, force: Boolean) { - val oldToken = TextSecurePreferences.getFCMToken(context) - val lastUploadDate = TextSecurePreferences.getLastFCMUploadTime(context) - if (!force && token == oldToken && System.currentTimeMillis() - lastUploadDate < tokenExpirationInterval) { return } - val parameters = mapOf( "token" to token, "pubKey" to publicKey ) - val url = "$server/register" - val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) - val request = Request.Builder().url(url).post(body) - retryIfNeeded(maxRetryCount) { - getResponseBody(request.build()).map { json -> - val code = json["code"] as? Int - if (code != null && code != 0) { - TextSecurePreferences.setIsUsingFCM(context, true) - TextSecurePreferences.setFCMToken(context, token) - TextSecurePreferences.setLastFCMUploadTime(context, System.currentTimeMillis()) - } else { - Log.d("Loki", "Couldn't register for FCM due to error: ${json["message"] as? String ?: "null"}.") - } - }.fail { exception -> - Log.d("Loki", "Couldn't register for FCM due to error: ${exception}.") - } - } - // Subscribe to all closed groups - val allClosedGroupPublicKeys = DatabaseComponent.get(context).lokiAPIDatabase().getAllClosedGroupPublicKeys() - allClosedGroupPublicKeys.iterator().forEach { closedGroup -> - performOperation(context, ClosedGroupOperation.Subscribe, closedGroup, publicKey) - } - } - - @JvmStatic - fun performOperation(context: Context, operation: ClosedGroupOperation, closedGroupPublicKey: String, publicKey: String) { - if (!TextSecurePreferences.isUsingFCM(context)) { return } - val parameters = mapOf( "closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey ) - val url = "$server/${operation.rawValue}" - val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) - val request = Request.Builder().url(url).post(body) - retryIfNeeded(maxRetryCount) { - getResponseBody(request.build()).map { json -> - val code = json["code"] as? Int - if (code == null || code == 0) { - Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${json["message"] as? String ?: "null"}.") - } - }.fail { exception -> - Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${exception}.") - } - } - } - - private fun getResponseBody(request: Request): Promise, Exception> { - return OnionRequestAPI.sendOnionRequest(request, server, pnServerPublicKey, Version.V2).map { response -> - JsonUtil.fromJson(response.body, Map::class.java) - } - } -} diff --git a/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebasePushManager.kt b/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebasePushManager.kt index 4f9a6292f3..c169f145c9 100644 --- a/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebasePushManager.kt +++ b/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebasePushManager.kt @@ -93,15 +93,15 @@ class FirebasePushManager(private val context: Context, private val prefs: TextS return content } - override fun register(force: Boolean) { + override fun refresh(force: Boolean) { firebaseInstanceIdJob?.apply { if (force) cancel() else if (isActive) return } - firebaseInstanceIdJob = getFcmInstanceId { register(it, force) } + firebaseInstanceIdJob = getFcmInstanceId { refresh(it, force) } } - private fun register(task: Task, force: Boolean) { + private fun refresh(task: Task, force: Boolean) { // context in here is Dispatchers.IO if (!task.isSuccessful) { Log.w( diff --git a/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/PushNotificationService.kt b/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/PushNotificationService.kt index bb625bc540..0d9765d737 100644 --- a/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/PushNotificationService.kt +++ b/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/PushNotificationService.kt @@ -8,6 +8,7 @@ import dagger.hilt.android.AndroidEntryPoint import org.session.libsession.messaging.jobs.BatchMessageReceiveJob import org.session.libsession.messaging.jobs.JobQueue import org.session.libsession.messaging.jobs.MessageReceiveParameters +import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI import org.session.libsession.messaging.utilities.MessageWrapper import org.session.libsession.utilities.TextSecurePreferences import org.session.libsignal.utilities.Base64 @@ -23,7 +24,7 @@ class PushNotificationService : FirebaseMessagingService() { super.onNewToken(token) Log.d("Loki", "New FCM token: $token.") TextSecurePreferences.getLocalNumber(this) ?: return - pushManager.register(true) + pushManager.refresh(true) } override fun onMessageReceived(message: RemoteMessage) { @@ -53,22 +54,18 @@ class PushNotificationService : FirebaseMessagingService() { Log.d("Loki", "Failed to decode data for message.") val builder = NotificationCompat.Builder(this, NotificationChannels.OTHER) .setSmallIcon(network.loki.messenger.R.drawable.ic_notification) - .setColor(this.getResources().getColor(network.loki.messenger.R.color.textsecure_primary)) + .setColor(resources.getColor(network.loki.messenger.R.color.textsecure_primary)) .setContentTitle("Session") .setContentText("You've got a new message.") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setAutoCancel(true) - with(NotificationManagerCompat.from(this)) { - notify(11111, builder.build()) - } + NotificationManagerCompat.from(this).notify(11111, builder.build()) } } override fun onDeletedMessages() { Log.d("Loki", "Called onDeletedMessages.") super.onDeletedMessages() - val token = TextSecurePreferences.getFCMToken(this)!! - val userPublicKey = TextSecurePreferences.getLocalNumber(this) ?: return - PushNotificationManager.register(token, userPublicKey, this, true) + PushNotificationAPI.register() } } \ No newline at end of file diff --git a/app/src/website/kotlin/org/thoughtcrime/securesms/notifications/NoOpPushManager.kt b/app/src/website/kotlin/org/thoughtcrime/securesms/notifications/NoOpPushManager.kt index 59f1b9df4b..a817e59d5d 100644 --- a/app/src/website/kotlin/org/thoughtcrime/securesms/notifications/NoOpPushManager.kt +++ b/app/src/website/kotlin/org/thoughtcrime/securesms/notifications/NoOpPushManager.kt @@ -4,7 +4,7 @@ import org.session.libsignal.utilities.Log class NoOpPushManager: PushManager { - override fun register(force: Boolean) { + override fun refresh(force: Boolean) { Log.d("NoOpPushManager", "Push notifications not supported, not registering for push notifications") } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt index b8a903539f..39afec418b 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroupHandler.kt @@ -74,7 +74,7 @@ fun MessageSender.create(name: String, members: Collection): Promise, name: St // Update name if needed if (name != group.title) { setName(groupPublicKey, name) } // Add members if needed - val addedMembers = members - group.members.map { it.serialize() } - if (!addedMembers.isEmpty()) { addMembers(groupPublicKey, addedMembers) } + val addedMembers = members - group.members.map { it.serialize() }.toSet() + if (addedMembers.isNotEmpty()) { addMembers(groupPublicKey, addedMembers) } // Remove members if needed - val removedMembers = group.members.map { it.serialize() } - members + val removedMembers = group.members.map { it.serialize() } - members.toSet() if (removedMembers.isEmpty()) { removeMembers(groupPublicKey, removedMembers) } } @@ -195,7 +195,7 @@ fun MessageSender.removeMembers(groupPublicKey: String, membersToRemove: List "subscribe_closed_group" - Unsubscribe -> "unsubscribe_closed_group" - } - } + private enum class ClosedGroupOperation(val rawValue: String) { + Subscribe("subscribe_closed_group"), + Unsubscribe("unsubscribe_closed_group"); } + fun register(token: String? = TextSecurePreferences.getFCMToken(context)) { + token?.let(::unregisterV1) + subscribeGroups() + } + + @JvmStatic fun unregister(token: String) { + unregisterV1(token) + unsubscribeGroups() + } + + private fun unregisterV1(token: String) { val parameters = mapOf( "token" to token ) val url = "$server/unregister" val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) val request = Request.Builder().url(url).post(body) retryIfNeeded(maxRetryCount) { OnionRequestAPI.sendOnionRequest(request.build(), server, serverPublicKey, Version.V2).map { response -> - val code = response.info["code"] as? Int - if (code != null && code != 0) { - TextSecurePreferences.setIsUsingFCM(context, false) - } else { - Log.d("Loki", "Couldn't disable FCM due to error: ${response.info["message"] as? String ?: "null"}.") + when (response.info["code"]) { + null, 0 -> Log.d("Loki", "Couldn't disable FCM due to error: ${response.info["message"]}.") } }.fail { exception -> Log.d("Loki", "Couldn't disable FCM due to error: ${exception}.") } } - // Unsubscribe from all closed groups - val allClosedGroupPublicKeys = MessagingModuleConfiguration.shared.storage.getAllClosedGroupPublicKeys() - val userPublicKey = MessagingModuleConfiguration.shared.storage.getUserPublicKey()!! - allClosedGroupPublicKeys.iterator().forEach { closedGroup -> - performOperation(ClosedGroupOperation.Unsubscribe, closedGroup, userPublicKey) - } } - fun register(token: String, publicKey: String, force: Boolean) { - // Subscribe to all closed groups - val allClosedGroupPublicKeys = MessagingModuleConfiguration.shared.storage.getAllClosedGroupPublicKeys() - allClosedGroupPublicKeys.iterator().forEach { closedGroup -> - performOperation(ClosedGroupOperation.Subscribe, closedGroup, publicKey) - } + // Legacy Closed Groups + + fun subscribeGroup( + closedGroupPublicKey: String, + publicKey: String = MessagingModuleConfiguration.shared.storage.getUserPublicKey()!! + ) { + performGroupOperation(ClosedGroupOperation.Subscribe, closedGroupPublicKey, publicKey) } - fun performOperation(operation: ClosedGroupOperation, closedGroupPublicKey: String, publicKey: String) { - if (!TextSecurePreferences.isUsingFCM(context)) { return } + private fun subscribeGroups( + closedGroupPublicKeys: Collection = MessagingModuleConfiguration.shared.storage.getAllClosedGroupPublicKeys(), + publicKey: String = MessagingModuleConfiguration.shared.storage.getUserPublicKey()!! + ) { + performGroupOperations(ClosedGroupOperation.Subscribe, closedGroupPublicKeys, publicKey) + } + + fun unsubscribeGroup( + closedGroupPublicKey: String, + publicKey: String = MessagingModuleConfiguration.shared.storage.getUserPublicKey()!! + ) { + performGroupOperation(ClosedGroupOperation.Unsubscribe, closedGroupPublicKey, publicKey) + } + + private fun unsubscribeGroups( + closedGroupPublicKeys: Collection = MessagingModuleConfiguration.shared.storage.getAllClosedGroupPublicKeys(), + publicKey: String = MessagingModuleConfiguration.shared.storage.getUserPublicKey()!! + ) { + performGroupOperations(ClosedGroupOperation.Unsubscribe, closedGroupPublicKeys, publicKey) + } + + private fun performGroupOperations( + operation: ClosedGroupOperation, + closedGroupPublicKeys: Collection, + publicKey: String + ) { + closedGroupPublicKeys.forEach { performGroupOperation(operation, it, publicKey) } + } + + private fun performGroupOperation( + operation: ClosedGroupOperation, + closedGroupPublicKey: String, + publicKey: String + ) { + if (!TextSecurePreferences.isUsingFCM(context)) return + val parameters = mapOf( "closedGroupPublicKey" to closedGroupPublicKey, "pubKey" to publicKey ) val url = "$legacyServer/${operation.rawValue}" val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) val request = Request.Builder().url(url).post(body) + retryIfNeeded(maxRetryCount) { OnionRequestAPI.sendOnionRequest(request.build(), legacyServer, legacyServerPublicKey, Version.V2).map { response -> - val code = response.info["code"] as? Int - if (code == null || code == 0) { - Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${response.info["message"] as? String ?: "null"}.") + when (response.info["code"]) { + null, 0 -> Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${response.info["message"]}.") } }.fail { exception -> Log.d("Loki", "Couldn't subscribe/unsubscribe closed group: $closedGroupPublicKey due to error: ${exception}.")