diff --git a/.github/workflows/autoclose.yml b/.github/workflows/autoclose.yml new file mode 100644 index 0000000..3e2b3cb --- /dev/null +++ b/.github/workflows/autoclose.yml @@ -0,0 +1,11 @@ +name: Auto-close External Pull Requests + +on: + pull_request_target: + types: [opened, reopened] + +jobs: + auto_close: + uses: appwrite/.github/.github/workflows/autoclose.yml@main + secrets: + GH_AUTO_CLOSE_PR_TOKEN: ${{ secrets.GH_AUTO_CLOSE_PR_TOKEN }} diff --git a/README.md b/README.md index f8dab1a..338b53e 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:6.0.0") +implementation("io.appwrite:sdk-for-android:6.1.0") ``` ### Maven @@ -49,7 +49,7 @@ Add this to your project's `pom.xml` file: io.appwrite sdk-for-android - 6.0.0 + 6.1.0 ``` diff --git a/example/src/main/java/io/appwrite/android/ui/accounts/AccountsViewModel.kt b/example/src/main/java/io/appwrite/android/ui/accounts/AccountsViewModel.kt index f53113d..4302327 100644 --- a/example/src/main/java/io/appwrite/android/ui/accounts/AccountsViewModel.kt +++ b/example/src/main/java/io/appwrite/android/ui/accounts/AccountsViewModel.kt @@ -81,8 +81,8 @@ class AccountsViewModel : ViewModel() { account.createOAuth2Session( activity, OAuthProvider.FACEBOOK, - "appwrite-callback-6070749e6acd4://demo.appwrite.io/auth/oauth2/success", - "appwrite-callback-6070749e6acd4://demo.appwrite.io/auth/oauth2/failure" + "appwrite-callback-6070749e6acd4://cloud.appwrite.io/auth/oauth2/success", + "appwrite-callback-6070749e6acd4://cloud.appwrite.io/auth/oauth2/failure" ) } catch (e: Exception) { _error.postValue(Event(e)) diff --git a/library/src/main/java/io/appwrite/Client.kt b/library/src/main/java/io/appwrite/Client.kt index 93a3e06..e2a48bb 100644 --- a/library/src/main/java/io/appwrite/Client.kt +++ b/library/src/main/java/io/appwrite/Client.kt @@ -86,7 +86,7 @@ class Client @JvmOverloads constructor( "x-sdk-name" to "Android", "x-sdk-platform" to "client", "x-sdk-language" to "android", - "x-sdk-version" to "6.0.0", + "x-sdk-version" to "6.1.0", "x-appwrite-response-format" to "1.6.0" ) config = mutableMapOf() diff --git a/library/src/main/java/io/appwrite/enums/ImageFormat.kt b/library/src/main/java/io/appwrite/enums/ImageFormat.kt index c6a3b0f..25eea90 100644 --- a/library/src/main/java/io/appwrite/enums/ImageFormat.kt +++ b/library/src/main/java/io/appwrite/enums/ImageFormat.kt @@ -12,7 +12,9 @@ enum class ImageFormat(val value: String) { @SerializedName("png") PNG("png"), @SerializedName("webp") - WEBP("webp"); + WEBP("webp"), + @SerializedName("avif") + AVIF("avif"); override fun toString() = value } \ No newline at end of file diff --git a/library/src/main/java/io/appwrite/models/Document.kt b/library/src/main/java/io/appwrite/models/Document.kt index 5a7d371..01204de 100644 --- a/library/src/main/java/io/appwrite/models/Document.kt +++ b/library/src/main/java/io/appwrite/models/Document.kt @@ -41,7 +41,7 @@ data class Document( * Document permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). */ @SerializedName("\$permissions") - val permissions: List, + val permissions: List, /** * Additional properties @@ -66,7 +66,7 @@ data class Document( databaseId: String, createdAt: String, updatedAt: String, - permissions: List, + permissions: List, data: Map ) = Document>( id, @@ -88,7 +88,7 @@ data class Document( databaseId = map["\$databaseId"] as String, createdAt = map["\$createdAt"] as String, updatedAt = map["\$updatedAt"] as String, - permissions = map["\$permissions"] as List, + permissions = map["\$permissions"] as List, data = map.jsonCast(to = nestedType) ) } diff --git a/library/src/main/java/io/appwrite/models/Execution.kt b/library/src/main/java/io/appwrite/models/Execution.kt index 44724fa..1b6226a 100644 --- a/library/src/main/java/io/appwrite/models/Execution.kt +++ b/library/src/main/java/io/appwrite/models/Execution.kt @@ -29,7 +29,7 @@ data class Execution( * Execution roles. */ @SerializedName("\$permissions") - val permissions: List, + val permissions: List, /** * Function ID. @@ -139,7 +139,7 @@ data class Execution( id = map["\$id"] as String, createdAt = map["\$createdAt"] as String, updatedAt = map["\$updatedAt"] as String, - permissions = map["\$permissions"] as List, + permissions = map["\$permissions"] as List, functionId = map["functionId"] as String, trigger = map["trigger"] as String, status = map["status"] as String, diff --git a/library/src/main/java/io/appwrite/models/File.kt b/library/src/main/java/io/appwrite/models/File.kt index 5c2b731..897fc4a 100644 --- a/library/src/main/java/io/appwrite/models/File.kt +++ b/library/src/main/java/io/appwrite/models/File.kt @@ -35,7 +35,7 @@ data class File( * File permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). */ @SerializedName("\$permissions") - val permissions: List, + val permissions: List, /** * File name. @@ -98,7 +98,7 @@ data class File( bucketId = map["bucketId"] as String, createdAt = map["\$createdAt"] as String, updatedAt = map["\$updatedAt"] as String, - permissions = map["\$permissions"] as List, + permissions = map["\$permissions"] as List, name = map["name"] as String, signature = map["signature"] as String, mimeType = map["mimeType"] as String, diff --git a/library/src/main/java/io/appwrite/models/Membership.kt b/library/src/main/java/io/appwrite/models/Membership.kt index 17ab303..4a899ee 100644 --- a/library/src/main/java/io/appwrite/models/Membership.kt +++ b/library/src/main/java/io/appwrite/models/Membership.kt @@ -32,13 +32,13 @@ data class Membership( val userId: String, /** - * User name. + * User name. Hide this attribute by toggling membership privacy in the Console. */ @SerializedName("userName") val userName: String, /** - * User email address. + * User email address. Hide this attribute by toggling membership privacy in the Console. */ @SerializedName("userEmail") val userEmail: String, @@ -74,7 +74,7 @@ data class Membership( val confirm: Boolean, /** - * Multi factor authentication status, true if the user has MFA enabled or false otherwise. + * Multi factor authentication status, true if the user has MFA enabled or false otherwise. Hide this attribute by toggling membership privacy in the Console. */ @SerializedName("mfa") val mfa: Boolean, @@ -83,7 +83,7 @@ data class Membership( * User list of roles */ @SerializedName("roles") - val roles: List, + val roles: List, ) { fun toMap(): Map = mapOf( @@ -120,7 +120,7 @@ data class Membership( joined = map["joined"] as String, confirm = map["confirm"] as Boolean, mfa = map["mfa"] as Boolean, - roles = map["roles"] as List, + roles = map["roles"] as List, ) } } \ No newline at end of file diff --git a/library/src/main/java/io/appwrite/models/MfaRecoveryCodes.kt b/library/src/main/java/io/appwrite/models/MfaRecoveryCodes.kt index ad8bc45..3fab41a 100644 --- a/library/src/main/java/io/appwrite/models/MfaRecoveryCodes.kt +++ b/library/src/main/java/io/appwrite/models/MfaRecoveryCodes.kt @@ -11,7 +11,7 @@ data class MfaRecoveryCodes( * Recovery codes. */ @SerializedName("recoveryCodes") - val recoveryCodes: List, + val recoveryCodes: List, ) { fun toMap(): Map = mapOf( @@ -24,7 +24,7 @@ data class MfaRecoveryCodes( fun from( map: Map, ) = MfaRecoveryCodes( - recoveryCodes = map["recoveryCodes"] as List, + recoveryCodes = map["recoveryCodes"] as List, ) } } \ No newline at end of file diff --git a/library/src/main/java/io/appwrite/models/Session.kt b/library/src/main/java/io/appwrite/models/Session.kt index 1c6a7b2..02dbb28 100644 --- a/library/src/main/java/io/appwrite/models/Session.kt +++ b/library/src/main/java/io/appwrite/models/Session.kt @@ -167,7 +167,7 @@ data class Session( * Returns a list of active session factors. */ @SerializedName("factors") - val factors: List, + val factors: List, /** * Secret used to authenticate the user. Only included if the request was made with an API key @@ -246,7 +246,7 @@ data class Session( countryCode = map["countryCode"] as String, countryName = map["countryName"] as String, current = map["current"] as Boolean, - factors = map["factors"] as List, + factors = map["factors"] as List, secret = map["secret"] as String, mfaUpdatedAt = map["mfaUpdatedAt"] as String, ) diff --git a/library/src/main/java/io/appwrite/models/Target.kt b/library/src/main/java/io/appwrite/models/Target.kt index bd4d207..54b4694 100644 --- a/library/src/main/java/io/appwrite/models/Target.kt +++ b/library/src/main/java/io/appwrite/models/Target.kt @@ -55,6 +55,12 @@ data class Target( @SerializedName("identifier") val identifier: String, + /** + * Is the target expired. + */ + @SerializedName("expired") + val expired: Boolean, + ) { fun toMap(): Map = mapOf( "\$id" to id as Any, @@ -65,6 +71,7 @@ data class Target( "providerId" to providerId as Any, "providerType" to providerType as Any, "identifier" to identifier as Any, + "expired" to expired as Any, ) companion object { @@ -81,6 +88,7 @@ data class Target( providerId = map["providerId"] as? String?, providerType = map["providerType"] as String, identifier = map["identifier"] as String, + expired = map["expired"] as Boolean, ) } } \ No newline at end of file diff --git a/library/src/main/java/io/appwrite/models/User.kt b/library/src/main/java/io/appwrite/models/User.kt index 095467a..fd03fc2 100644 --- a/library/src/main/java/io/appwrite/models/User.kt +++ b/library/src/main/java/io/appwrite/models/User.kt @@ -65,7 +65,7 @@ data class User( * Labels for the user. */ @SerializedName("labels") - val labels: List, + val labels: List, /** * Password update time in ISO 8601 format. @@ -155,7 +155,7 @@ data class User( hashOptions: Any?, registration: String, status: Boolean, - labels: List, + labels: List, passwordUpdate: String, email: String, phone: String, @@ -201,7 +201,7 @@ data class User( hashOptions = map["hashOptions"] as? Any?, registration = map["registration"] as String, status = map["status"] as Boolean, - labels = map["labels"] as List, + labels = map["labels"] as List, passwordUpdate = map["passwordUpdate"] as String, email = map["email"] as String, phone = map["phone"] as String, diff --git a/library/src/main/java/io/appwrite/services/Account.kt b/library/src/main/java/io/appwrite/services/Account.kt index c3655ea..6f4cbd8 100644 --- a/library/src/main/java/io/appwrite/services/Account.kt +++ b/library/src/main/java/io/appwrite/services/Account.kt @@ -190,7 +190,7 @@ class Account(client: Client) : Service(client) { ) /** - * List Identities + * List identities * * Get the list of identities for the currently logged in user. * @@ -370,7 +370,7 @@ class Account(client: Client) : Service(client) { ) /** - * Create Authenticator + * Create authenticator * * Add an authenticator app to be used as an MFA factor. Verify the authenticator using the [verify authenticator](/docs/references/cloud/client-web/account#updateMfaAuthenticator) method. * @@ -404,7 +404,7 @@ class Account(client: Client) : Service(client) { /** - * Verify Authenticator + * Verify authenticator * * Verify an authenticator app after adding it using the [add authenticator](/docs/references/cloud/client-web/account#createMfaAuthenticator) method. * @@ -441,7 +441,7 @@ class Account(client: Client) : Service(client) { } /** - * Verify Authenticator + * Verify authenticator * * Verify an authenticator app after adding it using the [add authenticator](/docs/references/cloud/client-web/account#createMfaAuthenticator) method. * @@ -460,7 +460,7 @@ class Account(client: Client) : Service(client) { ) /** - * Delete Authenticator + * Delete authenticator * * Delete an authenticator for a user by ID. * @@ -489,7 +489,7 @@ class Account(client: Client) : Service(client) { /** - * Create MFA Challenge + * Create MFA challenge * * Begin the process of MFA verification after sign-in. Finish the flow with [updateMfaChallenge](/docs/references/cloud/client-web/account#updateMfaChallenge) method. * @@ -523,7 +523,7 @@ class Account(client: Client) : Service(client) { /** - * Create MFA Challenge (confirmation) + * Create MFA challenge (confirmation) * * Complete the MFA challenge by providing the one-time password. Finish the process of MFA verification by providing the one-time password. To begin the flow, use [createMfaChallenge](/docs/references/cloud/client-web/account#createMfaChallenge) method. * @@ -555,7 +555,7 @@ class Account(client: Client) : Service(client) { /** - * List Factors + * List factors * * List the factors available on the account to be used as a MFA challange. * @@ -586,7 +586,7 @@ class Account(client: Client) : Service(client) { /** - * Get MFA Recovery Codes + * Get MFA recovery codes * * Get recovery codes that can be used as backup for MFA flow. Before getting codes, they must be generated using [createMfaRecoveryCodes](/docs/references/cloud/client-web/account#createMfaRecoveryCodes) method. An OTP challenge is required to read recovery codes. * @@ -617,7 +617,7 @@ class Account(client: Client) : Service(client) { /** - * Create MFA Recovery Codes + * Create MFA recovery codes * * Generate recovery codes as backup for MFA flow. It's recommended to generate and show then immediately after user successfully adds their authehticator. Recovery codes can be used as a MFA verification type in [createMfaChallenge](/docs/references/cloud/client-web/account#createMfaChallenge) method. * @@ -648,7 +648,7 @@ class Account(client: Client) : Service(client) { /** - * Regenerate MFA Recovery Codes + * Regenerate MFA recovery codes * * Regenerate recovery codes that can be used as backup for MFA flow. Before regenerating codes, they must be first generated using [createMfaRecoveryCodes](/docs/references/cloud/client-web/account#createMfaRecoveryCodes) method. An OTP challenge is required to regenreate recovery codes. * @@ -1209,7 +1209,9 @@ class Account(client: Client) : Service(client) { return@forEach } is List<*> -> { - apiQuery.add("${it.key}[]=${it.value.toString()}") + (it.value as List<*>).forEach { v -> + apiQuery.add("${it.key}[]=${v.toString()}") + } } else -> { apiQuery.add("${it.key}=${it.value.toString()}") @@ -1688,7 +1690,9 @@ class Account(client: Client) : Service(client) { return@forEach } is List<*> -> { - apiQuery.add("${it.key}[]=${it.value.toString()}") + (it.value as List<*>).forEach { v -> + apiQuery.add("${it.key}[]=${v.toString()}") + } } else -> { apiQuery.add("${it.key}=${it.value.toString()}") diff --git a/library/src/main/java/io/appwrite/services/Locale.kt b/library/src/main/java/io/appwrite/services/Locale.kt index ad3a6f6..01b3017 100644 --- a/library/src/main/java/io/appwrite/services/Locale.kt +++ b/library/src/main/java/io/appwrite/services/Locale.kt @@ -47,7 +47,7 @@ class Locale(client: Client) : Service(client) { /** - * List Locale Codes + * List locale codes * * List of all locale codes in [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). * diff --git a/library/src/main/java/io/appwrite/services/Realtime.kt b/library/src/main/java/io/appwrite/services/Realtime.kt index 1bffd3a..605be89 100644 --- a/library/src/main/java/io/appwrite/services/Realtime.kt +++ b/library/src/main/java/io/appwrite/services/Realtime.kt @@ -29,6 +29,8 @@ class Realtime(client: Client) : Service(client), CoroutineScope { private companion object { private const val TYPE_ERROR = "error" private const val TYPE_EVENT = "event" + private const val TYPE_PONG = "pong" + private const val HEARTBEAT_INTERVAL = 20_000L // 20 seconds private const val DEBOUNCE_MILLIS = 1L @@ -40,6 +42,7 @@ class Realtime(client: Client) : Service(client), CoroutineScope { private var reconnectAttempts = 0 private var subscriptionsCounter = 0 private var reconnect = true + private var heartbeatJob: Job? = null } private fun createSocket() { @@ -80,9 +83,25 @@ class Realtime(client: Client) : Service(client), CoroutineScope { } private fun closeSocket() { + stopHeartbeat() socket?.close(RealtimeCode.POLICY_VIOLATION.value, null) } + private fun startHeartbeat() { + stopHeartbeat() + heartbeatJob = launch { + while (isActive) { + delay(HEARTBEAT_INTERVAL) + socket?.send("""{"type":"ping"}""") + } + } + } + + private fun stopHeartbeat() { + heartbeatJob?.cancel() + heartbeatJob = null + } + private fun getTimeout() = when { reconnectAttempts < 5 -> 1000L reconnectAttempts < 15 -> 5000L @@ -145,6 +164,7 @@ class Realtime(client: Client) : Service(client), CoroutineScope { override fun onOpen(webSocket: WebSocket, response: Response) { super.onOpen(webSocket, response) reconnectAttempts = 0 + startHeartbeat() } override fun onMessage(webSocket: WebSocket, text: String) { @@ -181,6 +201,7 @@ class Realtime(client: Client) : Service(client), CoroutineScope { override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { super.onClosing(webSocket, code, reason) + stopHeartbeat() if (!reconnect || code == RealtimeCode.POLICY_VIOLATION.value) { reconnect = true return @@ -203,6 +224,7 @@ class Realtime(client: Client) : Service(client), CoroutineScope { override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { super.onFailure(webSocket, t, response) + stopHeartbeat() t.printStackTrace() } } diff --git a/library/src/main/java/io/appwrite/services/Storage.kt b/library/src/main/java/io/appwrite/services/Storage.kt index 8f21473..1f990c2 100644 --- a/library/src/main/java/io/appwrite/services/Storage.kt +++ b/library/src/main/java/io/appwrite/services/Storage.kt @@ -189,7 +189,7 @@ class Storage(client: Client) : Service(client) { /** - * Delete File + * Delete file * * Delete a file by its unique ID. Only users with write permissions have access to delete this resource. * diff --git a/library/src/main/java/io/appwrite/services/Teams.kt b/library/src/main/java/io/appwrite/services/Teams.kt index fd0ae92..b586f11 100644 --- a/library/src/main/java/io/appwrite/services/Teams.kt +++ b/library/src/main/java/io/appwrite/services/Teams.kt @@ -275,7 +275,7 @@ class Teams(client: Client) : Service(client) { /** * List team memberships * - * Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint. + * Use this endpoint to list a team's members using the team's ID. All team members have read access to this endpoint. Hide sensitive attributes from the response by toggling membership privacy in the Console. * * @param teamId Team ID. * @param queries Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm @@ -369,7 +369,7 @@ class Teams(client: Client) : Service(client) { /** * Get team membership * - * Get a team member by the membership unique id. All team members have read access for this resource. + * Get a team member by the membership unique id. All team members have read access for this resource. Hide sensitive attributes from the response by toggling membership privacy in the Console. * * @param teamId Team ID. * @param membershipId Membership ID.