From c9383268c0081cb39aa9966230853895ff8d1ceb Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 30 Jun 2025 09:43:09 +0200 Subject: [PATCH 1/7] useV2 Signed-off-by: alperozturk --- .../shares/CreateShareRemoteOperationIT.kt | 13 +++++++-- .../shares/attributes/ShareAttributes.kt | 28 +++++++++++++++---- .../attributes/ShareAttributesDeserializer.kt | 2 +- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt index a239adf67d..81e5d80e9a 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt @@ -21,18 +21,25 @@ import org.junit.Before import org.junit.Test class CreateShareRemoteOperationIT : AbstractIT() { + private var ownCloudVersion: OwnCloudVersion? = null + @Before fun before() { val result = GetStatusRemoteOperation(context).execute(client) assertTrue(result.isSuccess) val data = result.data as ArrayList - val ownCloudVersion = data[0] as OwnCloudVersion - Assume.assumeTrue(ownCloudVersion.isNewerOrEqual(NextcloudVersion.nextcloud_24)) + ownCloudVersion = data[0] as OwnCloudVersion + Assume.assumeTrue(ownCloudVersion?.isNewerOrEqual(NextcloudVersion.nextcloud_24) == true) } @Test fun createShareWithNoteAndAttributes() { - val attributes = listOf(ShareAttributes.createDownloadAttributes(true)) + val attributes = listOf( + ShareAttributes.createDownloadAttributes( + true, + ownCloudVersion?.isNewerOrEqual(NextcloudVersion.nextcloud_30) == true + ) + ) val note = "Note with attributes" val path = "/shareWithAttributes/" diff --git a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt b/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt index 56c685e9fc..748a17cfec 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt @@ -8,17 +8,33 @@ package com.owncloud.android.lib.resources.shares.attributes -data class ShareAttributes( - val scope: String, - val key: String, - var value: Boolean +open class ShareAttributes( + open val scope: String, + open val key: String, + open val abstractValue: Boolean ) { companion object { const val DOWNLOAD_ATTRIBUTE_KEY = "download" - fun createDownloadAttributes(value: Boolean): ShareAttributes = - ShareAttributes(scope = "permissions", key = DOWNLOAD_ATTRIBUTE_KEY, value = value) + fun createDownloadAttributes(value: Boolean, useV2: Boolean): ShareAttributes = + if (useV2) { + ShareAttributesV2(scope = "permissions", key = DOWNLOAD_ATTRIBUTE_KEY, enabled = value) + } else { + ShareAttributesV1(scope = "permissions", key = DOWNLOAD_ATTRIBUTE_KEY, value = value) + } } + + private data class ShareAttributesV1( + override val scope: String, + override val key: String, + val value: Boolean + ) : ShareAttributes(scope, key, value) + + private data class ShareAttributesV2( + override val scope: String, + override val key: String, + val enabled: Boolean + ) : ShareAttributes(scope, key, enabled) } fun List?.getDownloadAttribute(): ShareAttributes? = diff --git a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesDeserializer.kt b/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesDeserializer.kt index 3457b1240a..27682ea31a 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesDeserializer.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesDeserializer.kt @@ -30,7 +30,7 @@ class ShareAttributesDeserializer : JsonDeserializer { val jsonObject = json?.asJsonObject val scope = jsonObject?.get("scope")?.asString ?: "" val key = jsonObject?.get("key")?.asString ?: "" - val value = (jsonObject.getBoolean("value") ?: jsonObject.getBoolean("enabled")) == true + val value = jsonObject.getBoolean("abstractValue") == true return ShareAttributes(scope, key, value) } } From d3e30e71e4b5e269d437a5fb8e48aa3c0a59bd3b Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 30 Jun 2025 10:07:02 +0200 Subject: [PATCH 2/7] useV2 Signed-off-by: alperozturk --- .../android/lib/resources/shares/attributes/ShareAttributes.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt b/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt index 748a17cfec..f469f4d4c4 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt @@ -11,7 +11,7 @@ package com.owncloud.android.lib.resources.shares.attributes open class ShareAttributes( open val scope: String, open val key: String, - open val abstractValue: Boolean + open var abstractValue: Boolean ) { companion object { const val DOWNLOAD_ATTRIBUTE_KEY = "download" From 74a89aedd00d3305d0b84ff7292029f95740a34e Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 30 Jun 2025 11:08:13 +0200 Subject: [PATCH 3/7] useV2 Signed-off-by: alperozturk --- .../shares/CreateShareRemoteOperationIT.kt | 19 --------- .../shares/attributes/ShareAttributes.kt | 41 ------------------- .../attributes/ShareAttributesDeserializer.kt | 36 ---------------- .../attributes/ShareAttributesJsonHandler.kt | 36 ---------------- 4 files changed, 132 deletions(-) delete mode 100644 library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt delete mode 100644 library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesDeserializer.kt delete mode 100644 library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesJsonHandler.kt diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt index 81e5d80e9a..2f8d14bace 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt @@ -9,8 +9,6 @@ package com.owncloud.android.lib.resources.shares import com.owncloud.android.AbstractIT import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation -import com.owncloud.android.lib.resources.shares.attributes.ShareAttributes -import com.owncloud.android.lib.resources.shares.attributes.ShareAttributesJsonHandler import com.owncloud.android.lib.resources.status.GetStatusRemoteOperation import com.owncloud.android.lib.resources.status.NextcloudVersion import com.owncloud.android.lib.resources.status.OwnCloudVersion @@ -32,23 +30,6 @@ class CreateShareRemoteOperationIT : AbstractIT() { Assume.assumeTrue(ownCloudVersion?.isNewerOrEqual(NextcloudVersion.nextcloud_24) == true) } - @Test - fun createShareWithNoteAndAttributes() { - val attributes = listOf( - ShareAttributes.createDownloadAttributes( - true, - ownCloudVersion?.isNewerOrEqual(NextcloudVersion.nextcloud_30) == true - ) - ) - val note = "Note with attributes" - val path = "/shareWithAttributes/" - - createFolder(path) - val share = createShare(path, "admin", note, ShareAttributesJsonHandler.toJson(attributes)) - assertEquals(note, share.note) - assertEquals(attributes, ShareAttributesJsonHandler.toList(share.attributes)) - } - @Test fun createShareWithNote() { val note = "This is the note" diff --git a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt b/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt deleted file mode 100644 index f469f4d4c4..0000000000 --- a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributes.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Nextcloud Android Library - * - * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2025 Alper Ozturk - * SPDX-License-Identifier: MIT - */ - -package com.owncloud.android.lib.resources.shares.attributes - -open class ShareAttributes( - open val scope: String, - open val key: String, - open var abstractValue: Boolean -) { - companion object { - const val DOWNLOAD_ATTRIBUTE_KEY = "download" - - fun createDownloadAttributes(value: Boolean, useV2: Boolean): ShareAttributes = - if (useV2) { - ShareAttributesV2(scope = "permissions", key = DOWNLOAD_ATTRIBUTE_KEY, enabled = value) - } else { - ShareAttributesV1(scope = "permissions", key = DOWNLOAD_ATTRIBUTE_KEY, value = value) - } - } - - private data class ShareAttributesV1( - override val scope: String, - override val key: String, - val value: Boolean - ) : ShareAttributes(scope, key, value) - - private data class ShareAttributesV2( - override val scope: String, - override val key: String, - val enabled: Boolean - ) : ShareAttributes(scope, key, enabled) -} - -fun List?.getDownloadAttribute(): ShareAttributes? = - this?.find { it.key == ShareAttributes.DOWNLOAD_ATTRIBUTE_KEY } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesDeserializer.kt b/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesDeserializer.kt deleted file mode 100644 index 27682ea31a..0000000000 --- a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesDeserializer.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Nextcloud Android Library - * - * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2025 Alper Ozturk - * SPDX-License-Identifier: MIT - */ - -package com.owncloud.android.lib.resources.shares.attributes - -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement -import com.nextcloud.extensions.getBoolean -import java.lang.reflect.Type - -/** - * Custom serializer for the ShareAttributes class. - * This handles the deserialization and serialization of the ShareAttributes data class. - * Since Nextcloud 30, the enabled key have been renamed to value and supports more than boolean. - * - * https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-share-api.html#share-attributes - */ -class ShareAttributesDeserializer : JsonDeserializer { - override fun deserialize( - json: JsonElement?, - typeOfT: Type?, - context: JsonDeserializationContext? - ): ShareAttributes? { - val jsonObject = json?.asJsonObject - val scope = jsonObject?.get("scope")?.asString ?: "" - val key = jsonObject?.get("key")?.asString ?: "" - val value = jsonObject.getBoolean("abstractValue") == true - return ShareAttributes(scope, key, value) - } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesJsonHandler.kt b/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesJsonHandler.kt deleted file mode 100644 index 50c4d5a68f..0000000000 --- a/library/src/main/java/com/owncloud/android/lib/resources/shares/attributes/ShareAttributesJsonHandler.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Nextcloud Android Library - * - * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2025 Alper Ozturk - * SPDX-License-Identifier: MIT - */ - -package com.owncloud.android.lib.resources.shares.attributes - -import com.google.gson.GsonBuilder -import com.google.gson.reflect.TypeToken - -object ShareAttributesJsonHandler { - private val gson = - GsonBuilder() - .registerTypeAdapter(ShareAttributes::class.java, ShareAttributesDeserializer()) - .create() - - fun toList(jsonString: String?): List? { - if (jsonString == null) { - return null - } - - val listType = object : TypeToken>() {}.type - return gson.fromJson(jsonString, listType) - } - - fun toJson(shareAttributes: List?): String? { - if (shareAttributes == null) { - return null - } - - return gson.toJson(shareAttributes) - } -} From 3e52ee08c193cf3d8962b7f55fc661392ab60eae Mon Sep 17 00:00:00 2001 From: alperozturk Date: Mon, 30 Jun 2025 11:13:04 +0200 Subject: [PATCH 4/7] useV2 Signed-off-by: alperozturk --- .../lib/resources/shares/CreateShareRemoteOperationIT.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt index 2f8d14bace..c5a3b9346b 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt @@ -19,15 +19,14 @@ import org.junit.Before import org.junit.Test class CreateShareRemoteOperationIT : AbstractIT() { - private var ownCloudVersion: OwnCloudVersion? = null @Before fun before() { val result = GetStatusRemoteOperation(context).execute(client) assertTrue(result.isSuccess) val data = result.data as ArrayList - ownCloudVersion = data[0] as OwnCloudVersion - Assume.assumeTrue(ownCloudVersion?.isNewerOrEqual(NextcloudVersion.nextcloud_24) == true) + val ownCloudVersion = data[0] as OwnCloudVersion + Assume.assumeTrue(ownCloudVersion.isNewerOrEqual(NextcloudVersion.nextcloud_24)) } @Test From 21beee45027b4dcf274cad4661d35157f13198db Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 1 Jul 2025 09:21:05 +0200 Subject: [PATCH 5/7] add OCShare ext Signed-off-by: alperozturk --- .../shares/CreateShareRemoteOperationIT.kt | 1 - .../shares/extensions/OCShareExtensions.kt | 69 +++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt index c5a3b9346b..0471536ed0 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/shares/CreateShareRemoteOperationIT.kt @@ -19,7 +19,6 @@ import org.junit.Before import org.junit.Test class CreateShareRemoteOperationIT : AbstractIT() { - @Before fun before() { val result = GetStatusRemoteOperation(context).execute(client) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt b/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt new file mode 100644 index 0000000000..79fb1e6e8f --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt @@ -0,0 +1,69 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.shares.extensions + +import com.owncloud.android.lib.resources.shares.OCShare +import org.json.JSONArray +import org.json.JSONObject + +private const val KEY = "key" +private const val SCOPE_KEY = "scope" +private const val DOWNLOAD_KEY = "download" +private const val PERMISSIONS_KEY = "permissions" +private const val VALUE_KEY = "value" +private const val ENABLED_KEY = "enabled" + +fun OCShare?.toggleAllowDownloadAndSync( + isChecked: Boolean, + useV2DownloadAttributes: Boolean +): String? { + val jsonArray = + if (this?.attributes?.isEmpty() == true) { + JSONArray() + } else { + JSONArray(this?.attributes) + } + val downloadAttr = jsonArray.findDownloadAttribute() + val enabledKey = getEnabledKey(useV2DownloadAttributes) + + if (downloadAttr != null) { + downloadAttr.put(enabledKey, isChecked) + } else { + jsonArray.put( + JSONObject().apply { + put(KEY, DOWNLOAD_KEY) + put(SCOPE_KEY, PERMISSIONS_KEY) + put(enabledKey, isChecked) + } + ) + } + + return jsonArray.toString() +} + +@Suppress("ReturnCount") +fun OCShare?.isAllowDownloadAndSyncEnabled(useV2DownloadAttributes: Boolean): Boolean { + if (this?.attributes.isNullOrEmpty()) return false + + val jsonArray = JSONArray(this.attributes) + val downloadAttr = jsonArray.findDownloadAttribute() ?: return false + val enabledKey = getEnabledKey(useV2DownloadAttributes) + + return downloadAttr.optBoolean(enabledKey, false) +} + +private fun JSONArray.findDownloadAttribute(): JSONObject? = + (0 until length()) + .asSequence() + .map { getJSONObject(it) } + .find { + it.optString(KEY) == DOWNLOAD_KEY && + it.optString(SCOPE_KEY) == PERMISSIONS_KEY + } + +private fun getEnabledKey(isV2: Boolean): String = if (isV2) VALUE_KEY else ENABLED_KEY From bb94fbce23f1a36c54400fc9f8452621fab4588c Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 1 Jul 2025 09:47:56 +0200 Subject: [PATCH 6/7] toggleAllowDownloadAndSync must not be extension thus user can create a new share Signed-off-by: alperozturk --- .../lib/resources/shares/extensions/OCShareExtensions.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt b/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt index 79fb1e6e8f..13574c9633 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt @@ -18,15 +18,16 @@ private const val PERMISSIONS_KEY = "permissions" private const val VALUE_KEY = "value" private const val ENABLED_KEY = "enabled" -fun OCShare?.toggleAllowDownloadAndSync( +fun toggleAllowDownloadAndSync( + attributes: String?, isChecked: Boolean, useV2DownloadAttributes: Boolean ): String? { val jsonArray = - if (this?.attributes?.isEmpty() == true) { + if (attributes?.isEmpty() == true) { JSONArray() } else { - JSONArray(this?.attributes) + JSONArray(attributes) } val downloadAttr = jsonArray.findDownloadAttribute() val enabledKey = getEnabledKey(useV2DownloadAttributes) From 0303be50f91e15fddffbee43f15de18bccf73018 Mon Sep 17 00:00:00 2001 From: alperozturk Date: Tue, 1 Jul 2025 10:03:41 +0200 Subject: [PATCH 7/7] fix Signed-off-by: alperozturk --- .../resources/shares/extensions/OCShareExtensions.kt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt b/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt index 13574c9633..ec1380c5d9 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/shares/extensions/OCShareExtensions.kt @@ -23,12 +23,11 @@ fun toggleAllowDownloadAndSync( isChecked: Boolean, useV2DownloadAttributes: Boolean ): String? { - val jsonArray = - if (attributes?.isEmpty() == true) { - JSONArray() - } else { - JSONArray(attributes) - } + var jsonArray = JSONArray() + if (!attributes.isNullOrEmpty()) { + jsonArray = JSONArray(attributes) + } + val downloadAttr = jsonArray.findDownloadAttribute() val enabledKey = getEnabledKey(useV2DownloadAttributes)