diff --git a/example/src/main/java/org/xmtp/android/example/MainViewModel.kt b/example/src/main/java/org/xmtp/android/example/MainViewModel.kt index 99bd24111..7e0d30a2d 100644 --- a/example/src/main/java/org/xmtp/android/example/MainViewModel.kt +++ b/example/src/main/java/org/xmtp/android/example/MainViewModel.kt @@ -21,6 +21,7 @@ import org.xmtp.android.example.extension.stateFlow import org.xmtp.android.example.pushnotifications.PushNotificationTokenManager import org.xmtp.android.library.Conversation import org.xmtp.android.library.DecodedMessage +import org.xmtp.android.library.messages.Topic import org.xmtp.android.library.push.Service class MainViewModel : ViewModel() { @@ -46,7 +47,7 @@ class MainViewModel : ViewModel() { try { val conversations = ClientManager.client.conversations.list(includeGroups = true) val hmacKeysResult = ClientManager.client.conversations.getHmacKeys() - val subscriptions = conversations.map { + val subscriptions: MutableList = conversations.map { val hmacKeys = hmacKeysResult.hmacKeysMap val result = hmacKeys[it.topic]?.valuesList?.map { hmacKey -> Service.Subscription.HmacKey.newBuilder().also { sub_key -> @@ -56,11 +57,19 @@ class MainViewModel : ViewModel() { } Service.Subscription.newBuilder().also { sub -> - sub.addAllHmacKeys(result) + if (!result.isNullOrEmpty()) { + sub.addAllHmacKeys(result) + } sub.topic = it.topic sub.isSilent = it.version == Conversation.Version.V1 }.build() - } + }.toMutableList() + + val welcomeTopic = Service.Subscription.newBuilder().also { sub -> + sub.topic = Topic.userWelcome(ClientManager.client.installationId).description + sub.isSilent = false + }.build() + subscriptions.add(welcomeTopic) PushNotificationTokenManager.xmtpPush.subscribeWithMetadata(subscriptions) listItems.addAll( diff --git a/example/src/main/java/org/xmtp/android/example/pushnotifications/PushNotificationsService.kt b/example/src/main/java/org/xmtp/android/example/pushnotifications/PushNotificationsService.kt index b87e2fc1f..e6e89dcc6 100644 --- a/example/src/main/java/org/xmtp/android/example/pushnotifications/PushNotificationsService.kt +++ b/example/src/main/java/org/xmtp/android/example/pushnotifications/PushNotificationsService.kt @@ -20,7 +20,10 @@ import org.xmtp.android.example.R import org.xmtp.android.example.conversation.ConversationDetailActivity import org.xmtp.android.example.extension.truncatedAddress import org.xmtp.android.example.utils.KeyUtil +import org.xmtp.android.library.Conversation import org.xmtp.android.library.messages.EnvelopeBuilder +import org.xmtp.android.library.messages.Topic +import uniffi.xmtpv3.org.xmtp.android.library.codecs.GroupMembershipChanges import java.util.Date class PushNotificationsService : FirebaseMessagingService() { @@ -57,40 +60,78 @@ class PushNotificationsService : FirebaseMessagingService() { GlobalScope.launch(Dispatchers.Main) { ClientManager.createClient(keysData, applicationContext) } - val conversation = - runBlocking { ClientManager.client.fetchConversation(topic, includeGroups = true) } - if (conversation == null) { - Log.e(TAG, "No keys or conversation persisted") - return - } - val envelope = EnvelopeBuilder.buildFromString(topic, Date(), encryptedMessageData) - val peerAddress = conversation.peerAddress - val decodedMessage = conversation.decode(envelope) + val welcomeTopic = Topic.userWelcome(ClientManager.client.installationId).description + val builder = if (welcomeTopic == topic) { + val group = ClientManager.client.conversations.fromWelcome(encryptedMessageData) + val pendingIntent = PendingIntent.getActivity( + this, + 0, + ConversationDetailActivity.intent( + this, + topic = group.topic, + peerAddress = Conversation.Group(group).peerAddress + ), + (PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + ) + + NotificationCompat.Builder(this, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_xmtp_white) + .setContentTitle(Conversation.Group(group).peerAddress.truncatedAddress()) + .setContentText("New Group Chat") + .setAutoCancel(true) + .setColor(ContextCompat.getColor(this, R.color.black)) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setStyle(NotificationCompat.BigTextStyle().bigText("New Group Chat")) + .setContentIntent(pendingIntent) + } else { + val conversation = + runBlocking { ClientManager.client.fetchConversation(topic, includeGroups = true) } + if (conversation == null) { + Log.e(TAG, topic) + Log.e(TAG, "No keys or conversation persisted") + return + } + val decodedMessage = if (conversation is Conversation.Group) { + runBlocking { conversation.group.processMessage(encryptedMessageData).decode() } + } else { + val envelope = EnvelopeBuilder.buildFromString(topic, Date(), encryptedMessageData) + conversation.decode(envelope) + } + val peerAddress = conversation.peerAddress - val body = decodedMessage.body - val title = peerAddress.truncatedAddress() + val body: String = if (decodedMessage.content() is String) { + decodedMessage.body + } else if (decodedMessage.content() is GroupMembershipChanges) { + val changes = decodedMessage.content() as? GroupMembershipChanges + "Membership Changed ${ + changes?.membersAddedList?.mapNotNull { it.accountAddress }.toString() + }" + } else { + "" + } + val title = peerAddress.truncatedAddress() - val pendingIntent = PendingIntent.getActivity( - this, - 0, - ConversationDetailActivity.intent( + val pendingIntent = PendingIntent.getActivity( this, - topic = topic, - peerAddress = peerAddress - ), - (PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) - ) - - val builder = NotificationCompat.Builder(this, CHANNEL_ID) - .setSmallIcon(R.drawable.ic_xmtp_white) - .setContentTitle(title) - .setContentText(body) - .setAutoCancel(true) - .setColor(ContextCompat.getColor(this, R.color.black)) - .setPriority(NotificationCompat.PRIORITY_DEFAULT) - .setStyle(NotificationCompat.BigTextStyle().bigText(body)) - .setContentIntent(pendingIntent) + 0, + ConversationDetailActivity.intent( + this, + topic = topic, + peerAddress = peerAddress + ), + (PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + ) + NotificationCompat.Builder(this, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_xmtp_white) + .setContentTitle(title) + .setContentText(body) + .setAutoCancel(true) + .setColor(ContextCompat.getColor(this, R.color.black)) + .setPriority(NotificationCompat.PRIORITY_DEFAULT) + .setStyle(NotificationCompat.BigTextStyle().bigText(body)) + .setContentIntent(pendingIntent) + } // Use the URL as the ID for now until one is passed back from the server. NotificationManagerCompat.from(this).apply { if (ActivityCompat.checkSelfPermission( diff --git a/library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt b/library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt index 190bd50a1..61fbd66b0 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt @@ -123,6 +123,7 @@ class ClientTest { ) ) assert(client.canMessageV3(listOf(client.address))) + assert(client.installationId.isNotEmpty()) } @Test diff --git a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt index 33cf5db6a..8478dadf3 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt @@ -494,7 +494,7 @@ class GroupTest { boClient.conversations.streamAll().test { val group = caroClient.conversations.newGroup(listOf(bo.walletAddress)) - assertEquals(group.id.toHex(), awaitItem().topic) + assertEquals(group.topic, awaitItem().topic) val conversation = alixClient.conversations.newConversation(bo.walletAddress) assertEquals(conversation.topic, awaitItem().topic) diff --git a/library/src/main/java/org/xmtp/android/library/Client.kt b/library/src/main/java/org/xmtp/android/library/Client.kt index 75630de31..11c1048c3 100644 --- a/library/src/main/java/org/xmtp/android/library/Client.kt +++ b/library/src/main/java/org/xmtp/android/library/Client.kt @@ -43,7 +43,6 @@ import org.xmtp.android.library.messages.walletAddress import org.xmtp.proto.message.api.v1.MessageApiOuterClass import org.xmtp.proto.message.api.v1.MessageApiOuterClass.BatchQueryResponse import org.xmtp.proto.message.api.v1.MessageApiOuterClass.QueryRequest -import uniffi.xmtpv3.FfiV2ApiClient import uniffi.xmtpv3.FfiXmtpClient import uniffi.xmtpv3.LegacyIdentitySource import uniffi.xmtpv3.createClient @@ -87,9 +86,9 @@ class Client() { lateinit var conversations: Conversations var logger: XMTPLogger = XMTPLogger() val libXMTPVersion: String = getVersionInfo() + var installationId: String = "" private var libXMTPClient: FfiXmtpClient? = null private var dbPath: String = "" - private lateinit var v2RustClient: FfiV2ApiClient companion object { private const val TAG = "Client" @@ -166,6 +165,7 @@ class Client() { apiClient: ApiClient, libXMTPClient: FfiXmtpClient? = null, dbPath: String = "", + installationId: String = "", ) : this() { this.address = address this.privateKeyBundleV1 = privateKeyBundleV1 @@ -175,6 +175,7 @@ class Client() { this.conversations = Conversations(client = this, libXMTPConversations = libXMTPClient?.conversations()) this.dbPath = dbPath + this.installationId = installationId } fun buildFrom( @@ -207,7 +208,8 @@ class Client() { privateKeyBundleV1 = bundle, apiClient = apiClient, libXMTPClient = v3Client, - dbPath = dbPath + dbPath = dbPath, + installationId = v3Client?.installationId()?.toHex() ?: "" ) } @@ -257,7 +259,8 @@ class Client() { privateKeyBundleV1, apiClient, libXMTPClient, - dbPath + dbPath, + libXMTPClient?.installationId()?.toHex() ?: "" ) client.ensureUserContactPublished() client @@ -304,7 +307,8 @@ class Client() { privateKeyBundleV1 = v1Bundle, apiClient = apiClient, libXMTPClient = v3Client, - dbPath = dbPath + dbPath = dbPath, + installationId = v3Client?.installationId()?.toHex() ?: "" ) } @@ -502,6 +506,7 @@ class Client() { suspend fun subscribe(topics: List): Flow { return subscribe2(flowOf(makeSubscribeRequest(topics))) } + suspend fun subscribe2(request: Flow): Flow { return apiClient.subscribe(request = request) } diff --git a/library/src/main/java/org/xmtp/android/library/Conversation.kt b/library/src/main/java/org/xmtp/android/library/Conversation.kt index dad52d452..62e738102 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversation.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversation.kt @@ -212,7 +212,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.topic.description is V2 -> conversationV2.topic - is Group -> group.id.toHex() + is Group -> group.topic } } diff --git a/library/src/main/java/org/xmtp/android/library/Conversations.kt b/library/src/main/java/org/xmtp/android/library/Conversations.kt index 50799d17a..10f73adeb 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversations.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversations.kt @@ -98,6 +98,12 @@ data class Conversations( ) } + fun fromWelcome(envelopeBytes: ByteArray): Group { + val group = libXMTPConversations?.processStreamedWelcomeMessage(envelopeBytes) + ?: throw XMTPException("Client does not support Groups") + return Group(client, group) + } + suspend fun newGroup( accountAddresses: List, permissions: GroupPermissions = GroupPermissions.EVERYONE_IS_ADMIN, diff --git a/library/src/main/java/org/xmtp/android/library/Group.kt b/library/src/main/java/org/xmtp/android/library/Group.kt index 84986d663..7812201fc 100644 --- a/library/src/main/java/org/xmtp/android/library/Group.kt +++ b/library/src/main/java/org/xmtp/android/library/Group.kt @@ -9,6 +9,7 @@ import org.xmtp.android.library.codecs.compress import org.xmtp.android.library.libxmtp.Message import org.xmtp.android.library.messages.DecryptedMessage import org.xmtp.android.library.messages.PagingInfoSortDirection +import org.xmtp.android.library.messages.Topic import org.xmtp.proto.message.api.v1.MessageApiOuterClass import uniffi.xmtpv3.FfiGroup import uniffi.xmtpv3.FfiGroupMetadata @@ -24,6 +25,9 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) { val id: ByteArray get() = libXMTPGroup.id() + val topic: String + get() = Topic.groupMessage(id.toHex()).description + val createdAt: Date get() = Date(libXMTPGroup.createdAtNs() / 1_000_000) @@ -119,6 +123,11 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) { } } + suspend fun processMessage(envelopeBytes: ByteArray): Message { + val message = libXMTPGroup.processStreamedGroupMessage(envelopeBytes) + return Message(client, message) + } + fun isActive(): Boolean { return libXMTPGroup.isActive() } diff --git a/library/src/main/java/org/xmtp/android/library/libxmtp/Message.kt b/library/src/main/java/org/xmtp/android/library/libxmtp/Message.kt index 4c828a1e7..b2105aa6b 100644 --- a/library/src/main/java/org/xmtp/android/library/libxmtp/Message.kt +++ b/library/src/main/java/org/xmtp/android/library/libxmtp/Message.kt @@ -5,6 +5,7 @@ import org.xmtp.android.library.DecodedMessage import org.xmtp.android.library.XMTPException import org.xmtp.android.library.codecs.EncodedContent import org.xmtp.android.library.messages.DecryptedMessage +import org.xmtp.android.library.messages.Topic import org.xmtp.android.library.toHex import uniffi.xmtpv3.FfiMessage import java.util.Date @@ -27,7 +28,7 @@ data class Message(val client: Client, private val libXMTPMessage: FfiMessage) { return DecodedMessage( id = id.toHex(), client = client, - topic = id.toHex(), + topic = Topic.groupMessage(convoId.toHex()).description, encodedContent = EncodedContent.parseFrom(libXMTPMessage.content), senderAddress = senderAddress, sent = sentAt @@ -40,7 +41,7 @@ data class Message(val client: Client, private val libXMTPMessage: FfiMessage) { fun decrypt(): DecryptedMessage { return DecryptedMessage( id = id.toHex(), - topic = convoId.toHex(), + topic = Topic.groupMessage(convoId.toHex()).description, encodedContent = decode().encodedContent, senderAddress = senderAddress, sentAt = Date() diff --git a/library/src/main/java/org/xmtp/android/library/messages/Topic.kt b/library/src/main/java/org/xmtp/android/library/messages/Topic.kt index 36f5761f0..f26f153a4 100644 --- a/library/src/main/java/org/xmtp/android/library/messages/Topic.kt +++ b/library/src/main/java/org/xmtp/android/library/messages/Topic.kt @@ -8,6 +8,8 @@ sealed class Topic { data class directMessageV1(val address1: String?, val address2: String?) : Topic() data class directMessageV2(val addresses: String?) : Topic() data class preferenceList(val identifier: String?) : Topic() + data class userWelcome(val installationId: String?) : Topic() + data class groupMessage(val groupId: String?) : Topic() /** * Getting the [Topic] structured depending if is [userPrivateStoreKeyBundle], [contact], @@ -29,10 +31,13 @@ sealed class Topic { is directMessageV2 -> wrap("m-$addresses") is preferenceList -> wrap("userpreferences-$identifier") + is groupMessage -> wrapMls("g-$groupId") + is userWelcome -> wrapMls("w-$installationId") } } private fun wrap(value: String): String = "/xmtp/0/$value/proto" + private fun wrapMls(value: String): String = "/xmtp/mls/1/$value/proto" companion object { /** diff --git a/library/src/main/java/org/xmtp/android/library/push/README.md b/library/src/main/java/org/xmtp/android/library/push/README.md index 7f92cb43b..c0e08aba3 100644 --- a/library/src/main/java/org/xmtp/android/library/push/README.md +++ b/library/src/main/java/org/xmtp/android/library/push/README.md @@ -82,7 +82,7 @@ These files can serve as the basis for what you might want to provide for your o ```kotlin val hmacKeysResult = ClientManager.client.conversations.getHmacKeys() - val subscriptions = conversations.map { + val subscriptions: MutableList = conversations.map { val hmacKeys = hmacKeysResult.hmacKeysMap val result = hmacKeys[it.topic]?.valuesList?.map { hmacKey -> Service.Subscription.HmacKey.newBuilder().also { sub_key -> @@ -96,11 +96,20 @@ These files can serve as the basis for what you might want to provide for your o sub.topic = it.topic sub.isSilent = it.version == Conversation.Version.V1 }.build() - } - - XMTPPush(context, "10.0.2.2:8080").subscribeWithMetadata(subscriptions) - ``` + }.toMutableList() + + // To get pushes for New Group (WelcomeMessages) + val welcomeTopic = Service.Subscription.newBuilder().also { sub -> + sub.topic = Topic.userWelcome(ClientManager.client.installationId).description + sub.isSilent = false + }.build() + subscriptions.add(welcomeTopic) + + XMTPPush(context, "10.0.2.2:8080").subscribeWithMetadata(subscriptions) + ``` ```kotlin XMTPPush(context, "10.0.2.2:8080").unsubscribe(conversations.map { it.topic }) ``` + +8. See example in [PushNotificationsService](https://github.com/xmtp/xmtp-android/blob/main/example/src/main/java/org/xmtp/android/example/pushnotifications/PushNotificationsService.kt) for how to decrypt the different messages. \ No newline at end of file diff --git a/library/src/main/java/xmtpv3.kt b/library/src/main/java/xmtpv3.kt index 414d12113..242321bd3 100644 --- a/library/src/main/java/xmtpv3.kt +++ b/library/src/main/java/xmtpv3.kt @@ -399,6 +399,8 @@ internal interface _UniFFILib : Library { ): Pointer fun uniffi_xmtpv3_fn_method_fficonversations_list(`ptr`: Pointer,`opts`: RustBuffer.ByValue, ): Pointer + fun uniffi_xmtpv3_fn_method_fficonversations_process_streamed_welcome_message(`ptr`: Pointer,`envelopeBytes`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, + ): Pointer fun uniffi_xmtpv3_fn_method_fficonversations_stream(`ptr`: Pointer,`callback`: Long, ): Pointer fun uniffi_xmtpv3_fn_method_fficonversations_stream_all_messages(`ptr`: Pointer,`messageCallback`: Long, @@ -421,6 +423,8 @@ internal interface _UniFFILib : Library { ): Byte fun uniffi_xmtpv3_fn_method_ffigroup_list_members(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue + fun uniffi_xmtpv3_fn_method_ffigroup_process_streamed_group_message(`ptr`: Pointer,`envelopeBytes`: RustBuffer.ByValue, + ): Pointer fun uniffi_xmtpv3_fn_method_ffigroup_remove_members(`ptr`: Pointer,`accountAddresses`: RustBuffer.ByValue, ): Pointer fun uniffi_xmtpv3_fn_method_ffigroup_send(`ptr`: Pointer,`contentBytes`: RustBuffer.ByValue, @@ -471,6 +475,8 @@ internal interface _UniFFILib : Library { ): Pointer fun uniffi_xmtpv3_fn_method_ffixmtpclient_conversations(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, ): Pointer + fun uniffi_xmtpv3_fn_method_ffixmtpclient_installation_id(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, + ): RustBuffer.ByValue fun uniffi_xmtpv3_fn_method_ffixmtpclient_register_identity(`ptr`: Pointer,`recoverableWalletSignature`: RustBuffer.ByValue, ): Pointer fun uniffi_xmtpv3_fn_method_ffixmtpclient_text_to_sign(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, @@ -657,6 +663,8 @@ internal interface _UniFFILib : Library { ): Short fun uniffi_xmtpv3_checksum_method_fficonversations_list( ): Short + fun uniffi_xmtpv3_checksum_method_fficonversations_process_streamed_welcome_message( + ): Short fun uniffi_xmtpv3_checksum_method_fficonversations_stream( ): Short fun uniffi_xmtpv3_checksum_method_fficonversations_stream_all_messages( @@ -677,6 +685,8 @@ internal interface _UniFFILib : Library { ): Short fun uniffi_xmtpv3_checksum_method_ffigroup_list_members( ): Short + fun uniffi_xmtpv3_checksum_method_ffigroup_process_streamed_group_message( + ): Short fun uniffi_xmtpv3_checksum_method_ffigroup_remove_members( ): Short fun uniffi_xmtpv3_checksum_method_ffigroup_send( @@ -717,6 +727,8 @@ internal interface _UniFFILib : Library { ): Short fun uniffi_xmtpv3_checksum_method_ffixmtpclient_conversations( ): Short + fun uniffi_xmtpv3_checksum_method_ffixmtpclient_installation_id( + ): Short fun uniffi_xmtpv3_checksum_method_ffixmtpclient_register_identity( ): Short fun uniffi_xmtpv3_checksum_method_ffixmtpclient_text_to_sign( @@ -796,6 +808,9 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) { if (lib.uniffi_xmtpv3_checksum_method_fficonversations_list() != 44067.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (lib.uniffi_xmtpv3_checksum_method_fficonversations_process_streamed_welcome_message() != 55636.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (lib.uniffi_xmtpv3_checksum_method_fficonversations_stream() != 60583.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -826,6 +841,9 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) { if (lib.uniffi_xmtpv3_checksum_method_ffigroup_list_members() != 15786.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (lib.uniffi_xmtpv3_checksum_method_ffigroup_process_streamed_group_message() != 54382.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (lib.uniffi_xmtpv3_checksum_method_ffigroup_remove_members() != 1645.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -886,6 +904,9 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) { if (lib.uniffi_xmtpv3_checksum_method_ffixmtpclient_conversations() != 31628.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } + if (lib.uniffi_xmtpv3_checksum_method_ffixmtpclient_installation_id() != 62523.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } if (lib.uniffi_xmtpv3_checksum_method_ffixmtpclient_register_identity() != 64634.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } @@ -1295,6 +1316,7 @@ public interface FfiConversationsInterface { @Throws(GenericException::class) suspend fun `createGroup`(`accountAddresses`: List, `permissions`: GroupPermissions?): FfiGroup@Throws(GenericException::class) suspend fun `list`(`opts`: FfiListConversationsOptions): List@Throws(GenericException::class) + fun `processStreamedWelcomeMessage`(`envelopeBytes`: ByteArray): FfiGroup@Throws(GenericException::class) suspend fun `stream`(`callback`: FfiConversationCallback): FfiStreamCloser@Throws(GenericException::class) suspend fun `streamAllMessages`(`messageCallback`: FfiMessageCallback): FfiStreamCloser@Throws(GenericException::class) suspend fun `sync`() @@ -1360,6 +1382,18 @@ class FfiConversations( ) } + @Throws(GenericException::class)override fun `processStreamedWelcomeMessage`(`envelopeBytes`: ByteArray): FfiGroup = + callWithPointer { + rustCallWithError(GenericException) { _status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_fficonversations_process_streamed_welcome_message(it, + FfiConverterByteArray.lower(`envelopeBytes`), + _status) + } + }.let { + FfiConverterTypeFfiGroup.lift(it) + } + + @Throws(GenericException::class) @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") override suspend fun `stream`(`callback`: FfiConversationCallback) : FfiStreamCloser { @@ -1461,6 +1495,7 @@ public interface FfiGroupInterface { fun `id`(): ByteArray@Throws(GenericException::class) fun `isActive`(): Boolean@Throws(GenericException::class) fun `listMembers`(): List@Throws(GenericException::class) + suspend fun `processStreamedGroupMessage`(`envelopeBytes`: ByteArray): FfiMessage@Throws(GenericException::class) suspend fun `removeMembers`(`accountAddresses`: List)@Throws(GenericException::class) suspend fun `send`(`contentBytes`: ByteArray)@Throws(GenericException::class) suspend fun `stream`(`messageCallback`: FfiMessageCallback): FfiStreamCloser@Throws(GenericException::class) @@ -1578,6 +1613,26 @@ class FfiGroup( } + @Throws(GenericException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `processStreamedGroupMessage`(`envelopeBytes`: ByteArray) : FfiMessage { + return uniffiRustCallAsync( + callWithPointer { thisPtr -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_process_streamed_group_message( + thisPtr, + FfiConverterByteArray.lower(`envelopeBytes`), + ) + }, + { future, continuation -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_poll_rust_buffer(future, continuation) }, + { future, continuation -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_complete_rust_buffer(future, continuation) }, + { future -> _UniFFILib.INSTANCE.ffi_xmtpv3_rust_future_free_rust_buffer(future) }, + // lift function + { FfiConverterTypeFfiMessage.lift(it) }, + // Error FFI converter + GenericException.ErrorHandler, + ) + } + @Throws(GenericException::class) @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") override suspend fun `removeMembers`(`accountAddresses`: List) { @@ -2136,7 +2191,8 @@ public interface FfiXmtpClientInterface { fun `accountAddress`(): String@Throws(GenericException::class) suspend fun `canMessage`(`accountAddresses`: List): List - fun `conversations`(): FfiConversations@Throws(GenericException::class) + fun `conversations`(): FfiConversations + fun `installationId`(): ByteArray@Throws(GenericException::class) suspend fun `registerIdentity`(`recoverableWalletSignature`: ByteArray?) fun `textToSign`(): String? companion object @@ -2202,6 +2258,17 @@ class FfiXmtpClient( FfiConverterTypeFfiConversations.lift(it) } + override fun `installationId`(): ByteArray = + callWithPointer { + rustCall() { _status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffixmtpclient_installation_id(it, + + _status) + } + }.let { + FfiConverterByteArray.lift(it) + } + @Throws(GenericException::class) @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") diff --git a/library/src/main/jniLibs/arm64-v8a/libuniffi_xmtpv3.so b/library/src/main/jniLibs/arm64-v8a/libuniffi_xmtpv3.so index f4ad741bd..29b7e22df 100755 Binary files a/library/src/main/jniLibs/arm64-v8a/libuniffi_xmtpv3.so and b/library/src/main/jniLibs/arm64-v8a/libuniffi_xmtpv3.so differ diff --git a/library/src/main/jniLibs/armeabi-v7a/libuniffi_xmtpv3.so b/library/src/main/jniLibs/armeabi-v7a/libuniffi_xmtpv3.so index 4be31d1d7..c9dfe8fee 100755 Binary files a/library/src/main/jniLibs/armeabi-v7a/libuniffi_xmtpv3.so and b/library/src/main/jniLibs/armeabi-v7a/libuniffi_xmtpv3.so differ diff --git a/library/src/main/jniLibs/x86/libuniffi_xmtpv3.so b/library/src/main/jniLibs/x86/libuniffi_xmtpv3.so index f989d3769..7006a1741 100755 Binary files a/library/src/main/jniLibs/x86/libuniffi_xmtpv3.so and b/library/src/main/jniLibs/x86/libuniffi_xmtpv3.so differ diff --git a/library/src/main/jniLibs/x86_64/libuniffi_xmtpv3.so b/library/src/main/jniLibs/x86_64/libuniffi_xmtpv3.so index afbce8ffb..559d9f473 100755 Binary files a/library/src/main/jniLibs/x86_64/libuniffi_xmtpv3.so and b/library/src/main/jniLibs/x86_64/libuniffi_xmtpv3.so differ