diff --git a/.gitignore b/.gitignore index c9e7a53d2..2467a27f7 100644 --- a/.gitignore +++ b/.gitignore @@ -92,3 +92,6 @@ lint/tmp/ # lint/reports/ *google-services.json + +# OSX +.DS_Store diff --git a/README.md b/README.md index 402d79938..61bf138a2 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ To learn about example app push notifications, see [Enable the quickstart app to ## Reference docs -> **View the reference** +> **View the reference** > Access the [Kotlin client SDK reference documentation](https://xmtp.github.io/xmtp-android/). ## Install from Maven Central @@ -51,15 +51,15 @@ val messages = conversation.messages() // Send a message conversation.send(text = "gm") // Listen for new messages in the conversation -conversation.streamMessages().collect { +conversation.streamMessages().collect { print("${message.senderAddress}: ${message.body}") } ``` ## Use local storage -> **Important** -> If you are building a production-grade app, be sure to use an architecture that includes a local cache backed by an XMTP SDK. +> **Important** +> If you are building a production-grade app, be sure to use an architecture that includes a local cache backed by an XMTP SDK. To learn more, see [Use a local cache](https://xmtp.org/docs/build/local-first). @@ -70,7 +70,7 @@ A client is created with `Client().create(account: SigningKey): Client` that req 1. To sign the newly generated key bundle. This happens only the very first time when a key bundle is not found in storage. 2. To sign a random salt used to encrypt the key bundle in storage. This happens every time the client is started, including the very first time. -> **Note** +> **Note** > The client connects to the XMTP `dev` environment by default. [Use `ClientOptions`](#configure-the-client) to change this and other parameters of the network connection. ```kotlin @@ -106,10 +106,10 @@ val client = Client().buildFrom(bundle = keys, options = options) You can configure the client with these parameters of `Client.create`: -| Parameter | Default | Description | -| --------- | ------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| env | `DEV` | Connect to the specified XMTP network environment. Valid values include `DEV`, `.PRODUCTION`, or `LOCAL`. For important details about working with these environments, see [XMTP `production` and `dev` network environments](#xmtp-production-and-dev-network-environments). | -| appVersion | `undefined` | Add a client app version identifier that's included with API requests.
For example, you can use the following format: `appVersion: APP_NAME + '/' + APP_VERSION`.
Setting this value provides telemetry that shows which apps are using the XMTP client SDK. This information can help XMTP developers provide app support, especially around communicating important SDK updates, including deprecations and required upgrades. | +| Parameter | Default | Description | +| ---------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| env | `DEV` | Connect to the specified XMTP network environment. Valid values include `DEV`, `.PRODUCTION`, or `LOCAL`. For important details about working with these environments, see [XMTP `production` and `dev` network environments](#xmtp-production-and-dev-network-environments). | +| appVersion | `undefined` | Add a client app version identifier that's included with API requests.
For example, you can use the following format: `appVersion: APP_NAME + '/' + APP_VERSION`.
Setting this value provides telemetry that shows which apps are using the XMTP client SDK. This information can help XMTP developers provide app support, especially around communicating important SDK updates, including deprecations and required upgrades. | **Configure `env`** @@ -119,7 +119,7 @@ val options = ClientOptions(api = ClientOptions.Api(env = XMTPEnvironment.PRODUC val client = Client().create(account = account, options = options) ``` -> **Note** +> **Note** > The `apiUrl`, `keyStoreType`, `codecs`, and `maxContentSize` parameters from the XMTP client SDK for JavaScript (xmtp-js) are not yet supported. ## Handle conversations @@ -214,7 +214,7 @@ conversation.streamMessages().collect { if (it.senderAddress == client.address) { // This message was sent from me } - + print("New message from ${it.senderAddress}: ${it.body}") } ``` @@ -285,11 +285,11 @@ To learn more, see [Request and respect user consent](https://xmtp.org/docs/buil ## Handle different types of content -All the send functions support `SendOptions` as an optional parameter. The `contentType` option allows specifying different types of content than the default simple string, which is identified with content type identifier `ContentTypeText`. +All the send functions support `SendOptions` as an optional parameter. The `contentType` option allows specifying different types of content than the default simple string, which is identified with content type identifier `ContentTypeText`. To learn more about content types, see [Content types with XMTP](https://xmtp.org/docs/concepts/content-types). -Support for other types of content can be added by registering additional `ContentCodec`s with the Client. Every codec is associated with a content type identifier, `ContentTypeId`, which is used to signal to the Client which codec should be used to process the content that is being sent or received. +Support for other types of content can be added by registering additional `ContentCodec`s with the Client. Every codec is associated with a content type identifier, `ContentTypeId`, which is used to signal to the Client which codec should be used to process the content that is being sent or received. ```kotlin // Assuming we've loaded a fictional NumberCodec that can be used to encode numbers, @@ -302,7 +302,7 @@ aliceConversation.send(content = 3.14, options = options) As shown in the example above, you must provide a `contentFallback` value. Use it to provide an alt text-like description of the original content. Providing a `contentFallback` value enables clients that don't support the content type to still display something meaningful. -> **Caution** +> **Caution** > If you don't provide a `contentFallback` value, clients that don't support the content type will display an empty message. This results in a poor user experience and breaks interoperability. ### Handle custom content types @@ -334,9 +334,9 @@ Older versions of the SDK will eventually be deprecated, which means: The following table provides the deprecation schedule. -| Announced | Effective | Minimum Version | Rationale | -| ---------- | ---------- | --------------- | ----------------------------------------------------------------------------------------------------------------- | -| There are no deprecations scheduled for `xmtp-android` at this time. | | | | +| Announced | Effective | Minimum Version | Rationale | +| -------------------------------------------------------------------- | --------- | --------------- | --------- | +| There are no deprecations scheduled for `xmtp-android` at this time. | | | | Bug reports, feature requests, and PRs are welcome in accordance with these [contribution guidelines](https://github.com/xmtp/xmtp-android/blob/main/CONTRIBUTING.md). @@ -347,7 +347,7 @@ XMTP provides both `production` and `dev` network environments to support the de The `production` and `dev` networks are completely separate and not interchangeable. For example, for a given blockchain account, its XMTP identity on `dev` network is completely distinct from its XMTP identity on the `production` network, as are the messages associated with these identities. In addition, XMTP identities and messages created on the `dev` network can't be accessed from or moved to the `production` network, and vice versa. -> **Note** +> **Note** > When you [create a client](#create-a-client), it connects to the XMTP `dev` environment by default. To learn how to use the `env` parameter to set your client's network environment, see [Configure the client](#configure-the-client). The `env` parameter accepts one of three valid values: `dev`, `production`, or `local`. Here are some best practices for when to use each environment: diff --git a/example/src/main/java/org/xmtp/android/example/conversation/ConversationViewHolder.kt b/example/src/main/java/org/xmtp/android/example/conversation/ConversationViewHolder.kt index 25e0654d5..34a1ab895 100644 --- a/example/src/main/java/org/xmtp/android/example/conversation/ConversationViewHolder.kt +++ b/example/src/main/java/org/xmtp/android/example/conversation/ConversationViewHolder.kt @@ -7,7 +7,6 @@ import org.xmtp.android.example.R import org.xmtp.android.example.databinding.ListItemConversationBinding import org.xmtp.android.example.extension.truncatedAddress import org.xmtp.android.library.Conversation -import org.xmtp.android.library.codecs.GroupUpdatedCodec import org.xmtp.proto.mls.message.contents.TranscriptMessages import org.xmtp.proto.mls.message.contents.TranscriptMessages.GroupUpdated 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 7adc83892..a1c7655f6 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/ClientTest.kt @@ -509,13 +509,14 @@ class ClientTest { } var state = runBlocking { alixClient3.inboxState(true) } - assertEquals(state.installationIds.size, 3) + assertEquals(state.installations.size, 3) + assert(state.installations.first().createdAt != null) runBlocking { alixClient3.revokeAllOtherInstallations(alixWallet) } state = runBlocking { alixClient3.inboxState(true) } - assertEquals(state.installationIds.size, 1) + assertEquals(state.installations.size, 1) } } 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 796f3f19f..58b6c5423 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/GroupTest.kt @@ -84,8 +84,8 @@ class GroupTest { alixGroup.addMembers(listOf(caro.walletAddress)) boGroup.sync() } - assertEquals(alixGroup.members().size, 3) - assertEquals(boGroup.members().size, 3) + assertEquals(runBlocking { alixGroup.members().size }, 3) + assertEquals(runBlocking { boGroup.members().size }, 3) // All members also defaults remove to admin only now. assertThrows(XMTPException::class.java) { @@ -95,8 +95,8 @@ class GroupTest { } } - assertEquals(alixGroup.members().size, 3) - assertEquals(boGroup.members().size, 3) + assertEquals(runBlocking { alixGroup.members().size }, 3) + assertEquals(runBlocking { boGroup.members().size }, 3) assertEquals(boGroup.permissionPolicySet().addMemberPolicy, PermissionOption.Allow) assertEquals(alixGroup.permissionPolicySet().addMemberPolicy, PermissionOption.Allow) @@ -135,31 +135,31 @@ class GroupTest { alixGroup.sync() } - assertEquals(alixGroup.members().size, 3) - assertEquals(boGroup.members().size, 3) + assertEquals(runBlocking { alixGroup.members().size }, 3) + assertEquals(runBlocking { boGroup.members().size }, 3) assertThrows(XMTPException::class.java) { runBlocking { alixGroup.removeMembers(listOf(caro.walletAddress)) } } runBlocking { boGroup.sync() } - assertEquals(alixGroup.members().size, 3) - assertEquals(boGroup.members().size, 3) + assertEquals(runBlocking { alixGroup.members().size }, 3) + assertEquals(runBlocking { boGroup.members().size }, 3) runBlocking { boGroup.removeMembers(listOf(caro.walletAddress)) alixGroup.sync() } - assertEquals(alixGroup.members().size, 2) - assertEquals(boGroup.members().size, 2) + assertEquals(runBlocking { alixGroup.members().size }, 2) + assertEquals(runBlocking { boGroup.members().size }, 2) assertThrows(XMTPException::class.java) { runBlocking { alixGroup.addMembers(listOf(caro.walletAddress)) } } runBlocking { boGroup.sync() } - assertEquals(alixGroup.members().size, 2) - assertEquals(boGroup.members().size, 2) + assertEquals(runBlocking { alixGroup.members().size }, 2) + assertEquals(runBlocking { boGroup.members().size }, 2) assertEquals(boGroup.permissionPolicySet().addMemberPolicy, PermissionOption.Admin) assertEquals(alixGroup.permissionPolicySet().addMemberPolicy, PermissionOption.Admin) @@ -183,7 +183,7 @@ class GroupTest { ) } assertEquals( - group.members().map { it.inboxId }.sorted(), + runBlocking { group.members().map { it.inboxId }.sorted() }, listOf( caroClient.inboxId, alixClient.inboxId, @@ -200,7 +200,7 @@ class GroupTest { ) assertEquals( - group.peerInboxIds().sorted(), + runBlocking { group.peerInboxIds().sorted() }, listOf( caroClient.inboxId, alixClient.inboxId, @@ -238,7 +238,7 @@ class GroupTest { val group = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress)) } runBlocking { group.addMembers(listOf(caro.walletAddress)) } assertEquals( - group.members().map { it.inboxId }.sorted(), + runBlocking { group.members().map { it.inboxId }.sorted() }, listOf( caroClient.inboxId, alixClient.inboxId, @@ -259,7 +259,7 @@ class GroupTest { } runBlocking { group.removeMembers(listOf(caro.walletAddress)) } assertEquals( - group.members().map { it.inboxId }.sorted(), + runBlocking { group.members().map { it.inboxId }.sorted() }, listOf( alixClient.inboxId, boClient.inboxId @@ -291,7 +291,7 @@ class GroupTest { boGroup.sync() } assertEquals( - boGroup.members().map { it.inboxId }.sorted(), + runBlocking { boGroup.members().map { it.inboxId }.sorted() }, listOf( alixClient.inboxId, boClient.inboxId @@ -303,7 +303,7 @@ class GroupTest { val group = runBlocking { boClient.conversations.newGroup(listOf(alix.walletAddress)) } runBlocking { group.addMembersByInboxId(listOf(caroClient.inboxId)) } assertEquals( - group.members().map { it.inboxId }.sorted(), + runBlocking { group.members().map { it.inboxId }.sorted() }, listOf( caroClient.inboxId, alixClient.inboxId, @@ -324,7 +324,7 @@ class GroupTest { } runBlocking { group.removeMembersByInboxId(listOf(caroClient.inboxId)) } assertEquals( - group.members().map { it.inboxId }.sorted(), + runBlocking { group.members().map { it.inboxId }.sorted() }, listOf( alixClient.inboxId, boClient.inboxId diff --git a/library/src/androidTest/java/org/xmtp/android/library/GroupUpdatedTest.kt b/library/src/androidTest/java/org/xmtp/android/library/GroupUpdatedTest.kt index 15938fc04..2bedbeb63 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/GroupUpdatedTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/GroupUpdatedTest.kt @@ -89,11 +89,11 @@ class GroupUpdatedTest { } val messages = group.messages() assertEquals(messages.size, 1) - assertEquals(group.members().size, 3) + assertEquals(runBlocking { group.members().size }, 3) runBlocking { group.removeMembers(listOf(caro.walletAddress)) } val updatedMessages = group.messages() assertEquals(updatedMessages.size, 2) - assertEquals(group.members().size, 2) + assertEquals(runBlocking { group.members().size }, 2) val content: GroupUpdated? = updatedMessages.first().content() assertEquals( @@ -119,7 +119,7 @@ class GroupUpdatedTest { } val messages = group.messages() assertEquals(messages.size, 1) - assertEquals(group.members().size, 3) + assertEquals(runBlocking { group.members().size }, 3) runBlocking { group.send( content = membershipChange, diff --git a/library/src/androidTest/java/org/xmtp/android/library/V3ClientTest.kt b/library/src/androidTest/java/org/xmtp/android/library/V3ClientTest.kt index 8b63f59a2..f260d4968 100644 --- a/library/src/androidTest/java/org/xmtp/android/library/V3ClientTest.kt +++ b/library/src/androidTest/java/org/xmtp/android/library/V3ClientTest.kt @@ -82,7 +82,7 @@ class V3ClientTest { fun testsCanCreateGroup() { val group = runBlocking { boV3Client.conversations.newGroup(listOf(caroV2V3.walletAddress)) } assertEquals( - group.members().map { it.inboxId }.sorted(), + runBlocking { group.members().map { it.inboxId }.sorted() }, listOf(caroV2V3Client.inboxId, boV3Client.inboxId).sorted() ) diff --git a/library/src/main/java/libxmtp-version.txt b/library/src/main/java/libxmtp-version.txt index 580fc68e3..e188c0c23 100644 --- a/library/src/main/java/libxmtp-version.txt +++ b/library/src/main/java/libxmtp-version.txt @@ -1,3 +1,3 @@ -Version: 34d32499 +Version: 4f529aeb Branch: main -Date: 2024-09-19 21:05:16 +0000 +Date: 2024-09-26 04:05:26 +0000 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 b431967fc..b8db34d09 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversation.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversation.kt @@ -3,6 +3,7 @@ package org.xmtp.android.library import android.util.Log import com.google.protobuf.kotlin.toByteString import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.runBlocking import org.xmtp.android.library.codecs.EncodedContent import org.xmtp.android.library.libxmtp.MessageV3 import org.xmtp.android.library.messages.DecryptedMessage @@ -56,7 +57,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.peerAddress is V2 -> conversationV2.peerAddress - is Group -> group.peerInboxIds().joinToString(",") + is Group -> runBlocking { group.peerInboxIds().joinToString(",") } } } @@ -65,7 +66,7 @@ sealed class Conversation { return when (this) { is V1 -> listOf(conversationV1.peerAddress) is V2 -> listOf(conversationV2.peerAddress) - is Group -> group.peerInboxIds() + is Group -> runBlocking { group.peerInboxIds() } } } 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 6eb221a2a..176a25f8f 100644 --- a/library/src/main/java/org/xmtp/android/library/Group.kt +++ b/library/src/main/java/org/xmtp/android/library/Group.kt @@ -247,11 +247,11 @@ class Group(val client: Client, private val libXMTPGroup: FfiGroup) { } } - fun members(): List { + suspend fun members(): List { return libXMTPGroup.listMembers().map { Member(it) } } - fun peerInboxIds(): List { + suspend fun peerInboxIds(): List { val ids = members().map { it.inboxId }.toMutableList() ids.remove(client.inboxId) return ids diff --git a/library/src/main/java/org/xmtp/android/library/libxmtp/InboxState.kt b/library/src/main/java/org/xmtp/android/library/libxmtp/InboxState.kt index efa97b756..376fe9f67 100644 --- a/library/src/main/java/org/xmtp/android/library/libxmtp/InboxState.kt +++ b/library/src/main/java/org/xmtp/android/library/libxmtp/InboxState.kt @@ -1,6 +1,5 @@ package uniffi.xmtpv3.org.xmtp.android.library.libxmtp -import org.xmtp.android.library.toHex import uniffi.xmtpv3.FfiInboxState class InboxState(private val ffiInboxState: FfiInboxState) { @@ -9,8 +8,8 @@ class InboxState(private val ffiInboxState: FfiInboxState) { val addresses: List get() = ffiInboxState.accountAddresses - val installationIds: List - get() = ffiInboxState.installationIds.map { it.toHex() } + val installations: List + get() = ffiInboxState.installations.map { Installation(it) } val recoveryAddress: String get() = ffiInboxState.recoveryAddress diff --git a/library/src/main/java/org/xmtp/android/library/libxmtp/Installation.kt b/library/src/main/java/org/xmtp/android/library/libxmtp/Installation.kt new file mode 100644 index 000000000..8eeb767c9 --- /dev/null +++ b/library/src/main/java/org/xmtp/android/library/libxmtp/Installation.kt @@ -0,0 +1,14 @@ +package uniffi.xmtpv3.org.xmtp.android.library.libxmtp + +import org.xmtp.android.library.toHex +import uniffi.xmtpv3.FfiInstallation +import java.util.Date + +class Installation(private val ffiInstallation: FfiInstallation) { + val installationId: String + get() = ffiInstallation.id.toHex() + val createdAt: Date? + get() = ffiInstallation.clientTimestampNs?.let { + Date(it.toLong()) + } +} diff --git a/library/src/main/java/xmtpv3.kt b/library/src/main/java/xmtpv3.kt index fc40684ab..f4ee4ec8c 100644 --- a/library/src/main/java/xmtpv3.kt +++ b/library/src/main/java/xmtpv3.kt @@ -981,8 +981,8 @@ internal interface UniffiLib : Library { ): Byte fun uniffi_xmtpv3_fn_method_ffigroup_list_members( - `ptr`: Pointer, uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue + `ptr`: Pointer, + ): Long fun uniffi_xmtpv3_fn_method_ffigroup_process_streamed_group_message( `ptr`: Pointer, `envelopeBytes`: RustBuffer.ByValue, @@ -2076,7 +2076,7 @@ private fun uniffiCheckApiChecksums(lib: UniffiLib) { if (lib.uniffi_xmtpv3_checksum_method_ffigroup_is_super_admin() != 61614.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } - if (lib.uniffi_xmtpv3_checksum_method_ffigroup_list_members() != 61034.toShort()) { + if (lib.uniffi_xmtpv3_checksum_method_ffigroup_list_members() != 3945.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_xmtpv3_checksum_method_ffigroup_process_streamed_group_message() != 19069.toShort()) { @@ -3179,7 +3179,7 @@ public interface FfiGroupInterface { fun `isSuperAdmin`(`inboxId`: kotlin.String): kotlin.Boolean - fun `listMembers`(): List + suspend fun `listMembers`(): List suspend fun `processStreamedGroupMessage`(`envelopeBytes`: kotlin.ByteArray): FfiMessage @@ -3651,15 +3651,33 @@ open class FfiGroup : Disposable, AutoCloseable, FfiGroupInterface { @Throws(GenericException::class) - override fun `listMembers`(): List { - return FfiConverterSequenceTypeFfiGroupMember.lift( - callWithPointer { - uniffiRustCallWithError(GenericException) { _status -> - UniffiLib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_list_members( - it, _status + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `listMembers`(): List { + return uniffiRustCallAsync( + callWithPointer { thisPtr -> + UniffiLib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_list_members( + thisPtr, + ) - } - } + }, + { future, callback, continuation -> + UniffiLib.INSTANCE.ffi_xmtpv3_rust_future_poll_rust_buffer( + future, + callback, + 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 + { FfiConverterSequenceTypeFfiGroupMember.lift(it) }, + // Error FFI converter + GenericException.ErrorHandler, ) } @@ -7076,7 +7094,7 @@ public object FfiConverterTypeFfiGroupMember : FfiConverterRustBuffer, + var `installations`: List, var `accountAddresses`: List, ) { @@ -7088,7 +7106,7 @@ public object FfiConverterTypeFfiInboxState : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): FfiInstallation { + return FfiInstallation( + FfiConverterByteArray.read(buf), + FfiConverterOptionalULong.read(buf), + ) + } + + override fun allocationSize(value: FfiInstallation) = ( + FfiConverterByteArray.allocationSize(value.`id`) + + FfiConverterOptionalULong.allocationSize(value.`clientTimestampNs`) + ) + + override fun write(value: FfiInstallation, buf: ByteBuffer) { + FfiConverterByteArray.write(value.`id`, buf) + FfiConverterOptionalULong.write(value.`clientTimestampNs`, buf) + } +} + + data class FfiListConversationsOptions( var `createdAfterNs`: kotlin.Long?, var `createdBeforeNs`: kotlin.Long?, @@ -8203,6 +8249,33 @@ public object FfiConverterTypeFfiV2SubscriptionCallback : FfiConverterCallbackInterface() +public object FfiConverterOptionalULong : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): kotlin.ULong? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterULong.read(buf) + } + + override fun allocationSize(value: kotlin.ULong?): ULong { + if (value == null) { + return 1UL + } else { + return 1UL + FfiConverterULong.allocationSize(value) + } + } + + override fun write(value: kotlin.ULong?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterULong.write(value, buf) + } + } +} + + public object FfiConverterOptionalLong : FfiConverterRustBuffer { override fun read(buf: ByteBuffer): kotlin.Long? { if (buf.get().toInt() == 0) { @@ -8616,6 +8689,30 @@ public object FfiConverterSequenceTypeFfiGroupMember : } +public object FfiConverterSequenceTypeFfiInstallation : + FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeFfiInstallation.read(buf) + } + } + + override fun allocationSize(value: List): ULong { + val sizeForLength = 4UL + val sizeForItems = value.map { FfiConverterTypeFfiInstallation.allocationSize(it) }.sum() + return sizeForLength + sizeForItems + } + + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.iterator().forEach { + FfiConverterTypeFfiInstallation.write(it, buf) + } + } +} + + public object FfiConverterSequenceTypeFfiMessage : FfiConverterRustBuffer> { override fun read(buf: ByteBuffer): List { val len = buf.getInt() diff --git a/library/src/main/jniLibs/arm64-v8a/libuniffi_xmtpv3.so b/library/src/main/jniLibs/arm64-v8a/libuniffi_xmtpv3.so index 51254ed12..77929a222 100644 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 c743f2891..4f3b41364 100644 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 01d1c1c8d..df9465ed8 100644 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 e3a3aeb67..debc92755 100644 Binary files a/library/src/main/jniLibs/x86_64/libuniffi_xmtpv3.so and b/library/src/main/jniLibs/x86_64/libuniffi_xmtpv3.so differ