diff --git a/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt b/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt index 39a9c46078..19f49bf67e 100644 --- a/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt +++ b/src/org/thoughtcrime/securesms/loki/api/LokiPushNotificationManager.kt @@ -2,23 +2,41 @@ package org.thoughtcrime.securesms.loki.api import android.content.Context import okhttp3.* +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.jobmanager.Data +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.BaseJob +import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJob import org.thoughtcrime.securesms.util.TextSecurePreferences import org.whispersystems.libsignal.logging.Log import org.whispersystems.signalservice.internal.util.JsonUtil import org.whispersystems.signalservice.loki.api.PushNotificationAcknowledgement import java.io.IOException +import java.lang.Exception +import java.util.concurrent.TimeUnit object LokiPushNotificationManager { private val connection = OkHttpClient() + private val tokenExpirationInterval = 12 * 60 * 60 * 1000 private val server by lazy { PushNotificationAcknowledgement.shared.server } - private const val tokenExpirationInterval = 12 * 60 * 60 * 1000 + 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?) { + fun unregister(token: String, context: Context) { val parameters = mapOf( "token" to token ) val url = "$server/register" val body = RequestBody.create(MediaType.get("application/json"), JsonUtil.toJson(parameters)) @@ -30,7 +48,7 @@ object LokiPushNotificationManager { 200 -> { val bodyAsString = response.body()!!.string() val json = JsonUtil.fromJson(bodyAsString, Map::class.java) - val code = json?.get("code") as? Int + val code = json?.get("code") as? Int if (code != null && code != 0) { TextSecurePreferences.setIsUsingFCM(context, false) } else { @@ -44,10 +62,16 @@ object LokiPushNotificationManager { Log.d("Loki", "Couldn't disable FCM.") } }) + // Unsubscribe from all closed groups + val allClosedGroupPublicKeys = DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys() + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + allClosedGroupPublicKeys.forEach { closedGroup -> + performOperation(context, ClosedGroupOperation.Unsubscribe, closedGroup, userPublicKey) + } } @JvmStatic - fun register(token: String, publicKey: String, context: Context?, force: Boolean) { + 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 } @@ -62,7 +86,7 @@ object LokiPushNotificationManager { 200 -> { val bodyAsString = response.body()!!.string() val json = JsonUtil.fromJson(bodyAsString, Map::class.java) - val code = json?.get("code") as? Int + val code = json?.get("code") as? Int if (code != null && code != 0) { TextSecurePreferences.setIsUsingFCM(context, true) TextSecurePreferences.setFCMToken(context, token) @@ -78,5 +102,41 @@ object LokiPushNotificationManager { Log.d("Loki", "Couldn't register for FCM.") } }) + // Subscribe to all closed groups + val allClosedGroupPublicKeys = DatabaseFactory.getSSKDatabase(context).getAllClosedGroupPublicKeys() + allClosedGroupPublicKeys.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).build() + connection.newCall(request).enqueue(object : Callback { + + override fun onResponse(call: Call, response: Response) { + when (response.code()) { + 200 -> { + val bodyAsString = response.body()!!.string() + val json = JsonUtil.fromJson(bodyAsString, Map::class.java) + val code = json?.get("code") as? Int + if (code == null || code == 0) { + Log.d("Loki", "Couldn't subscribe/unsubscribe to/from PNs for closed group with ID: $closedGroupPublicKey due to error: ${json?.get("message") as? String ?: "null"}.") + } + } + else -> { + Log.d("Loki", "Couldn't subscribe/unsubscribe to/from PNs for closed group with ID: $closedGroupPublicKey.") + } + } + } + + override fun onFailure(call: Call, exception: IOException) { + Log.d("Loki", "Couldn't subscribe/unsubscribe to/from PNs for closed group with ID: $closedGroupPublicKey due to error: $exception.") + } + }) } } diff --git a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt index 0841aaf2a6..2ff6e2a137 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt @@ -8,6 +8,8 @@ import nl.komponents.kovenant.deferred import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager +import org.thoughtcrime.securesms.loki.api.LokiPushNotificationManager.ClosedGroupOperation import org.thoughtcrime.securesms.loki.utilities.recipient import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage import org.thoughtcrime.securesms.recipients.Recipient @@ -66,13 +68,15 @@ object ClosedGroupsProtocol { if (member == userPublicKey) { continue } val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) job.setContext(context) - job.onRun() // Run the job immediately + job.onRun() // Run the job immediately to make all of this sync } // Add the group to the user's set of public keys to poll for DatabaseFactory.getSSKDatabase(context).setClosedGroupPrivateKey(groupPublicKey, groupKeyPair.hexEncodedPrivateKey) // Notify the user val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) insertOutgoingInfoMessage(context, groupID, GroupContext.Type.UPDATE, name, members, admins, threadID) + // Notify the PN server + LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) // Fulfill the promise deferred.resolve(groupID) }.start() @@ -137,6 +141,8 @@ object ClosedGroupsProtocol { sskDatabase.removeClosedGroupPrivateKey(groupPublicKey) groupDB.setActive(groupID, false) groupDB.remove(groupID, Address.fromSerialized(userPublicKey)) + // Notify the PN server + LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) } else { // Establish sessions if needed establishSessionsWithMembersIfNeeded(context, members) @@ -231,6 +237,7 @@ object ClosedGroupsProtocol { public fun handleNewClosedGroup(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { // Prepare + val userPublicKey = TextSecurePreferences.getLocalNumber(context) val sskDatabase = DatabaseFactory.getSSKDatabase(context) // Unwrap the message val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() @@ -258,6 +265,8 @@ object ClosedGroupsProtocol { insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) // Establish sessions if needed establishSessionsWithMembersIfNeeded(context, members) + // Notify the PN server + LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Subscribe, groupPublicKey, userPublicKey) } public fun handleClosedGroupUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { @@ -303,6 +312,8 @@ object ClosedGroupsProtocol { sskDatabase.removeClosedGroupPrivateKey(groupPublicKey) groupDB.setActive(groupID, false) groupDB.remove(groupID, Address.fromSerialized(userPublicKey)) + // Notify the PN server + LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) } else { establishSessionsWithMembersIfNeeded(context, members) val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey)