From 079cf3c31155f721d15aa634ac1e1d08ca46a438 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 1 Jun 2026 09:16:28 +0000 Subject: [PATCH 1/2] chore: update Android SDK to 25.0.0 --- CHANGELOG.md | 7 + README.md | 4 +- docs/examples/java/avatars/get-screenshot.md | 4 +- .../examples/kotlin/avatars/get-screenshot.md | 6 +- library/src/main/java/io/appwrite/Client.kt | 145 ++++++++++++++---- .../enums/{Theme.kt => BrowserTheme.kt} | 2 +- .../main/java/io/appwrite/models/Presence.kt | 37 +---- .../java/io/appwrite/models/PresenceList.kt | 18 +-- .../main/java/io/appwrite/services/Avatars.kt | 2 +- .../java/io/appwrite/services/Presences.kt | 144 +++-------------- 10 files changed, 166 insertions(+), 203 deletions(-) rename library/src/main/java/io/appwrite/enums/{Theme.kt => BrowserTheme.kt} (82%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31674228..ed8c1769 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Change Log +## 25.0.0 + +* Breaking: `avatars.getScreenshot` `theme` parameter now uses the `BrowserTheme` enum +* Breaking: Removed generic type parameters from `presences` service methods +* Added: `BrowserTheme` enum +* Updated: `Presence` model is now concrete and adds a `metadata` field + ## 24.1.1 * Fixed: Removed `Advisor` service and `Insight`, `InsightCTA`, `InsightList`, `Report`, `ReportList` models (admin-only endpoints, not intended for client SDKs) diff --git a/README.md b/README.md index 2be70474..fadaf5f7 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ repositories { Next, add the dependency to your project's `build.gradle(.kts)` file: ```groovy -implementation("io.appwrite:sdk-for-android:24.1.1") +implementation("io.appwrite:sdk-for-android:25.0.0") ``` ### Maven @@ -49,7 +49,7 @@ Add this to your project's `pom.xml` file: io.appwrite sdk-for-android - 24.1.1 + 25.0.0 ``` diff --git a/docs/examples/java/avatars/get-screenshot.md b/docs/examples/java/avatars/get-screenshot.md index 2f261c5f..c016ea5f 100644 --- a/docs/examples/java/avatars/get-screenshot.md +++ b/docs/examples/java/avatars/get-screenshot.md @@ -2,7 +2,7 @@ import io.appwrite.Client; import io.appwrite.coroutines.CoroutineCallback; import io.appwrite.services.Avatars; -import io.appwrite.enums.Theme; +import io.appwrite.enums.BrowserTheme; import io.appwrite.enums.Timezone; import io.appwrite.enums.BrowserPermission; import io.appwrite.enums.ImageFormat; @@ -22,7 +22,7 @@ avatars.getScreenshot( 1920, // viewportWidth (optional) 1080, // viewportHeight (optional) 2, // scale (optional) - Theme.LIGHT, // theme (optional) + BrowserTheme.LIGHT, // theme (optional) "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15", // userAgent (optional) true, // fullpage (optional) "en-US", // locale (optional) diff --git a/docs/examples/kotlin/avatars/get-screenshot.md b/docs/examples/kotlin/avatars/get-screenshot.md index 94551fdb..1093c060 100644 --- a/docs/examples/kotlin/avatars/get-screenshot.md +++ b/docs/examples/kotlin/avatars/get-screenshot.md @@ -2,7 +2,7 @@ import io.appwrite.Client import io.appwrite.coroutines.CoroutineCallback import io.appwrite.services.Avatars -import io.appwrite.enums.Theme +import io.appwrite.enums.BrowserTheme import io.appwrite.enums.Timezone import io.appwrite.enums.BrowserPermission import io.appwrite.enums.ImageFormat @@ -22,11 +22,11 @@ val result = avatars.getScreenshot( viewportWidth = 1920, // (optional) viewportHeight = 1080, // (optional) scale = 2, // (optional) - theme = theme.LIGHT, // (optional) + theme = BrowserTheme.LIGHT, // (optional) userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15", // (optional) fullpage = true, // (optional) locale = "en-US", // (optional) - timezone = timezone.AFRICA_ABIDJAN, // (optional) + timezone = Timezone.AFRICA_ABIDJAN, // (optional) latitude = 37.7749, // (optional) longitude = -122.4194, // (optional) accuracy = 100, // (optional) diff --git a/library/src/main/java/io/appwrite/Client.kt b/library/src/main/java/io/appwrite/Client.kt index ef9d8f51..f43ec95a 100644 --- a/library/src/main/java/io/appwrite/Client.kt +++ b/library/src/main/java/io/appwrite/Client.kt @@ -13,6 +13,9 @@ import io.appwrite.models.UploadProgress import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.suspendCancellableCoroutine import okhttp3.* import okhttp3.Headers.Companion.toHeaders @@ -30,6 +33,9 @@ import java.net.CookieManager import java.net.CookiePolicy import java.security.SecureRandom import java.security.cert.X509Certificate +import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.AtomicLong +import java.util.concurrent.atomic.AtomicReference import javax.net.ssl.SSLContext import javax.net.ssl.SSLSocketFactory import javax.net.ssl.TrustManager @@ -49,6 +55,7 @@ class Client @JvmOverloads constructor( * The size for chunked uploads in bytes. */ internal const val CHUNK_SIZE = 5*1024*1024; // 5MB + internal const val MAX_CONCURRENT_UPLOADS = 8 internal const val GLOBAL_PREFS = "io.appwrite" internal const val COOKIE_PREFS = "myCookie" } @@ -87,7 +94,7 @@ class Client @JvmOverloads constructor( "x-sdk-name" to "Android", "x-sdk-platform" to "client", "x-sdk-language" to "android", - "x-sdk-version" to "24.1.1", + "x-sdk-version" to "25.0.0", "x-appwrite-response-format" to "1.9.5" ) config = mutableMapOf() @@ -484,12 +491,10 @@ class Client @JvmOverloads constructor( idParamName: String? = null, onProgress: ((UploadProgress) -> Unit)? = null, ): T { - var file: RandomAccessFile? = null val input = params[paramName] as InputFile val size: Long = when(input.sourceType) { "path", "file" -> { - file = RandomAccessFile(input.path, "r") - file.length() + File(input.path).length() } "bytes" -> { (input.data as ByteArray).size.toLong() @@ -518,9 +523,9 @@ class Client @JvmOverloads constructor( ) } - val buffer = ByteArray(CHUNK_SIZE) var offset = 0L var result: Map<*, *>? = null + var uploadId: String? = null if (idParamName?.isNotEmpty() == true) { // Make a request to check if a file already exists @@ -533,59 +538,135 @@ class Client @JvmOverloads constructor( ) val chunksUploaded = current["chunksUploaded"] as Long offset = chunksUploaded * CHUNK_SIZE + uploadId = params[idParamName]?.toString() + result = current } - while (offset < size) { - when(input.sourceType) { + fun readChunk(start: Long, end: Long): ByteArray { + val length = (end - start).toInt() + return when(input.sourceType) { "file", "path" -> { - file!!.seek(offset) - file!!.read(buffer) + RandomAccessFile(input.path, "r").use { chunkFile -> + val chunk = ByteArray(length) + chunkFile.seek(start) + chunkFile.readFully(chunk) + chunk + } } "bytes" -> { - val end = if (offset + CHUNK_SIZE < size) { - offset + CHUNK_SIZE - 1 - } else { - size - 1 - } - (input.data as ByteArray).copyInto( - buffer, - startIndex = offset.toInt(), - endIndex = end.toInt() - ) + (input.data as ByteArray).copyOfRange(start.toInt(), end.toInt()) } else -> throw UnsupportedOperationException() } + } - params[paramName] = MultipartBody.Part.createFormData( + val totalChunks = (size + CHUNK_SIZE - 1) / CHUNK_SIZE + + fun isUploadComplete(chunkResult: Map<*, *>): Boolean { + val chunksUploaded = chunkResult["chunksUploaded"]?.toString()?.toLongOrNull() ?: return false + val chunksTotal = chunkResult["chunksTotal"]?.toString()?.toLongOrNull() ?: totalChunks + return chunksUploaded >= chunksTotal + } + + suspend fun uploadChunk(index: Int, start: Long, end: Long, includeUploadId: Boolean): Map<*, *> { + val chunkParams = params.toMutableMap() + val chunkHeaders = headers.toMutableMap() + + if (includeUploadId && uploadId != null) { + chunkHeaders["x-appwrite-id"] = uploadId!! + } + + chunkHeaders["Content-Range"] = "bytes $start-${end - 1}/$size" + chunkParams[paramName] = MultipartBody.Part.createFormData( paramName, input.filename, - buffer.toRequestBody() + readChunk(start, end).toRequestBody() ) - headers["Content-Range"] = - "bytes $offset-${((offset + CHUNK_SIZE) - 1).coerceAtMost(size - 1)}/$size" - - result = call( + val chunkResult = call( method = "POST", path, - headers, - params, + chunkHeaders, + chunkParams, responseType = Map::class.java ) - offset += CHUNK_SIZE - headers["x-appwrite-id"] = result["\$id"].toString() + if (index == 0) { + uploadId = chunkResult["\$id"].toString() + } + + return chunkResult + } + + if (offset == 0L) { + val firstChunkEnd = CHUNK_SIZE.toLong().coerceAtMost(size) + result = uploadChunk(0, 0, firstChunkEnd, false) + offset = firstChunkEnd onProgress?.invoke( UploadProgress( - id = result["\$id"].toString(), + id = uploadId ?: result!!["\$id"].toString(), progress = offset.coerceAtMost(size).toDouble() / size * 100, sizeUploaded = offset.coerceAtMost(size), - chunksTotal = result["chunksTotal"].toString().toInt(), - chunksUploaded = result["chunksUploaded"].toString().toInt(), + chunksTotal = result!!["chunksTotal"].toString().toInt(), + chunksUploaded = result!!["chunksUploaded"].toString().toInt(), ) ) } + val chunks = mutableListOf>() + var chunkOffset = offset + while (chunkOffset < size) { + val end = (chunkOffset + CHUNK_SIZE).coerceAtMost(size) + chunks.add(Triple((chunkOffset / CHUNK_SIZE).toInt(), chunkOffset, end)) + chunkOffset = end + } + + if (chunks.isNotEmpty()) { + val nextChunk = AtomicInteger(0) + val completedChunks = AtomicInteger((offset / CHUNK_SIZE).toInt()) + val uploadedBytes = AtomicLong(offset.coerceAtMost(size)) + val completedResultRef = AtomicReference?>(null) + val lastResultRef = AtomicReference(result) + val progressLock = Any() + + coroutineScope { + List(MAX_CONCURRENT_UPLOADS.coerceAtMost(chunks.size)) { + async { + while (true) { + val chunkIndex = nextChunk.getAndIncrement() + if (chunkIndex >= chunks.size) { + break + } + + val (index, start, end) = chunks[chunkIndex] + val chunkResult = uploadChunk(index, start, end, true) + + val chunksUploaded = completedChunks.incrementAndGet() + val sizeUploaded = uploadedBytes.addAndGet(end - start) + + lastResultRef.set(chunkResult) + if (isUploadComplete(chunkResult)) { + completedResultRef.set(chunkResult) + } + + synchronized(progressLock) { + onProgress?.invoke( + UploadProgress( + id = uploadId ?: chunkResult["\$id"].toString(), + progress = sizeUploaded.coerceAtMost(size).toDouble() / size * 100, + sizeUploaded = sizeUploaded.coerceAtMost(size), + chunksTotal = chunkResult["chunksTotal"].toString().toInt(), + chunksUploaded = chunksUploaded, + ) + ) + } + } + } + }.awaitAll() + } + result = completedResultRef.get() ?: lastResultRef.get() + } + return converter(result as Map) } diff --git a/library/src/main/java/io/appwrite/enums/Theme.kt b/library/src/main/java/io/appwrite/enums/BrowserTheme.kt similarity index 82% rename from library/src/main/java/io/appwrite/enums/Theme.kt rename to library/src/main/java/io/appwrite/enums/BrowserTheme.kt index d62acaf2..66584d24 100644 --- a/library/src/main/java/io/appwrite/enums/Theme.kt +++ b/library/src/main/java/io/appwrite/enums/BrowserTheme.kt @@ -2,7 +2,7 @@ package io.appwrite.enums import com.google.gson.annotations.SerializedName -enum class Theme(val value: String) { +enum class BrowserTheme(val value: String) { @SerializedName("light") LIGHT("light"), @SerializedName("dark") diff --git a/library/src/main/java/io/appwrite/models/Presence.kt b/library/src/main/java/io/appwrite/models/Presence.kt index d62ddc12..48cde361 100644 --- a/library/src/main/java/io/appwrite/models/Presence.kt +++ b/library/src/main/java/io/appwrite/models/Presence.kt @@ -6,7 +6,7 @@ import io.appwrite.extensions.jsonCast /** * Presence */ -data class Presence( +data class Presence( /** * Presence ID. */ @@ -56,10 +56,11 @@ data class Presence( var expiresAt: String?, /** - * Additional properties + * Presence metadata. */ @SerializedName("metadata") - val metadata: T + var metadata: Any?, + ) { fun toMap(): Map = mapOf( "\$id" to id as Any, @@ -70,37 +71,15 @@ data class Presence( "status" to status as Any, "source" to source as Any, "expiresAt" to expiresAt as Any, - "metadata" to metadata!!.jsonCast(to = Map::class.java) + "metadata" to metadata as Any, ) companion object { - operator fun invoke( - id: String, - createdAt: String, - updatedAt: String, - permissions: List, - userId: String, - status: String?, - source: String, - expiresAt: String?, - metadata: Map - ) = Presence>( - id, - createdAt, - updatedAt, - permissions, - userId, - status, - source, - expiresAt, - metadata - ) @Suppress("UNCHECKED_CAST") - fun from( + fun from( map: Map, - nestedType: Class - ) = Presence( + ) = Presence( id = map["\$id"] as String, createdAt = map["\$createdAt"] as String, updatedAt = map["\$updatedAt"] as String, @@ -109,7 +88,7 @@ data class Presence( status = map["status"] as? String, source = map["source"] as String, expiresAt = map["expiresAt"] as? String, - metadata = map["metadata"]?.jsonCast(to = nestedType) ?: emptyMap().jsonCast(to = nestedType) + metadata = map["metadata"] as? Any, ) } } \ No newline at end of file diff --git a/library/src/main/java/io/appwrite/models/PresenceList.kt b/library/src/main/java/io/appwrite/models/PresenceList.kt index 00d2ac14..b5b9a62a 100644 --- a/library/src/main/java/io/appwrite/models/PresenceList.kt +++ b/library/src/main/java/io/appwrite/models/PresenceList.kt @@ -6,7 +6,7 @@ import io.appwrite.extensions.jsonCast /** * Presences List */ -data class PresenceList( +data class PresenceList( /** * Total number of presences that matched your query. */ @@ -17,7 +17,7 @@ data class PresenceList( * List of presences. */ @SerializedName("presences") - val presences: List>, + val presences: List, ) { fun toMap(): Map = mapOf( @@ -26,21 +26,13 @@ data class PresenceList( ) companion object { - operator fun invoke( - total: Long, - presences: List>>, - ) = PresenceList>( - total, - presences, - ) @Suppress("UNCHECKED_CAST") - fun from( + fun from( map: Map, - nestedType: Class - ) = PresenceList( + ) = PresenceList( total = (map["total"] as Number).toLong(), - presences = (map["presences"] as List>).map { Presence.from(map = it, nestedType) }, + presences = (map["presences"] as List>).map { Presence.from(map = it) }, ) } } \ No newline at end of file diff --git a/library/src/main/java/io/appwrite/services/Avatars.kt b/library/src/main/java/io/appwrite/services/Avatars.kt index 004440f3..298713c4 100644 --- a/library/src/main/java/io/appwrite/services/Avatars.kt +++ b/library/src/main/java/io/appwrite/services/Avatars.kt @@ -297,7 +297,7 @@ class Avatars(client: Client) : Service(client) { viewportWidth: Long? = null, viewportHeight: Long? = null, scale: Double? = null, - theme: io.appwrite.enums.Theme? = null, + theme: io.appwrite.enums.BrowserTheme? = null, userAgent: String? = null, fullpage: Boolean? = null, locale: String? = null, diff --git a/library/src/main/java/io/appwrite/services/Presences.kt b/library/src/main/java/io/appwrite/services/Presences.kt index 883fec4f..21d234a6 100644 --- a/library/src/main/java/io/appwrite/services/Presences.kt +++ b/library/src/main/java/io/appwrite/services/Presences.kt @@ -21,15 +21,14 @@ class Presences(client: Client) : Service(client) { * @param queries Array of query strings generated using the Query class provided by the SDK. * @param total When set to false, the total count returned will be 0 and will not be calculated. * @param ttl TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, collection, schema version (attributes and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; document writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Must be between 0 and 86400 (24 hours). - * @return [io.appwrite.models.PresenceList] + * @return [io.appwrite.models.PresenceList] */ @JvmOverloads - suspend fun list( + suspend fun list( queries: List? = null, total: Boolean? = null, ttl: Long? = null, - nestedType: Class, - ): io.appwrite.models.PresenceList { + ): io.appwrite.models.PresenceList { val apiPath = "/presences" val apiParams = mutableMapOf( @@ -39,53 +38,31 @@ class Presences(client: Client) : Service(client) { ) val apiHeaders = mutableMapOf( ) - val converter: (Any) -> io.appwrite.models.PresenceList = { + val converter: (Any) -> io.appwrite.models.PresenceList = { @Suppress("UNCHECKED_CAST") - io.appwrite.models.PresenceList.from(map = it as Map, nestedType) + io.appwrite.models.PresenceList.from(map = it as Map) } return client.call( "GET", apiPath, apiHeaders, apiParams, - responseType = classOf(), + responseType = io.appwrite.models.PresenceList::class.java, converter, ) } - /** - * List presence logs. Expired entries are filtered out automatically. - * - * - * @param queries Array of query strings generated using the Query class provided by the SDK. - * @param total When set to false, the total count returned will be 0 and will not be calculated. - * @param ttl TTL (seconds) for caching list responses. Responses are stored in an in-memory key-value cache, keyed per project, collection, schema version (attributes and indexes), caller authorization roles, and the exact query — so users with different permissions never share cached entries. Schema changes invalidate cached entries automatically; document writes do not, so choose a TTL you are comfortable serving as stale data. Set to 0 to disable caching. Must be between 0 and 86400 (24 hours). - * @return [io.appwrite.models.PresenceList] - */ - @JvmOverloads - @Throws(AppwriteException::class) - suspend fun list( - queries: List? = null, - total: Boolean? = null, - ttl: Long? = null, - ): io.appwrite.models.PresenceList> = list( - queries, - total, - ttl, - nestedType = classOf(), - ) /** * Get a presence log by its unique ID. Entries whose `expiresAt` is in the past are treated as not found. * * * @param presenceId Presence unique ID. - * @return [io.appwrite.models.Presence] + * @return [io.appwrite.models.Presence] */ - suspend fun get( + suspend fun get( presenceId: String, - nestedType: Class, - ): io.appwrite.models.Presence { + ): io.appwrite.models.Presence { val apiPath = "/presences/{presenceId}" .replace("{presenceId}", presenceId) @@ -93,34 +70,20 @@ class Presences(client: Client) : Service(client) { ) val apiHeaders = mutableMapOf( ) - val converter: (Any) -> io.appwrite.models.Presence = { + val converter: (Any) -> io.appwrite.models.Presence = { @Suppress("UNCHECKED_CAST") - io.appwrite.models.Presence.from(map = it as Map, nestedType) + io.appwrite.models.Presence.from(map = it as Map) } return client.call( "GET", apiPath, apiHeaders, apiParams, - responseType = classOf(), + responseType = io.appwrite.models.Presence::class.java, converter, ) } - /** - * Get a presence log by its unique ID. Entries whose `expiresAt` is in the past are treated as not found. - * - * - * @param presenceId Presence unique ID. - * @return [io.appwrite.models.Presence] - */ - @Throws(AppwriteException::class) - suspend fun get( - presenceId: String, - ): io.appwrite.models.Presence> = get( - presenceId, - nestedType = classOf(), - ) /** * Create or update a presence log by its user ID. @@ -131,17 +94,16 @@ class Presences(client: Client) : Service(client) { * @param permissions An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). * @param expiresAt Presence expiry datetime. * @param metadata Presence metadata object. - * @return [io.appwrite.models.Presence] + * @return [io.appwrite.models.Presence] */ @JvmOverloads - suspend fun upsert( + suspend fun upsert( presenceId: String, status: String, permissions: List? = null, expiresAt: String? = null, metadata: Any? = null, - nestedType: Class, - ): io.appwrite.models.Presence { + ): io.appwrite.models.Presence { val apiPath = "/presences/{presenceId}" .replace("{presenceId}", presenceId) @@ -154,47 +116,20 @@ class Presences(client: Client) : Service(client) { val apiHeaders = mutableMapOf( "content-type" to "application/json", ) - val converter: (Any) -> io.appwrite.models.Presence = { + val converter: (Any) -> io.appwrite.models.Presence = { @Suppress("UNCHECKED_CAST") - io.appwrite.models.Presence.from(map = it as Map, nestedType) + io.appwrite.models.Presence.from(map = it as Map) } return client.call( "PUT", apiPath, apiHeaders, apiParams, - responseType = classOf(), + responseType = io.appwrite.models.Presence::class.java, converter, ) } - /** - * Create or update a presence log by its user ID. - * - * - * @param presenceId Presence unique ID. - * @param status Presence status. - * @param permissions An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). - * @param expiresAt Presence expiry datetime. - * @param metadata Presence metadata object. - * @return [io.appwrite.models.Presence] - */ - @JvmOverloads - @Throws(AppwriteException::class) - suspend fun upsert( - presenceId: String, - status: String, - permissions: List? = null, - expiresAt: String? = null, - metadata: Any? = null, - ): io.appwrite.models.Presence> = upsert( - presenceId, - status, - permissions, - expiresAt, - metadata, - nestedType = classOf(), - ) /** * Update a presence log by its unique ID. Using the patch method you can pass only specific fields that will get updated. @@ -206,18 +141,17 @@ class Presences(client: Client) : Service(client) { * @param metadata Presence metadata object. * @param permissions An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). * @param purge When true, purge cached responses used by list presences endpoint. - * @return [io.appwrite.models.Presence] + * @return [io.appwrite.models.Presence] */ @JvmOverloads - suspend fun update( + suspend fun update( presenceId: String, status: String? = null, expiresAt: String? = null, metadata: Any? = null, permissions: List? = null, purge: Boolean? = null, - nestedType: Class, - ): io.appwrite.models.Presence { + ): io.appwrite.models.Presence { val apiPath = "/presences/{presenceId}" .replace("{presenceId}", presenceId) @@ -231,50 +165,20 @@ class Presences(client: Client) : Service(client) { val apiHeaders = mutableMapOf( "content-type" to "application/json", ) - val converter: (Any) -> io.appwrite.models.Presence = { + val converter: (Any) -> io.appwrite.models.Presence = { @Suppress("UNCHECKED_CAST") - io.appwrite.models.Presence.from(map = it as Map, nestedType) + io.appwrite.models.Presence.from(map = it as Map) } return client.call( "PATCH", apiPath, apiHeaders, apiParams, - responseType = classOf(), + responseType = io.appwrite.models.Presence::class.java, converter, ) } - /** - * Update a presence log by its unique ID. Using the patch method you can pass only specific fields that will get updated. - * - * - * @param presenceId Presence unique ID. - * @param status Presence status. - * @param expiresAt Presence expiry datetime. - * @param metadata Presence metadata object. - * @param permissions An array of permissions strings. By default, only the current user is granted all permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). - * @param purge When true, purge cached responses used by list presences endpoint. - * @return [io.appwrite.models.Presence] - */ - @JvmOverloads - @Throws(AppwriteException::class) - suspend fun update( - presenceId: String, - status: String? = null, - expiresAt: String? = null, - metadata: Any? = null, - permissions: List? = null, - purge: Boolean? = null, - ): io.appwrite.models.Presence> = update( - presenceId, - status, - expiresAt, - metadata, - permissions, - purge, - nestedType = classOf(), - ) /** * Delete a presence log by its unique ID. From f03f00fc0422d26fde27c56f8892661b5749b571 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 1 Jun 2026 10:23:03 +0000 Subject: [PATCH 2/2] chore: update Android SDK to 25.0.0 --- library/src/main/java/io/appwrite/models/Document.kt | 2 +- library/src/main/java/io/appwrite/models/Row.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/io/appwrite/models/Document.kt b/library/src/main/java/io/appwrite/models/Document.kt index a192a1dd..c55e6d26 100644 --- a/library/src/main/java/io/appwrite/models/Document.kt +++ b/library/src/main/java/io/appwrite/models/Document.kt @@ -99,7 +99,7 @@ data class Document( createdAt = map["\$createdAt"] as String, updatedAt = map["\$updatedAt"] as String, permissions = map["\$permissions"] as List, - data = map["data"]?.jsonCast(to = nestedType) ?: emptyMap().jsonCast(to = nestedType) + data = map["data"]?.jsonCast(to = nestedType) ?: map.jsonCast(to = nestedType) ) } } \ No newline at end of file diff --git a/library/src/main/java/io/appwrite/models/Row.kt b/library/src/main/java/io/appwrite/models/Row.kt index c01672e7..488ec7bc 100644 --- a/library/src/main/java/io/appwrite/models/Row.kt +++ b/library/src/main/java/io/appwrite/models/Row.kt @@ -99,7 +99,7 @@ data class Row( createdAt = map["\$createdAt"] as String, updatedAt = map["\$updatedAt"] as String, permissions = map["\$permissions"] as List, - data = map["data"]?.jsonCast(to = nestedType) ?: emptyMap().jsonCast(to = nestedType) + data = map["data"]?.jsonCast(to = nestedType) ?: map.jsonCast(to = nestedType) ) } } \ No newline at end of file