From f8a829d6b73bfe6e77f98bc278c428f53a778e20 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 02:15:21 +0000 Subject: [PATCH 01/62] Bump robolectric from 4.8.1 to 4.9 Bumps [robolectric](https://github.com/robolectric/robolectric) from 4.8.1 to 4.9. - [Release notes](https://github.com/robolectric/robolectric/releases) - [Commits](https://github.com/robolectric/robolectric/compare/robolectric-4.8.1...robolectric-4.9) --- updated-dependencies: - dependency-name: org.robolectric:robolectric dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- owncloudComLibrary/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudComLibrary/build.gradle b/owncloudComLibrary/build.gradle index cfcd453d..c3c59d9a 100644 --- a/owncloudComLibrary/build.gradle +++ b/owncloudComLibrary/build.gradle @@ -16,7 +16,7 @@ dependencies { kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshiVersion" testImplementation 'junit:junit:4.13.2' - testImplementation 'org.robolectric:robolectric:4.8.1' + testImplementation 'org.robolectric:robolectric:4.9' debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.6.0' } From c966bf14d0f6db273221e5d6a9cfbbdd3ed5482d Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Wed, 20 Apr 2022 15:12:38 +0200 Subject: [PATCH 02/62] add moshi classes for webfinger responses --- .../GetOCInstanceViaWebfingerOperation.kt | 39 ++++++++++++++ .../webfinger/responses/WebfingerResponse.kt | 39 ++++++++++++++ .../shares/responses/ShareeResponseTest.kt | 1 + .../GetOCInstanceViaWebfingerOperationTest.kt | 11 ++++ .../responses/WebfingerResponseTest.kt | 51 +++++++++++++++++++ .../broken_response.json | 15 ++++++ ...ot_containing_releavant_info_response.json | 10 ++++ .../simple_response.json | 9 ++++ .../to_much_information_response.json | 27 ++++++++++ 9 files changed, 202 insertions(+) create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt create mode 100644 owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperationTest.kt create mode 100644 owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt create mode 100644 owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/broken_response.json create mode 100644 owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_releavant_info_response.json create mode 100644 owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/simple_response.json create mode 100644 owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/to_much_information_response.json diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt new file mode 100644 index 00000000..e74f50b5 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt @@ -0,0 +1,39 @@ +/* ownCloud Android Library is available under MIT license + * + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.owncloud.android.lib.resources.webfinger + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult + +class GetOCInstanceViaWebfingerOperation( + private val rel:String, + private val resource:String +) : RemoteOperation() { + + override fun run(client: OwnCloudClient?): RemoteOperationResult { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt new file mode 100644 index 00000000..fcf9bf84 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt @@ -0,0 +1,39 @@ +/* ownCloud Android Library is available under MIT license + * + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.owncloud.android.lib.resources.webfinger.responses + +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class WebfingerJrdResponse ( + val subject:String, + val links:List +) + +@JsonClass(generateAdapter = true) +data class LinkItem( + val href:String, + val rel:String +) \ No newline at end of file diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt index 3981ab5b..fad1178a 100644 --- a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt @@ -25,6 +25,7 @@ package com.owncloud.android.lib.resources.shares.responses import com.owncloud.android.lib.resources.CommonOcsResponse +import com.owncloud.android.lib.resources.webfinger.responses.WebfingerJrdResponse import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.Types diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperationTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperationTest.kt new file mode 100644 index 00000000..d4f84a83 --- /dev/null +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperationTest.kt @@ -0,0 +1,11 @@ +package com.owncloud.android.lib.resources.webfinger + +import android.os.Build +import org.junit.runner.RunWith +import org.robolectric.RobolectricTestRunner +import org.robolectric.annotation.Config + +@RunWith(RobolectricTestRunner::class) +@Config(sdk = [Build.VERSION_CODES.O], manifest = Config.NONE) +class GetOCInstanceViaWebfingerOperationTest { +} \ No newline at end of file diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt new file mode 100644 index 00000000..9704326a --- /dev/null +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt @@ -0,0 +1,51 @@ +package com.owncloud.android.lib.resources.webfinger.responses + +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonDataException +import com.squareup.moshi.Moshi +import org.junit.Assert +import org.junit.Assert.assertThrows +import org.junit.Before +import org.junit.Test +import java.io.File + +class WebfingerResponseTest { + lateinit var adapter: JsonAdapter + + private fun loadResponses(fileName: String) = + adapter.fromJson(File(fileName).readText()) + + @Before + fun prepare() { + val moshi = Moshi.Builder().build() + adapter = moshi.adapter(WebfingerJrdResponse::class.java) + } + + @Test + fun `check rel in to much information - ok - correct rell is returned`() { + val response = loadResponses(TO_MUCH_INFORMATION_JSON)!! + Assert.assertEquals("https://gast.somedomain.de", response.links[0].href) + Assert.assertEquals("http://webfinger.owncloud/rel/server-instance", response.links[0].rel) + } + + @Test(expected = JsonDataException::class) + fun `check key value pairs - ko - no href key`() { + val response = loadResponses(BROKEN_JSON)!! + Assert.assertEquals("https://gast.somedomain.de", response.links[0].href) + } + + @Test(expected = JsonDataException::class) + fun `check key value pairs - ko - no rel key`() { + val response = loadResponses(BROKEN_JSON)!! + Assert.assertEquals("https://gast.somedomain.de", response.links[0].href) + } + + companion object { + val RESOURCES_PATH = + "src/test/responses/com.owncloud.android.lib.resources.webfinger.responses" + val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/simple_response.json" + val TO_MUCH_INFORMATION_JSON = "$RESOURCES_PATH/to_much_information_response.json" + val BROKEN_JSON = "$RESOURCES_PATH/broken_response.json" + val NOT_CONTAINING_RELEVANT_INFORMATION_JSON = "$RESOURCES_PATH/not_containing_relevant_info_response.json" + } +} \ No newline at end of file diff --git a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/broken_response.json b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/broken_response.json new file mode 100644 index 00000000..85dd2279 --- /dev/null +++ b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/broken_response.json @@ -0,0 +1,15 @@ +{ + "subject": "acct:bob@example.com", + "aliases": [ + "https://www.example.com/~bob/" + ], + "properties": { + "http://example.com/ns/role": "employee" + }, + "links": [ + { + "lel": "http://webfinger.example/rel/profile-page", + "foo": "https://www.example.com/~bob/" + } + ] +} \ No newline at end of file diff --git a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_releavant_info_response.json b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_releavant_info_response.json new file mode 100644 index 00000000..cae2ae74 --- /dev/null +++ b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_releavant_info_response.json @@ -0,0 +1,10 @@ +{ + "subject": "acct:peter.sine@gurken.xxx", + "links" : + [ + { + "rel" : "http://webfinger.example/rel/businesscard", + "href" : "https://www.example.com/~bob/bob.vcf" + } + ] +} \ No newline at end of file diff --git a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/simple_response.json b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/simple_response.json new file mode 100644 index 00000000..a7e18f2c --- /dev/null +++ b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/simple_response.json @@ -0,0 +1,9 @@ +{ + "links": [ + { + "href": "https://gast.somedomain.de", + "rel": "http://webfinger.owncloud/rel/server-instance" + } + ], + "subject": "acct:peter.sine@gurken.xxx" +} diff --git a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/to_much_information_response.json b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/to_much_information_response.json new file mode 100644 index 00000000..be02c6ad --- /dev/null +++ b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/to_much_information_response.json @@ -0,0 +1,27 @@ +{ + "subject": "acct:peter.sine@gurken.xxx", + "aliases" : + [ + "https://www.example.com/~bob/" + ], + "properties" : + { + "http://example.com/ns/role" : "employee" + }, + "gurken": { + "whatever": 42 + }, + "links" : + [ + { + "gurken": "sallat", + "href": "https://gast.somedomain.de", + "rel": "http://webfinger.owncloud/rel/server-instance" + }, + { + "gurken": "sallat", + "rel" : "http://webfinger.example/rel/businesscard", + "href" : "https://www.example.com/~bob/bob.vcf" + } + ] +} \ No newline at end of file From f19b2355b41c83443782216b328d50a4cfd1969b Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Wed, 20 Apr 2022 18:25:31 +0200 Subject: [PATCH 03/62] create scaffold for webfinger request operation --- .../GetOCInstanceViaWebfingerOperation.kt | 75 ++++++++++++++++++- .../GetOCInstanceViaWebfingerOperationTest.kt | 11 --- 2 files changed, 73 insertions(+), 13 deletions(-) delete mode 100644 owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperationTest.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt index e74f50b5..ee6c7e95 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt @@ -24,16 +24,87 @@ package com.owncloud.android.lib.resources.webfinger +import android.net.Uri import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod +import com.owncloud.android.lib.common.http.methods.nonwebdav.HttpMethod import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.status.HttpScheme +import com.owncloud.android.lib.resources.webfinger.responses.WebfingerJrdResponse +import com.squareup.moshi.Moshi +import timber.log.Timber +import java.net.URL class GetOCInstanceViaWebfingerOperation( + private val lockupServerDomain:String, private val rel:String, private val resource:String ) : RemoteOperation() { - override fun run(client: OwnCloudClient?): RemoteOperationResult { - TODO("Not yet implemented") + private fun buildRequestUri() = + Uri.parse(lockupServerDomain).buildUpon() + .scheme(HttpScheme.HTTPS_SCHEME) + .path(WEBFINGER_PATH) + .appendQueryParameter("rel", rel) + .appendQueryParameter("resource", resource) + .build() + + private fun isSuccess(status: Int): Boolean = status == HttpConstants.HTTP_OK + + private fun parseResponse(response: String): WebfingerJrdResponse { + val moshi = Moshi.Builder().build() + val adapter = moshi.adapter(WebfingerJrdResponse::class.java) + return adapter.fromJson(response)!! + } + + private fun onResultUnsuccessful( + method: HttpMethod, + response: String?, + status: Int + ): RemoteOperationResult { + Timber.e("Failed requesting webfinger info") + if (response != null) { + Timber.e("*** status code: $status; response message: $response") + } else { + Timber.e("*** status code: $status") + } + return RemoteOperationResult(method) + } + + private fun onRequestSuccessful(rawResponse:String): RemoteOperationResult { + val response = parseResponse(rawResponse) + for(i in response.links) { + if (i.rel == rel) { + val operationResult = RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + operationResult.data = i.href + return operationResult + } + } + Timber.e("Could not find ownCloud relevant information in webfinger response: $rawResponse") + throw java.lang.Exception("Could not find ownCloud relevant information in webfinger response") + } + + override fun run(client: OwnCloudClient): RemoteOperationResult { + val requestUri = buildRequestUri() + val getMethod = GetMethod(URL(requestUri.toString())) + return try { + val status = client.executeHttpMethod(getMethod) + val response = getMethod.getResponseBodyAsString()!! + + if (isSuccess(status)) { + onRequestSuccessful(response) + } else { + onResultUnsuccessful(getMethod, response, status) + } + } catch(e: Exception) { + Timber.e(e, "Requesting webfinger info failed") + RemoteOperationResult(e) + } + } + + companion object { + val WEBFINGER_PATH = "/.well-known/webfinger" } } \ No newline at end of file diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperationTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperationTest.kt deleted file mode 100644 index d4f84a83..00000000 --- a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperationTest.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.owncloud.android.lib.resources.webfinger - -import android.os.Build -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner -import org.robolectric.annotation.Config - -@RunWith(RobolectricTestRunner::class) -@Config(sdk = [Build.VERSION_CODES.O], manifest = Config.NONE) -class GetOCInstanceViaWebfingerOperationTest { -} \ No newline at end of file From 1afa124b7d705a4871bfc0ae0d3f5932876b9bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Mon, 10 Oct 2022 13:07:11 +0200 Subject: [PATCH 04/62] Add webfinger service and update the remote operation --- .../GetOCInstanceViaWebfingerOperation.kt | 20 +++++----- .../webfinger/services/WebfingerService.kt | 29 ++++++++++++++ .../implementation/OCWebfingerService.kt | 38 +++++++++++++++++++ 3 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/WebfingerService.kt create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/implementation/OCWebfingerService.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt index ee6c7e95..42ee4b12 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/GetOCInstanceViaWebfingerOperation.kt @@ -31,22 +31,20 @@ import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod import com.owncloud.android.lib.common.http.methods.nonwebdav.HttpMethod import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult -import com.owncloud.android.lib.resources.status.HttpScheme import com.owncloud.android.lib.resources.webfinger.responses.WebfingerJrdResponse import com.squareup.moshi.Moshi import timber.log.Timber import java.net.URL class GetOCInstanceViaWebfingerOperation( - private val lockupServerDomain:String, - private val rel:String, - private val resource:String + private val lockupServerDomain: String, + private val rel: String, + private val resource: String, ) : RemoteOperation() { private fun buildRequestUri() = Uri.parse(lockupServerDomain).buildUpon() - .scheme(HttpScheme.HTTPS_SCHEME) - .path(WEBFINGER_PATH) + .path(ENDPOINT_WEBFINGER_PATH) .appendQueryParameter("rel", rel) .appendQueryParameter("resource", resource) .build() @@ -73,9 +71,9 @@ class GetOCInstanceViaWebfingerOperation( return RemoteOperationResult(method) } - private fun onRequestSuccessful(rawResponse:String): RemoteOperationResult { + private fun onRequestSuccessful(rawResponse: String): RemoteOperationResult { val response = parseResponse(rawResponse) - for(i in response.links) { + for (i in response.links) { if (i.rel == rel) { val operationResult = RemoteOperationResult(RemoteOperationResult.ResultCode.OK) operationResult.data = i.href @@ -98,13 +96,13 @@ class GetOCInstanceViaWebfingerOperation( } else { onResultUnsuccessful(getMethod, response, status) } - } catch(e: Exception) { + } catch (e: Exception) { Timber.e(e, "Requesting webfinger info failed") RemoteOperationResult(e) } } companion object { - val WEBFINGER_PATH = "/.well-known/webfinger" + private const val ENDPOINT_WEBFINGER_PATH = "/.well-known/webfinger" } -} \ No newline at end of file +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/WebfingerService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/WebfingerService.kt new file mode 100644 index 00000000..4e057beb --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/WebfingerService.kt @@ -0,0 +1,29 @@ +/** + * ownCloud Android client application + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.lib.resources.webfinger.services + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult + +interface WebfingerService { + fun getInstanceFromWebfinger( + lookupServer: String, + username: String, + client: OwnCloudClient, + ): RemoteOperationResult +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/implementation/OCWebfingerService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/implementation/OCWebfingerService.kt new file mode 100644 index 00000000..3eab83d4 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/services/implementation/OCWebfingerService.kt @@ -0,0 +1,38 @@ +/** + * ownCloud Android client application + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.lib.resources.webfinger.services.implementation + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.webfinger.GetOCInstanceViaWebfingerOperation +import com.owncloud.android.lib.resources.webfinger.services.WebfingerService + +class OCWebfingerService : WebfingerService { + + override fun getInstanceFromWebfinger( + lookupServer: String, + username: String, + client: OwnCloudClient, + ): RemoteOperationResult = + GetOCInstanceViaWebfingerOperation(lookupServer, OWNCLOUD_REL, username).execute(client) + + companion object { + private const val OWNCLOUD_REL = "http://webfinger.owncloud/rel/server-instance" + } + +} From 70bf35f6830d6fcbb4c2160f5d6ea0e6520f9a1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Tue, 11 Oct 2022 18:00:03 +0200 Subject: [PATCH 05/62] Remove unused imports --- .../lib/resources/shares/responses/ShareeResponseTest.kt | 3 +-- .../lib/resources/webfinger/responses/WebfingerResponseTest.kt | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt index fad1178a..374429c3 100644 --- a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt @@ -25,7 +25,6 @@ package com.owncloud.android.lib.resources.shares.responses import com.owncloud.android.lib.resources.CommonOcsResponse -import com.owncloud.android.lib.resources.webfinger.responses.WebfingerJrdResponse import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.Types @@ -91,4 +90,4 @@ class ShareeResponseTest { val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/example_sharee_response.json" val EMPTY_RESPONSE_JSON = "$RESOURCES_PATH/empty_sharee_response.json" } -} \ No newline at end of file +} diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt index 9704326a..303e06d9 100644 --- a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt @@ -4,7 +4,6 @@ import com.squareup.moshi.JsonAdapter import com.squareup.moshi.JsonDataException import com.squareup.moshi.Moshi import org.junit.Assert -import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Test import java.io.File @@ -48,4 +47,4 @@ class WebfingerResponseTest { val BROKEN_JSON = "$RESOURCES_PATH/broken_response.json" val NOT_CONTAINING_RELEVANT_INFORMATION_JSON = "$RESOURCES_PATH/not_containing_relevant_info_response.json" } -} \ No newline at end of file +} From e7696849202a11c613604362d2f2a23036d899df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Thu, 13 Oct 2022 10:22:39 +0200 Subject: [PATCH 06/62] Reformat some webfinger classes --- .../webfinger/responses/WebfingerResponse.kt | 12 ++++++------ .../responses/WebfingerResponseTest.kt | 17 ++++++++--------- .../not_containing_releavant_info_response.json | 10 ---------- .../not_containing_relevant_info_response.json | 9 +++++++++ .../simple_response.json | 14 +++++++------- .../to_much_information_response.json | 17 +++++++---------- 6 files changed, 37 insertions(+), 42 deletions(-) delete mode 100644 owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_releavant_info_response.json create mode 100644 owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_relevant_info_response.json diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt index fcf9bf84..45e85a75 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponse.kt @@ -27,13 +27,13 @@ package com.owncloud.android.lib.resources.webfinger.responses import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) -data class WebfingerJrdResponse ( - val subject:String, - val links:List +data class WebfingerJrdResponse( + val subject: String, + val links: List ) @JsonClass(generateAdapter = true) data class LinkItem( - val href:String, - val rel:String -) \ No newline at end of file + val href: String, + val rel: String +) diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt index 303e06d9..2d0c6235 100644 --- a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/webfinger/responses/WebfingerResponseTest.kt @@ -11,8 +11,7 @@ import java.io.File class WebfingerResponseTest { lateinit var adapter: JsonAdapter - private fun loadResponses(fileName: String) = - adapter.fromJson(File(fileName).readText()) + private fun loadResponses(fileName: String) = adapter.fromJson(File(fileName).readText()) @Before fun prepare() { @@ -21,8 +20,8 @@ class WebfingerResponseTest { } @Test - fun `check rel in to much information - ok - correct rell is returned`() { - val response = loadResponses(TO_MUCH_INFORMATION_JSON)!! + fun `check rel in too much information - ok`() { + val response = loadResponses(TOO_MUCH_INFORMATION_JSON)!! Assert.assertEquals("https://gast.somedomain.de", response.links[0].href) Assert.assertEquals("http://webfinger.owncloud/rel/server-instance", response.links[0].rel) } @@ -40,11 +39,11 @@ class WebfingerResponseTest { } companion object { - val RESOURCES_PATH = + private const val RESOURCES_PATH = "src/test/responses/com.owncloud.android.lib.resources.webfinger.responses" - val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/simple_response.json" - val TO_MUCH_INFORMATION_JSON = "$RESOURCES_PATH/to_much_information_response.json" - val BROKEN_JSON = "$RESOURCES_PATH/broken_response.json" - val NOT_CONTAINING_RELEVANT_INFORMATION_JSON = "$RESOURCES_PATH/not_containing_relevant_info_response.json" + private const val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/simple_response.json" + private const val TOO_MUCH_INFORMATION_JSON = "$RESOURCES_PATH/to_much_information_response.json" + private const val BROKEN_JSON = "$RESOURCES_PATH/broken_response.json" + private const val NOT_CONTAINING_RELEVANT_INFORMATION_JSON = "$RESOURCES_PATH/not_containing_relevant_info_response.json" } } diff --git a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_releavant_info_response.json b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_releavant_info_response.json deleted file mode 100644 index cae2ae74..00000000 --- a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_releavant_info_response.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "subject": "acct:peter.sine@gurken.xxx", - "links" : - [ - { - "rel" : "http://webfinger.example/rel/businesscard", - "href" : "https://www.example.com/~bob/bob.vcf" - } - ] -} \ No newline at end of file diff --git a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_relevant_info_response.json b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_relevant_info_response.json new file mode 100644 index 00000000..712d113a --- /dev/null +++ b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/not_containing_relevant_info_response.json @@ -0,0 +1,9 @@ +{ + "subject": "acct:peter.sine@gurken.xxx", + "links": [ + { + "rel": "http://webfinger.example/rel/businesscard", + "href": "https://www.example.com/~bob/bob.vcf" + } + ] +} diff --git a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/simple_response.json b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/simple_response.json index a7e18f2c..bda67449 100644 --- a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/simple_response.json +++ b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/simple_response.json @@ -1,9 +1,9 @@ { - "links": [ - { - "href": "https://gast.somedomain.de", - "rel": "http://webfinger.owncloud/rel/server-instance" - } - ], - "subject": "acct:peter.sine@gurken.xxx" + "links": [ + { + "href": "https://gast.somedomain.de", + "rel": "http://webfinger.owncloud/rel/server-instance" + } + ], + "subject": "acct:peter.sine@gurken.xxx" } diff --git a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/to_much_information_response.json b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/to_much_information_response.json index be02c6ad..79c68f3c 100644 --- a/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/to_much_information_response.json +++ b/owncloudComLibrary/src/test/responses/com.owncloud.android.lib.resources.webfinger.responses/to_much_information_response.json @@ -1,18 +1,15 @@ { "subject": "acct:peter.sine@gurken.xxx", - "aliases" : - [ + "aliases": [ "https://www.example.com/~bob/" ], - "properties" : - { - "http://example.com/ns/role" : "employee" + "properties": { + "http://example.com/ns/role": "employee" }, "gurken": { "whatever": 42 }, - "links" : - [ + "links": [ { "gurken": "sallat", "href": "https://gast.somedomain.de", @@ -20,8 +17,8 @@ }, { "gurken": "sallat", - "rel" : "http://webfinger.example/rel/businesscard", - "href" : "https://www.example.com/~bob/bob.vcf" + "rel": "http://webfinger.example/rel/businesscard", + "href": "https://www.example.com/~bob/bob.vcf" } ] -} \ No newline at end of file +} From b083debac9a73f306c85e261ad26771c993543b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Tue, 11 Oct 2022 17:32:19 +0200 Subject: [PATCH 07/62] Handle 425 TOO EARLY propfind responses --- .../android/lib/common/http/HttpConstants.java | 1 + .../android/lib/resources/files/RemoteFile.java | 2 +- .../android/lib/resources/files/RemoteFileUtil.kt | 13 +++++++++++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java index 091643ab..8978a556 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpConstants.java @@ -184,6 +184,7 @@ public class HttpConstants { public static final int HTTP_LOCKED = 423; // 424 Failed Dependency (WebDAV - RFC 2518) public static final int HTTP_FAILED_DEPENDENCY = 424; + public static final int HTTP_TOO_EARLY = 425; /** * 5xx Client Error diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java index 84a48bcd..8d243d14 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java @@ -120,7 +120,7 @@ public class RemoteFile implements Parcelable, Serializable { public RemoteFile(final Response davResource, String userId) { this(RemoteFileUtil.Companion.getRemotePathFromUrl(davResource.getHref(), userId)); - final List properties = davResource.getProperties(); + final List properties = RemoteFileUtil.Companion.getProperties(davResource); for (Property property : properties) { if (property instanceof CreationDate) { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFileUtil.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFileUtil.kt index 30550e70..66609bd1 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFileUtil.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFileUtil.kt @@ -24,7 +24,11 @@ package com.owncloud.android.lib.resources.files import android.net.Uri +import at.bitfire.dav4jvm.PropStat +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.Response import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants import okhttp3.HttpUrl class RemoteFileUtil { @@ -45,5 +49,14 @@ class RemoteFileUtil { val pathToOc = absoluteDavPath.split(davFilesPath)[0] return absoluteDavPath.replace(pathToOc + davFilesPath, "") } + + fun getProperties(response: Response): List { + return if (response.isSuccess()) + response.propstat.filter { propStat -> propStat.isSuccessOrPostProcessing() }.map { it.properties }.flatten() + else + emptyList() + } + + private fun PropStat.isSuccessOrPostProcessing() = (status.code / 100 == 2 || status.code == HttpConstants.HTTP_TOO_EARLY) } } From 1e9d8cef42132c24fca2623660f04f1f5310add0 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Fri, 28 Oct 2022 10:05:09 +0200 Subject: [PATCH 08/62] Handle 425 when trying to open in web --- .../lib/common/operations/RemoteOperationResult.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java index 7430771d..5d849b08 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/operations/RemoteOperationResult.java @@ -237,6 +237,10 @@ public class RemoteOperationResult httpMethod.getResponseBodyAsString(), ResultCode.SPECIFIC_METHOD_NOT_ALLOWED ); + break; + case HttpConstants.HTTP_TOO_EARLY: + mCode = ResultCode.TOO_EARLY; + break; default: break; } @@ -583,6 +587,7 @@ public class RemoteOperationResult SPECIFIC_SERVICE_UNAVAILABLE, SPECIFIC_UNSUPPORTED_MEDIA_TYPE, SPECIFIC_METHOD_NOT_ALLOWED, - SPECIFIC_BAD_REQUEST + SPECIFIC_BAD_REQUEST, + TOO_EARLY, } } From d164b34fe891bf42e4102277d636bbfbf270b196 Mon Sep 17 00:00:00 2001 From: agarcia Date: Tue, 23 Jun 2020 16:10:36 +0200 Subject: [PATCH 09/62] Migrate ReadRemoteFolderOperation to kotlin and add it to FileService --- .../files/ReadRemoteFolderOperation.java | 128 ------------------ .../files/ReadRemoteFolderOperation.kt | 100 ++++++++++++++ .../resources/files/services/FileService.kt | 2 + .../services/implementation/OCFileService.kt | 8 +- 4 files changed, 108 insertions(+), 130 deletions(-) delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java deleted file mode 100644 index bf6a0604..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.java +++ /dev/null @@ -1,128 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.resources.files; - -import at.bitfire.dav4jvm.PropertyRegistry; -import at.bitfire.dav4jvm.Response; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.accounts.AccountUtils; -import com.owncloud.android.lib.common.http.HttpConstants; -import com.owncloud.android.lib.common.http.methods.webdav.DavConstants; -import com.owncloud.android.lib.common.http.methods.webdav.DavUtils; -import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod; -import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import timber.log.Timber; - -import java.net.URL; -import java.util.ArrayList; - -import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK; - -/** - * Remote operation performing the read of remote file or folder in the ownCloud server. - * - * @author David A. Velasco - * @author masensio - * @author David González Verdugo - */ - -public class ReadRemoteFolderOperation extends RemoteOperation> { - - private String mRemotePath; - - /** - * Constructor - * - * @param remotePath Remote path of the file. - */ - public ReadRemoteFolderOperation(String remotePath) { - mRemotePath = remotePath; - } - - /** - * Performs the read operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Override - protected RemoteOperationResult> run(OwnCloudClient client) { - RemoteOperationResult> result = null; - - try { - PropertyRegistry.INSTANCE.register(OCShareTypes.Factory.class.newInstance()); - PropfindMethod propfindMethod = new PropfindMethod( - new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), - DavConstants.DEPTH_1, - DavUtils.getAllPropset()); - - int status = client.executeHttpMethod(propfindMethod); - - if (isSuccess(status)) { - ArrayList mFolderAndFiles = new ArrayList<>(); - - // parse data from remote folder - mFolderAndFiles.add( - new RemoteFile(propfindMethod.getRoot(), AccountUtils.getUserId(mAccount, mContext)) - ); - - // loop to update every child - for (Response resource : propfindMethod.getMembers()) { - RemoteFile file = new RemoteFile(resource, AccountUtils.getUserId(mAccount, mContext)); - mFolderAndFiles.add(file); - } - - // Result of the operation - result = new RemoteOperationResult<>(OK); - result.setData(mFolderAndFiles); - - } else { // synchronization failed - result = new RemoteOperationResult<>(propfindMethod); - } - - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - } finally { - if (result == null) { - Timber.e("Synchronized " + mRemotePath + ": result is null"); - } else if (result.isSuccess()) { - Timber.i("Synchronized " + mRemotePath + ": " + result.getLogMessage()); - } else { - if (result.isException()) { - Timber.e(result.getException(), "Synchronized " + mRemotePath + ": " + result.getLogMessage()); - } else { - Timber.e("Synchronized " + mRemotePath + ": " + result.getLogMessage()); - } - } - } - return result; - } - - private boolean isSuccess(int status) { - return status == HttpConstants.HTTP_MULTI_STATUS || status == HttpConstants.HTTP_OK; - } -} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt new file mode 100644 index 00000000..619e7a03 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt @@ -0,0 +1,100 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2020 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.resources.files + +import at.bitfire.dav4jvm.PropertyRegistry +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.accounts.AccountUtils +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.webdav.DavConstants +import com.owncloud.android.lib.common.http.methods.webdav.DavUtils +import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod +import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import timber.log.Timber +import java.net.URL + +/** + * Remote operation performing the read of remote file or folder in the ownCloud server. + * + * @author David A. Velasco + * @author masensio + * @author David González Verdugo + */ +class ReadRemoteFolderOperation( + val remotePath: String +) : RemoteOperation>() { + + /** + * Performs the read operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + override fun run(client: OwnCloudClient): RemoteOperationResult> { + try { + PropertyRegistry.register(OCShareTypes.Factory()) + + val propfindMethod = PropfindMethod( + URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)), + DavConstants.DEPTH_1, + DavUtils.allPropset + ) + + val status = client.executeHttpMethod(propfindMethod) + + if (isSuccess(status)) { + val mFolderAndFiles = ArrayList() + + // parse data from remote folder + mFolderAndFiles.add(RemoteFile(propfindMethod.root, AccountUtils.getUserId(mAccount, mContext))) + + // loop to update every child + propfindMethod.members.forEach { resource -> + val file = RemoteFile(resource, AccountUtils.getUserId(mAccount, mContext)) + mFolderAndFiles.add(file) + } + + // Result of the operation + return RemoteOperationResult>(ResultCode.OK).apply { + data = mFolderAndFiles + Timber.i("Synchronized $remotePath with ${mFolderAndFiles.size} files. ${this.logMessage}") + } + } else { // synchronization failed + return RemoteOperationResult>(propfindMethod).also { + Timber.w("Synchronized $remotePath ${it.logMessage}") + } + } + } catch (e: Exception) { + return RemoteOperationResult>(e).also { + Timber.e(it.exception, "Synchronized $remotePath") + } + } + } + + private fun isSuccess(status: Int): Boolean = + status == HttpConstants.HTTP_MULTI_STATUS || status == HttpConstants.HTTP_OK +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index 4de85052..85ad1c03 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -25,8 +25,10 @@ package com.owncloud.android.lib.resources.files.services import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.Service +import com.owncloud.android.lib.resources.files.RemoteFile interface FileService : Service { fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult + fun refreshFolder(remotePath: String): RemoteOperationResult> } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index b0670a9f..dee92cfb 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -27,10 +27,11 @@ import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation +import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation +import com.owncloud.android.lib.resources.files.RemoteFile import com.owncloud.android.lib.resources.files.services.FileService -class OCFileService(override val client: OwnCloudClient) : - FileService { +class OCFileService(override val client: OwnCloudClient) : FileService { override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult = CheckPathExistenceRemoteOperation( remotePath = path, @@ -40,4 +41,7 @@ class OCFileService(override val client: OwnCloudClient) : override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult = GetUrlToOpenInWebRemoteOperation(openWithWebEndpoint = openWebEndpoint, fileId = fileId).execute(client) + override fun refreshFolder(remotePath: String): RemoteOperationResult> { + return ReadRemoteFolderOperation(remotePath = remotePath).execute(client) + } } From 32891e2cf553bda7aa1ef5afe35782bce53b4131 Mon Sep 17 00:00:00 2001 From: agarcia Date: Tue, 23 Jun 2020 17:46:48 +0200 Subject: [PATCH 10/62] Add owner field to remote file --- .../lib/resources/files/ReadRemoteFolderOperation.kt | 6 +++++- .../owncloud/android/lib/resources/files/RemoteFile.java | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt index 619e7a03..f838c7e3 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt @@ -70,11 +70,15 @@ class ReadRemoteFolderOperation( val mFolderAndFiles = ArrayList() // parse data from remote folder - mFolderAndFiles.add(RemoteFile(propfindMethod.root, AccountUtils.getUserId(mAccount, mContext))) + val remoteFolder = RemoteFile(propfindMethod.root, AccountUtils.getUserId(mAccount, mContext)).apply { + owner = mAccount.name + } + mFolderAndFiles.add(remoteFolder) // loop to update every child propfindMethod.members.forEach { resource -> val file = RemoteFile(resource, AccountUtils.getUserId(mAccount, mContext)) + file.owner = mAccount.name mFolderAndFiles.add(file) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java index 8d243d14..c73e15ff 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java @@ -91,6 +91,7 @@ public class RemoteFile implements Parcelable, Serializable { private String mPrivateLink; private boolean mSharedByLink; private boolean mSharedWithSharee; + private String mOwner; public RemoteFile() { resetData(); @@ -305,6 +306,14 @@ public class RemoteFile implements Parcelable, Serializable { return mSharedByLink; } + public String getOwner() { + return mOwner; + } + + public void setOwner(String owner) { + mOwner = owner; + } + /** * Used internally. Reset all file properties */ From 2b5f80bdb964ba3a6e5898a6412a04b32e983ee4 Mon Sep 17 00:00:00 2001 From: agarcia Date: Tue, 28 Jul 2020 13:56:19 +0200 Subject: [PATCH 11/62] Apply CR suggestions --- .../files/services/implementation/OCFileService.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index dee92cfb..7861f57f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -41,7 +41,8 @@ class OCFileService(override val client: OwnCloudClient) : FileService { override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult = GetUrlToOpenInWebRemoteOperation(openWithWebEndpoint = openWebEndpoint, fileId = fileId).execute(client) - override fun refreshFolder(remotePath: String): RemoteOperationResult> { - return ReadRemoteFolderOperation(remotePath = remotePath).execute(client) - } + override fun refreshFolder(remotePath: String): RemoteOperationResult> = + ReadRemoteFolderOperation( + remotePath = remotePath + ).execute(client) } From b0798194bef861c839f98e8d1c26b873830b1f8b Mon Sep 17 00:00:00 2001 From: agarcia Date: Tue, 28 Jul 2020 17:54:57 +0200 Subject: [PATCH 12/62] Migrate CreateRemoteFolderOperation to kotlin --- .../files/CreateRemoteFolderOperation.java | 114 ------------------ .../files/CreateRemoteFolderOperation.kt | 101 ++++++++++++++++ .../CreateRemoteChunkFolderOperation.java | 45 ------- 3 files changed, 101 insertions(+), 159 deletions(-) delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/CreateRemoteChunkFolderOperation.java diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java deleted file mode 100644 index 302df20d..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.java +++ /dev/null @@ -1,114 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2019 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.resources.files; - -import android.net.Uri; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.http.HttpConstants; -import com.owncloud.android.lib.common.http.methods.webdav.MkColMethod; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; -import timber.log.Timber; - -import java.net.URL; -import java.util.concurrent.TimeUnit; - -/** - * Remote operation performing the creation of a new folder in the ownCloud server. - * - * @author David A. Velasco - * @author masensio - */ -public class CreateRemoteFolderOperation extends RemoteOperation { - - private static final int READ_TIMEOUT = 30000; - private static final int CONNECTION_TIMEOUT = 5000; - - private String mRemotePath; - private boolean mCreateFullPath; - protected boolean createChunksFolder; - - /** - * Constructor - * - * @param remotePath Full path to the new directory to create in the remote server. - * @param createFullPath 'True' means that all the ancestor folders should be created. - */ - public CreateRemoteFolderOperation(String remotePath, boolean createFullPath) { - mRemotePath = remotePath; - mCreateFullPath = createFullPath; - createChunksFolder = false; - } - - /** - * Performs the operation - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result = createFolder(client); - if (!result.isSuccess() && mCreateFullPath && - RemoteOperationResult.ResultCode.CONFLICT == result.getCode()) { - result = createParentFolder(FileUtils.getParentPath(mRemotePath), client); - if (result.isSuccess()) { - result = createFolder(client); // second (and last) try - } - } - return result; - } - - private RemoteOperationResult createFolder(OwnCloudClient client) { - RemoteOperationResult result; - try { - Uri webDavUri = createChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri(); - final MkColMethod mkcol = new MkColMethod( - new URL(webDavUri + WebdavUtils.encodePath(mRemotePath))); - mkcol.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS); - mkcol.setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS); - final int status = client.executeHttpMethod(mkcol); - - result = (status == HttpConstants.HTTP_CREATED) - ? new RemoteOperationResult<>(ResultCode.OK) - : new RemoteOperationResult<>(mkcol); - Timber.d("Create directory " + mRemotePath + ": " + result.getLogMessage()); - client.exhaustResponse(mkcol.getResponseBodyAsStream()); - - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - Timber.e(e, "Create directory " + mRemotePath + ": " + result.getLogMessage()); - } - - return result; - } - - private RemoteOperationResult createParentFolder(String parentPath, OwnCloudClient client) { - RemoteOperation operation = new CreateRemoteFolderOperation(parentPath, mCreateFullPath); - return operation.execute(client); - } -} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt new file mode 100644 index 00000000..f5c8176d --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt @@ -0,0 +1,101 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2020 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.resources.files + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.webdav.MkColMethod +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import timber.log.Timber +import java.net.URL +import java.util.concurrent.TimeUnit + +/** + * Remote operation performing the creation of a new folder in the ownCloud server. + * + * @author David A. Velasco + * @author masensio + * + * @param remotePath Full path to the new directory to create in the remote server. + * @param createFullPath 'True' means that all the ancestor folders should be created. + */ +class CreateRemoteFolderOperation( + val remotePath: String, + private val createFullPath: Boolean, + private val isChunksFolder: Boolean = false +) : RemoteOperation() { + + override fun run(client: OwnCloudClient): RemoteOperationResult { + + var result = createFolder(client) + if (!result.isSuccess && createFullPath && result.code == ResultCode.CONFLICT) { + result = createParentFolder(FileUtils.getParentPath(remotePath), client) + + if (result.isSuccess) { + result = createFolder(client) + } + } + return result + } + + private fun createFolder(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + try { + val webDavUri = if (isChunksFolder) client.uploadsWebDavUri else client.userFilesWebDavUri + val mkcol = MkColMethod(URL(webDavUri.toString() + WebdavUtils.encodePath(remotePath))).apply { + setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS) + setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS) + } + + val status = client.executeHttpMethod(mkcol) + result = + if (status == HttpConstants.HTTP_CREATED) { + RemoteOperationResult(ResultCode.OK) + } else { + RemoteOperationResult(mkcol) + } + + Timber.d("Create directory $remotePath: ${result.logMessage}") + client.exhaustResponse(mkcol.getResponseBodyAsStream()) + + } catch (e: Exception) { + result = RemoteOperationResult(e) + Timber.e(e, "Create directory $remotePath: ${result.logMessage}") + } + return result + } + + private fun createParentFolder(parentPath: String, client: OwnCloudClient): RemoteOperationResult { + val operation: RemoteOperation = CreateRemoteFolderOperation(parentPath, createFullPath) + return operation.execute(client) + } + + companion object { + private const val READ_TIMEOUT: Long = 30_000 + private const val CONNECTION_TIMEOUT: Long = 5_000 + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/CreateRemoteChunkFolderOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/CreateRemoteChunkFolderOperation.java deleted file mode 100644 index fc2d797f..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/CreateRemoteChunkFolderOperation.java +++ /dev/null @@ -1,45 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.resources.files.chunks; - -import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation; - -/** - * Remote operation performing the creation of a new folder to save chunks during an upload to the ownCloud server. - * - * @author David González Verdugo - */ -public class CreateRemoteChunkFolderOperation extends CreateRemoteFolderOperation { - /** - * Constructor - * - * @param remotePath Full path to the new directory to create in the remote server. - * @param createFullPath 'True' means that all the ancestor folders should be created. - */ - public CreateRemoteChunkFolderOperation(String remotePath, boolean createFullPath) { - super(remotePath, createFullPath); - createChunksFolder = true; - } -} \ No newline at end of file From cd94e39b7f15919474db0a39043a652450dcb2b4 Mon Sep 17 00:00:00 2001 From: agarcia Date: Tue, 28 Jul 2020 18:08:57 +0200 Subject: [PATCH 13/62] Include createFolder as file service operation --- .../lib/resources/files/services/FileService.kt | 1 + .../services/implementation/OCFileService.kt | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index 85ad1c03..00547f5c 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -30,5 +30,6 @@ import com.owncloud.android.lib.resources.files.RemoteFile interface FileService : Service { fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult + fun createFolder(remotePath: String, createFullPath: Boolean, isChunkFolder: Boolean = false): RemoteOperationResult fun refreshFolder(remotePath: String): RemoteOperationResult> } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index 7861f57f..a21c6fe0 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -26,13 +26,17 @@ package com.owncloud.android.lib.resources.files.services.implementation import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation +import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation import com.owncloud.android.lib.resources.files.RemoteFile import com.owncloud.android.lib.resources.files.services.FileService class OCFileService(override val client: OwnCloudClient) : FileService { - override fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult = + override fun checkPathExistence( + path: String, + isUserLogged: Boolean + ): RemoteOperationResult = CheckPathExistenceRemoteOperation( remotePath = path, isUserLoggedIn = isUserLogged @@ -41,6 +45,17 @@ class OCFileService(override val client: OwnCloudClient) : FileService { override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult = GetUrlToOpenInWebRemoteOperation(openWithWebEndpoint = openWebEndpoint, fileId = fileId).execute(client) + override fun createFolder( + remotePath: String, + createFullPath: Boolean, + isChunkFolder: Boolean + ): RemoteOperationResult = + CreateRemoteFolderOperation( + remotePath = remotePath, + createFullPath = createFullPath, + isChunksFolder = isChunkFolder + ).execute(client) + override fun refreshFolder(remotePath: String): RemoteOperationResult> = ReadRemoteFolderOperation( remotePath = remotePath From 5e478036ae1012a2f232dbb008cf8e71905eb6a0 Mon Sep 17 00:00:00 2001 From: agarcia Date: Wed, 5 Aug 2020 14:00:48 +0200 Subject: [PATCH 14/62] Apply code review suggestions --- .../files/CreateRemoteFolderOperation.kt | 22 +++++++++++++------ .../resources/files/services/FileService.kt | 18 ++++++++++++--- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt index f5c8176d..372c8f21 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CreateRemoteFolderOperation.kt @@ -56,6 +56,7 @@ class CreateRemoteFolderOperation( result = createParentFolder(FileUtils.getParentPath(remotePath), client) if (result.isSuccess) { + // Second and last try result = createFolder(client) } } @@ -65,25 +66,32 @@ class CreateRemoteFolderOperation( private fun createFolder(client: OwnCloudClient): RemoteOperationResult { var result: RemoteOperationResult try { - val webDavUri = if (isChunksFolder) client.uploadsWebDavUri else client.userFilesWebDavUri - val mkcol = MkColMethod(URL(webDavUri.toString() + WebdavUtils.encodePath(remotePath))).apply { + val webDavUri = if (isChunksFolder) { + client.uploadsWebDavUri + } else { + client.userFilesWebDavUri + } + + val mkCol = MkColMethod( + URL(webDavUri.toString() + WebdavUtils.encodePath(remotePath)) + ).apply { setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS) setConnectionTimeout(CONNECTION_TIMEOUT, TimeUnit.SECONDS) } - val status = client.executeHttpMethod(mkcol) + val status = client.executeHttpMethod(mkCol) result = if (status == HttpConstants.HTTP_CREATED) { - RemoteOperationResult(ResultCode.OK) + RemoteOperationResult(ResultCode.OK) } else { - RemoteOperationResult(mkcol) + RemoteOperationResult(mkCol) } Timber.d("Create directory $remotePath: ${result.logMessage}") - client.exhaustResponse(mkcol.getResponseBodyAsStream()) + client.exhaustResponse(mkCol.getResponseBodyAsStream()) } catch (e: Exception) { - result = RemoteOperationResult(e) + result = RemoteOperationResult(e) Timber.e(e, "Create directory $remotePath: ${result.logMessage}") } return result diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index 00547f5c..904496c0 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -28,8 +28,20 @@ import com.owncloud.android.lib.resources.Service import com.owncloud.android.lib.resources.files.RemoteFile interface FileService : Service { - fun checkPathExistence(path: String, isUserLogged: Boolean): RemoteOperationResult fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult - fun createFolder(remotePath: String, createFullPath: Boolean, isChunkFolder: Boolean = false): RemoteOperationResult - fun refreshFolder(remotePath: String): RemoteOperationResult> + + fun checkPathExistence( + path: String, + isUserLogged: Boolean + ): RemoteOperationResult + + fun createFolder( + remotePath: String, + createFullPath: Boolean, + isChunkFolder: Boolean = false + ): RemoteOperationResult + + fun refreshFolder( + remotePath: String + ): RemoteOperationResult> } From 53c99a21c2328177591655c1d7efa46755a6c8f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Thu, 8 Oct 2020 09:33:04 +0200 Subject: [PATCH 15/62] Transform RemoteFile to kotlin and apply necessary changes --- owncloudComLibrary/build.gradle | 1 + .../files/ReadRemoteFileOperation.java | 3 +- .../files/ReadRemoteFolderOperation.kt | 18 +- .../lib/resources/files/RemoteFile.java | 372 ------------------ .../android/lib/resources/files/RemoteFile.kt | 174 ++++++++ .../lib/resources/files/RemoteFileUtil.kt | 62 --- 6 files changed, 189 insertions(+), 441 deletions(-) delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFileUtil.kt diff --git a/owncloudComLibrary/build.gradle b/owncloudComLibrary/build.gradle index c3c59d9a..8153da36 100644 --- a/owncloudComLibrary/build.gradle +++ b/owncloudComLibrary/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' +apply plugin: 'kotlin-android-extensions' dependencies { api 'com.squareup.okhttp3:okhttp:4.6.0' diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java index 24496ebe..237ce877 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java @@ -88,7 +88,8 @@ public class ReadRemoteFileOperation extends RemoteOperation { if (status == HttpConstants.HTTP_MULTI_STATUS || status == HttpConstants.HTTP_OK) { - final RemoteFile file = new RemoteFile(propfind.getRoot(), AccountUtils.getUserId(mAccount, mContext)); + final RemoteFile file = RemoteFile.Companion.getRemoteFileFromDav(propfind.getRoot(), + AccountUtils.getUserId(mAccount, mContext), mAccount.name); result = new RemoteOperationResult<>(OK); result.setData(file); diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt index f838c7e3..82f14ab1 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt @@ -70,16 +70,22 @@ class ReadRemoteFolderOperation( val mFolderAndFiles = ArrayList() // parse data from remote folder - val remoteFolder = RemoteFile(propfindMethod.root, AccountUtils.getUserId(mAccount, mContext)).apply { - owner = mAccount.name - } + // TODO: Remove that !! + val remoteFolder = RemoteFile.getRemoteFileFromDav( + davResource = propfindMethod.root!!, + userId = AccountUtils.getUserId(mAccount, mContext), + userName = mAccount.name + ) mFolderAndFiles.add(remoteFolder) // loop to update every child propfindMethod.members.forEach { resource -> - val file = RemoteFile(resource, AccountUtils.getUserId(mAccount, mContext)) - file.owner = mAccount.name - mFolderAndFiles.add(file) + val remoteFile = RemoteFile.getRemoteFileFromDav( + davResource = resource, + userId = AccountUtils.getUserId(mAccount, mContext), + userName = mAccount.name + ) + mFolderAndFiles.add(remoteFile) } // Result of the operation diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java deleted file mode 100644 index c73e15ff..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.java +++ /dev/null @@ -1,372 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.resources.files; - -import android.os.Parcel; -import android.os.Parcelable; - -import at.bitfire.dav4jvm.Property; -import at.bitfire.dav4jvm.Response; -import at.bitfire.dav4jvm.property.CreationDate; -import at.bitfire.dav4jvm.property.GetContentLength; -import at.bitfire.dav4jvm.property.GetContentType; -import at.bitfire.dav4jvm.property.GetETag; -import at.bitfire.dav4jvm.property.GetLastModified; -import at.bitfire.dav4jvm.property.OCId; -import at.bitfire.dav4jvm.property.OCPermissions; -import at.bitfire.dav4jvm.property.OCPrivatelink; -import at.bitfire.dav4jvm.property.OCSize; -import at.bitfire.dav4jvm.property.QuotaAvailableBytes; -import at.bitfire.dav4jvm.property.QuotaUsedBytes; -import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes; -import com.owncloud.android.lib.resources.shares.ShareType; -import timber.log.Timber; - -import java.io.File; -import java.io.Serializable; -import java.math.BigDecimal; -import java.util.LinkedList; -import java.util.List; - -/** - * Contains the data of a Remote File from a WebDavEntry - * - * @author masensio - * @author Christian Schabesberger - */ - -public class RemoteFile implements Parcelable, Serializable { - - /** - * Parcelable Methods - */ - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public RemoteFile createFromParcel(Parcel source) { - return new RemoteFile(source); - } - - @Override - public RemoteFile[] newArray(int size) { - return new RemoteFile[size]; - } - }; - /** - * Generated - should be refreshed every time the class changes!! - */ - private static final long serialVersionUID = -8965995357413958539L; - private String mRemotePath; - private String mMimeType; - private long mLength; - private long mCreationTimestamp; - private long mModifiedTimestamp; - private String mEtag; - private String mPermissions; - private String mRemoteId; - private long mSize; - private BigDecimal mQuotaUsedBytes; - private BigDecimal mQuotaAvailableBytes; - private String mPrivateLink; - private boolean mSharedByLink; - private boolean mSharedWithSharee; - private String mOwner; - - public RemoteFile() { - resetData(); - } - - /** - * Create new {@link RemoteFile} with given path. - *

- * The path received must be URL-decoded. Path separator must be File.separator, and it must be the first - * character in 'path'. - * - * @param path The remote path of the file. - */ - public RemoteFile(String path) { - resetData(); - if (path == null || path.length() <= 0 || !path.startsWith(File.separator)) { - throw new IllegalArgumentException("Trying to create a OCFile with a non valid remote path: " + path); - } - mRemotePath = path; - mCreationTimestamp = 0; - mLength = 0; - mMimeType = FileUtils.MIME_DIR; - mQuotaUsedBytes = BigDecimal.ZERO; - mQuotaAvailableBytes = BigDecimal.ZERO; - mPrivateLink = null; - } - - public RemoteFile(final Response davResource, String userId) { - this(RemoteFileUtil.Companion.getRemotePathFromUrl(davResource.getHref(), userId)); - final List properties = RemoteFileUtil.Companion.getProperties(davResource); - - for (Property property : properties) { - if (property instanceof CreationDate) { - this.setCreationTimestamp( - Long.parseLong(((CreationDate) property).getCreationDate())); - } - if (property instanceof GetContentLength) { - this.setLength(((GetContentLength) property).getContentLength()); - } - if (property instanceof GetContentType) { - this.setMimeType(((GetContentType) property).getType()); - } - if (property instanceof GetLastModified) { - this.setModifiedTimestamp(((GetLastModified) property).getLastModified()); - } - if (property instanceof GetETag) { - this.setEtag(((GetETag) property).getETag()); - } - if (property instanceof OCPermissions) { - this.setPermissions(((OCPermissions) property).getPermission()); - } - if (property instanceof OCId) { - this.setRemoteId(((OCId) property).getId()); - } - if (property instanceof OCSize) { - this.setSize(((OCSize) property).getSize()); - } - if (property instanceof QuotaUsedBytes) { - this.setQuotaUsedBytes( - BigDecimal.valueOf(((QuotaUsedBytes) property).getQuotaUsedBytes())); - } - if (property instanceof QuotaAvailableBytes) { - this.setQuotaAvailableBytes( - BigDecimal.valueOf(((QuotaAvailableBytes) property).getQuotaAvailableBytes())); - } - if (property instanceof OCPrivatelink) { - this.setPrivateLink(((OCPrivatelink) property).getLink()); - } - if (property instanceof OCShareTypes) { - LinkedList list = ((OCShareTypes) property).getShareTypes(); - for (int i = 0; i < list.size(); i++) { - ShareType shareType = ShareType.Companion.fromValue(Integer.parseInt(list.get(i))); - if (shareType == null) { - Timber.d("Illegal share type value: " + list.get(i)); - continue; - } - if (shareType.equals(ShareType.PUBLIC_LINK)) { - this.setSharedViaLink(true); - } else if (shareType.equals(ShareType.USER) || - shareType.equals(ShareType.FEDERATED) || - shareType.equals(ShareType.GROUP)) { - this.setSharedWithSharee(true); - } - } - } - } - } - - /** - * Reconstruct from parcel - * - * @param source The source parcel - */ - protected RemoteFile(Parcel source) { - readFromParcel(source); - } - - /** - * Use this to find out if this file is a folder. - * - * @return true if it is a folder - */ - public boolean isFolder() { - return mMimeType != null && (mMimeType.equals(FileUtils.MIME_DIR) || mMimeType.equals(FileUtils.MIME_DIR_UNIX)); - } - - /** - * Getters and Setters - */ - - public String getRemotePath() { - return mRemotePath; - } - - public void setRemotePath(String remotePath) { - this.mRemotePath = remotePath; - } - - public String getMimeType() { - return mMimeType; - } - - public void setMimeType(String mimeType) { - this.mMimeType = mimeType; - } - - public long getLength() { - return mLength; - } - - public void setLength(long length) { - this.mLength = length; - } - - public long getCreationTimestamp() { - return mCreationTimestamp; - } - - public void setCreationTimestamp(long creationTimestamp) { - this.mCreationTimestamp = creationTimestamp; - } - - public long getModifiedTimestamp() { - return mModifiedTimestamp; - } - - public void setModifiedTimestamp(long modifiedTimestamp) { - this.mModifiedTimestamp = modifiedTimestamp; - } - - public String getEtag() { - return mEtag; - } - - public void setEtag(String etag) { - this.mEtag = etag; - } - - public String getPermissions() { - return mPermissions; - } - - public void setPermissions(String permissions) { - this.mPermissions = permissions; - } - - public String getRemoteId() { - return mRemoteId; - } - - public void setRemoteId(String remoteId) { - this.mRemoteId = remoteId; - } - - public long getSize() { - return mSize; - } - - public void setSize(long size) { - mSize = size; - } - - public void setQuotaUsedBytes(BigDecimal quotaUsedBytes) { - mQuotaUsedBytes = quotaUsedBytes; - } - - public void setQuotaAvailableBytes(BigDecimal quotaAvailableBytes) { - mQuotaAvailableBytes = quotaAvailableBytes; - } - - public String getPrivateLink() { - return mPrivateLink; - } - - public void setPrivateLink(String privateLink) { - mPrivateLink = privateLink; - } - - public void setSharedWithSharee(boolean shareWithSharee) { - mSharedWithSharee = shareWithSharee; - } - - public boolean isSharedWithSharee() { - return mSharedWithSharee; - } - - public void setSharedViaLink(boolean sharedViaLink) { - mSharedByLink = sharedViaLink; - } - - public boolean isSharedByLink() { - return mSharedByLink; - } - - public String getOwner() { - return mOwner; - } - - public void setOwner(String owner) { - mOwner = owner; - } - - /** - * Used internally. Reset all file properties - */ - private void resetData() { - mRemotePath = null; - mMimeType = null; - mLength = 0; - mCreationTimestamp = 0; - mModifiedTimestamp = 0; - mEtag = null; - mPermissions = null; - mRemoteId = null; - mSize = 0; - mQuotaUsedBytes = null; - mQuotaAvailableBytes = null; - mPrivateLink = null; - mSharedWithSharee = false; - mSharedByLink = false; - } - - public void readFromParcel(Parcel source) { - mRemotePath = source.readString(); - mMimeType = source.readString(); - mLength = source.readLong(); - mCreationTimestamp = source.readLong(); - mModifiedTimestamp = source.readLong(); - mEtag = source.readString(); - mPermissions = source.readString(); - mRemoteId = source.readString(); - mSize = source.readLong(); - mQuotaUsedBytes = (BigDecimal) source.readSerializable(); - mQuotaAvailableBytes = (BigDecimal) source.readSerializable(); - mPrivateLink = source.readString(); - } - - @Override - public int describeContents() { - return this.hashCode(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mRemotePath); - dest.writeString(mMimeType); - dest.writeLong(mLength); - dest.writeLong(mCreationTimestamp); - dest.writeLong(mModifiedTimestamp); - dest.writeString(mEtag); - dest.writeString(mPermissions); - dest.writeString(mRemoteId); - dest.writeLong(mSize); - dest.writeSerializable(mQuotaUsedBytes); - dest.writeSerializable(mQuotaAvailableBytes); - dest.writeString(mPrivateLink); - } -} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt new file mode 100644 index 00000000..4535a07b --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt @@ -0,0 +1,174 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2020 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.resources.files + +import android.net.Uri +import android.os.Parcelable +import at.bitfire.dav4jvm.PropStat +import at.bitfire.dav4jvm.Property +import at.bitfire.dav4jvm.Response +import at.bitfire.dav4jvm.property.CreationDate +import at.bitfire.dav4jvm.property.GetContentLength +import at.bitfire.dav4jvm.property.GetContentType +import at.bitfire.dav4jvm.property.GetETag +import at.bitfire.dav4jvm.property.GetLastModified +import at.bitfire.dav4jvm.property.OCId +import at.bitfire.dav4jvm.property.OCPermissions +import at.bitfire.dav4jvm.property.OCPrivatelink +import at.bitfire.dav4jvm.property.OCSize +import at.bitfire.dav4jvm.property.QuotaAvailableBytes +import at.bitfire.dav4jvm.property.QuotaUsedBytes +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes +import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.lib.resources.shares.ShareType.Companion.fromValue +import kotlinx.parcelize.Parcelize +import okhttp3.HttpUrl +import timber.log.Timber +import java.io.File +import java.math.BigDecimal + +/** + * Contains the data of a Remote File from a WebDavEntry + * + * The path received must be URL-decoded. Path separator must be File.separator, and it must be the first character in 'path'. + * + * @author masensio + * @author Christian Schabesberger + * @author Abel García de Prada + */ +@Parcelize +data class RemoteFile( + var remotePath: String, + var mimeType: String = "DIR", + var length: Long = 0, + var creationTimestamp: Long = 0, + var modifiedTimestamp: Long = 0, + var etag: String? = null, + var permissions: String? = null, + var remoteId: String? = null, + var size: Long = 0, + var quotaUsedBytes: BigDecimal? = null, + var quotaAvailableBytes: BigDecimal? = null, + var privateLink: String? = null, + var owner: String, + var sharedByLink: Boolean = false, + var sharedWithSharee: Boolean = false, +) : Parcelable { + + init { + require(!(remotePath.isEmpty() || !remotePath.startsWith(File.separator))) { "Trying to create a OCFile with a non valid remote path: $remotePath" } + } + + companion object { + + fun getRemoteFileFromDav(davResource: Response, userId: String, userName: String): RemoteFile { + val remotePath = getRemotePathFromUrl(davResource.href, userId) + val remoteFile = RemoteFile(remotePath = remotePath, owner = userName) + val properties = getPropertiesEvenIfPostProcessing(davResource) + + for (property in properties) { + when (property) { + is CreationDate -> { + remoteFile.creationTimestamp = property.creationDate.toLong() + } + is GetContentLength -> { + remoteFile.length = property.contentLength + } + is GetContentType -> { + property.type?.let { remoteFile.mimeType = it } + } + is GetLastModified -> { + remoteFile.modifiedTimestamp = property.lastModified + } + is GetETag -> { + remoteFile.etag = property.eTag + } + is OCPermissions -> { + remoteFile.permissions = property.permission + } + is OCId -> { + remoteFile.remoteId = property.id + } + is OCSize -> { + remoteFile.size = property.size + } + is QuotaUsedBytes -> { + remoteFile.quotaUsedBytes = BigDecimal.valueOf(property.quotaUsedBytes) + } + is QuotaAvailableBytes -> { + remoteFile.quotaAvailableBytes = BigDecimal.valueOf(property.quotaAvailableBytes) + } + is OCPrivatelink -> { + remoteFile.privateLink = property.link + } + is OCShareTypes -> { + val list = property.shareTypes + for (i in list.indices) { + val shareType = fromValue(list[i].toInt()) + if (shareType == null) { + Timber.d("Illegal share type value: " + list[i]) + continue + } + if (shareType == ShareType.PUBLIC_LINK) { + remoteFile.sharedByLink = true + } else if (shareType == ShareType.USER || shareType == ShareType.FEDERATED || shareType == ShareType.GROUP) { + remoteFile.sharedWithSharee = true + } + } + } + } + } + return remoteFile + } + + /** + * Retrieves a relative path from a remote file url + * + * + * Example: url:port/remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt + * + * @param url remote file url + * @param userId file owner + * @return remote relative path of the file + */ + private fun getRemotePathFromUrl(url: HttpUrl, userId: String): String { + val davFilesPath = OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId + val absoluteDavPath = Uri.decode(url.encodedPath) + val pathToOc = absoluteDavPath.split(davFilesPath)[0] + return absoluteDavPath.replace(pathToOc + davFilesPath, "") + } + + private fun getPropertiesEvenIfPostProcessing(response: Response): List { + return if (response.isSuccess()) + response.propstat.filter { propStat -> propStat.isSuccessOrPostProcessing() }.map { it.properties }.flatten() + else + emptyList() + } + + private fun PropStat.isSuccessOrPostProcessing() = (status.code / 100 == 2 || status.code == HttpConstants.HTTP_TOO_EARLY) + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFileUtil.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFileUtil.kt deleted file mode 100644 index 66609bd1..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFileUtil.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ -package com.owncloud.android.lib.resources.files - -import android.net.Uri -import at.bitfire.dav4jvm.PropStat -import at.bitfire.dav4jvm.Property -import at.bitfire.dav4jvm.Response -import com.owncloud.android.lib.common.OwnCloudClient -import com.owncloud.android.lib.common.http.HttpConstants -import okhttp3.HttpUrl - -class RemoteFileUtil { - companion object { - /** - * Retrieves a relative path from a remote file url - * - * - * Example: url:port/remote.php/dav/files/username/Documents/text.txt => /Documents/text.txt - * - * @param url remote file url - * @param userId file owner - * @return remote relative path of the file - */ - fun getRemotePathFromUrl(url: HttpUrl, userId: String): String? { - val davFilesPath = OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId - val absoluteDavPath = Uri.decode(url.encodedPath) - val pathToOc = absoluteDavPath.split(davFilesPath)[0] - return absoluteDavPath.replace(pathToOc + davFilesPath, "") - } - - fun getProperties(response: Response): List { - return if (response.isSuccess()) - response.propstat.filter { propStat -> propStat.isSuccessOrPostProcessing() }.map { it.properties }.flatten() - else - emptyList() - } - - private fun PropStat.isSuccessOrPostProcessing() = (status.code / 100 == 2 || status.code == HttpConstants.HTTP_TOO_EARLY) - } -} From a02844d2c7c81ee5265eb556b5ea6b143e383508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Thu, 8 Oct 2020 09:52:19 +0200 Subject: [PATCH 16/62] Map size or length property depending on mimetype --- .../android/lib/resources/files/RemoteFile.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt index 4535a07b..ad81c287 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt @@ -79,12 +79,24 @@ data class RemoteFile( var sharedWithSharee: Boolean = false, ) : Parcelable { + // TODO: Quotas not used. Use or remove them. init { require(!(remotePath.isEmpty() || !remotePath.startsWith(File.separator))) { "Trying to create a OCFile with a non valid remote path: $remotePath" } } + /** + * Use this to find out if this file is a folder. + * + * @return true if it is a folder + */ + val isFolder + get() = mimeType == MIME_DIR || mimeType == MIME_DIR_UNIX + companion object { + const val MIME_DIR = "DIR" + const val MIME_DIR_UNIX = "httpd/unix-directory" + fun getRemoteFileFromDav(davResource: Response, userId: String, userName: String): RemoteFile { val remotePath = getRemotePathFromUrl(davResource.href, userId) val remoteFile = RemoteFile(remotePath = remotePath, owner = userName) From 405da9a729340df19e2ab7d29e7c9e46b03d8128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Thu, 22 Oct 2020 09:31:25 +0200 Subject: [PATCH 17/62] Apply CR changes --- .../android/lib/common/utils/AnyExt.kt | 29 +++++++++++++++++++ .../files/ReadRemoteFolderOperation.kt | 7 +++-- .../android/lib/resources/files/RemoteFile.kt | 9 ++++-- 3 files changed, 39 insertions(+), 6 deletions(-) create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/AnyExt.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/AnyExt.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/AnyExt.kt new file mode 100644 index 00000000..462ca975 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/utils/AnyExt.kt @@ -0,0 +1,29 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2020 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.common.utils + +fun Any.isOneOf(vararg values: Any): Boolean { + return this in values +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt index 82f14ab1..40747ace 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFolderOperation.kt @@ -26,7 +26,8 @@ package com.owncloud.android.lib.resources.files import at.bitfire.dav4jvm.PropertyRegistry import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.accounts.AccountUtils -import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.HttpConstants.HTTP_MULTI_STATUS +import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK import com.owncloud.android.lib.common.http.methods.webdav.DavConstants import com.owncloud.android.lib.common.http.methods.webdav.DavUtils import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod @@ -35,6 +36,7 @@ import com.owncloud.android.lib.common.network.WebdavUtils import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.common.utils.isOneOf import timber.log.Timber import java.net.URL @@ -105,6 +107,5 @@ class ReadRemoteFolderOperation( } } - private fun isSuccess(status: Int): Boolean = - status == HttpConstants.HTTP_MULTI_STATUS || status == HttpConstants.HTTP_OK + private fun isSuccess(status: Int): Boolean = status.isOneOf(HTTP_OK, HTTP_MULTI_STATUS) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt index ad81c287..715bdf1e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt @@ -46,6 +46,7 @@ import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTyp import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.lib.resources.shares.ShareType.Companion.fromValue import kotlinx.parcelize.Parcelize +import com.owncloud.android.lib.common.utils.isOneOf import okhttp3.HttpUrl import timber.log.Timber import java.io.File @@ -81,7 +82,9 @@ data class RemoteFile( // TODO: Quotas not used. Use or remove them. init { - require(!(remotePath.isEmpty() || !remotePath.startsWith(File.separator))) { "Trying to create a OCFile with a non valid remote path: $remotePath" } + require( + !(remotePath.isEmpty() || !remotePath.startsWith(File.separator)) + ) { "Trying to create a OCFile with a non valid remote path: $remotePath" } } /** @@ -90,7 +93,7 @@ data class RemoteFile( * @return true if it is a folder */ val isFolder - get() = mimeType == MIME_DIR || mimeType == MIME_DIR_UNIX + get() = mimeType.isOneOf(MIME_DIR, MIME_DIR_UNIX) companion object { @@ -170,7 +173,7 @@ data class RemoteFile( private fun getRemotePathFromUrl(url: HttpUrl, userId: String): String { val davFilesPath = OwnCloudClient.WEBDAV_FILES_PATH_4_0 + userId val absoluteDavPath = Uri.decode(url.encodedPath) - val pathToOc = absoluteDavPath.split(davFilesPath)[0] + val pathToOc = absoluteDavPath.split(davFilesPath).first() return absoluteDavPath.replace(pathToOc + davFilesPath, "") } From 389c0a0fc6e36cf76df8e982ebbd6f4ef29bacba Mon Sep 17 00:00:00 2001 From: agarcia Date: Wed, 12 Aug 2020 18:28:48 +0200 Subject: [PATCH 18/62] Migrate DownloadRemoteFileOperation to kotlin --- .../files/DownloadRemoteFileOperation.java | 221 ------------------ .../files/DownloadRemoteFileOperation.kt | 189 +++++++++++++++ 2 files changed, 189 insertions(+), 221 deletions(-) delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.java create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.java deleted file mode 100644 index 51ba6b3e..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.java +++ /dev/null @@ -1,221 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2016 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.resources.files; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.http.HttpConstants; -import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod; -import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.OperationCancelledException; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import timber.log.Timber; - -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.net.URL; -import java.util.Date; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Remote operation performing the download of a remote file in the ownCloud server. - * - * @author David A. Velasco - * @author masensio - */ - -public class DownloadRemoteFileOperation extends RemoteOperation { - - private static final int FORBIDDEN_ERROR = 403; - private static final int SERVICE_UNAVAILABLE_ERROR = 503; - private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false); - private Set mDataTransferListeners = new HashSet<>(); - private long mModificationTimestamp = 0; - private String mEtag = ""; - private GetMethod mGet; - - private String mRemotePath; - private String mLocalFolderPath; - - public DownloadRemoteFileOperation(String remotePath, String localFolderPath) { - mRemotePath = remotePath; - mLocalFolderPath = localFolderPath; - } - - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result; - - /// download will be performed to a temporal file, then moved to the final location - File tmpFile = new File(getTmpPath()); - - /// perform the download - try { - tmpFile.getParentFile().mkdirs(); - result = downloadFile(client, tmpFile); - Timber.i("Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage()); - - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - Timber.e(e, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + result.getLogMessage()); - } - - return result; - } - - private RemoteOperationResult downloadFile(OwnCloudClient client, File targetFile) throws - Exception { - - RemoteOperationResult result; - int status; - boolean savedFile = false; - mGet = new GetMethod(new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath))); - Iterator it; - - FileOutputStream fos = null; - BufferedInputStream bis = null; - try { - status = client.executeHttpMethod(mGet); - if (isSuccess(status)) { - targetFile.createNewFile(); - bis = new BufferedInputStream(mGet.getResponseBodyAsStream()); - fos = new FileOutputStream(targetFile); - long transferred = 0; - - String contentLength = mGet.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER); - long totalToTransfer = - (contentLength != null - && contentLength.length() > 0) - ? Long.parseLong(contentLength) - : 0; - - byte[] bytes = new byte[4096]; - int readResult; - while ((readResult = bis.read(bytes)) != -1) { - synchronized (mCancellationRequested) { - if (mCancellationRequested.get()) { - mGet.abort(); - throw new OperationCancelledException(); - } - } - fos.write(bytes, 0, readResult); - transferred += readResult; - synchronized (mDataTransferListeners) { - it = mDataTransferListeners.iterator(); - while (it.hasNext()) { - it.next().onTransferProgress(readResult, transferred, totalToTransfer, - targetFile.getName()); - } - } - } - if (transferred == totalToTransfer) { // Check if the file is completed - savedFile = true; - final String modificationTime = - mGet.getResponseHeaders().get("Last-Modified") != null - ? mGet.getResponseHeaders().get("Last-Modified") - : mGet.getResponseHeader("last-modified"); - - if (modificationTime != null) { - final Date d = WebdavUtils.parseResponseDate(modificationTime); - mModificationTimestamp = (d != null) ? d.getTime() : 0; - } else { - Timber.e("Could not read modification time from response downloading %s", mRemotePath); - } - - mEtag = WebdavUtils.getEtagFromResponse(mGet); - - // Get rid of extra quotas - mEtag = mEtag.replace("\"", ""); - - if (mEtag.length() == 0) { - Timber.e("Could not read eTag from response downloading %s", mRemotePath); - } - - } else { - Timber.e("Content-Length not equal to transferred bytes."); - Timber.d("totalToTransfer = %d, transferred = %d", totalToTransfer, transferred); - client.exhaustResponse(mGet.getResponseBodyAsStream()); - // TODO some kind of error control! - } - - } else if (status != FORBIDDEN_ERROR && status != SERVICE_UNAVAILABLE_ERROR) { - client.exhaustResponse(mGet.getResponseBodyAsStream()); - - } // else, body read by RemoteOperationResult constructor - - result = isSuccess(status) - ? new RemoteOperationResult<>(RemoteOperationResult.ResultCode.OK) - : new RemoteOperationResult<>(mGet); - } finally { - if (fos != null) { - fos.close(); - } - if (bis != null) { - bis.close(); - } - if (!savedFile && targetFile.exists()) { - targetFile.delete(); - } - } - return result; - } - - private boolean isSuccess(int status) { - return (status == HttpConstants.HTTP_OK); - } - - private String getTmpPath() { - return mLocalFolderPath + mRemotePath; - } - - public void addDatatransferProgressListener(OnDatatransferProgressListener listener) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.add(listener); - } - } - - public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.remove(listener); - } - } - - public void cancel() { - mCancellationRequested.set(true); // atomic set; there is no need of synchronizing it - } - - public long getModificationTimestamp() { - return mModificationTimestamp; - } - - public String getEtag() { - return mEtag; - } -} \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt new file mode 100644 index 00000000..7bf535b5 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt @@ -0,0 +1,189 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2020 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.resources.files + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.nonwebdav.GetMethod +import com.owncloud.android.lib.common.network.OnDatatransferProgressListener +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.OperationCancelledException +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import timber.log.Timber +import java.io.BufferedInputStream +import java.io.File +import java.io.FileOutputStream +import java.net.URL +import java.util.HashSet +import java.util.concurrent.atomic.AtomicBoolean + +/** + * Remote operation performing the download of a remote file in the ownCloud server. + * + * @author David A. Velasco + * @author masensio + */ +class DownloadRemoteFileOperation( + private val remotePath: String, + localFolderPath: String +) : RemoteOperation() { + + private val mCancellationRequested = AtomicBoolean(false) + private val mDataTransferListeners: MutableSet = HashSet() + var modificationTimestamp: Long = 0 + private set + + var etag: String = "" + private set + + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + + /// download will be performed to a temporal file, then moved to the final location + val tmpFile = File(tmpPath) + + /// perform the download + try { + tmpFile.mkdirs() + result = downloadFile(client, tmpFile) + Timber.i("Download of $remotePath to $tmpPath: ${result.logMessage}") + } catch (e: Exception) { + result = RemoteOperationResult(e) + Timber.e(e, "Download of $remotePath to $tmpPath: ${result.logMessage}") + } + return result + } + + @Throws(Exception::class) + private fun downloadFile(client: OwnCloudClient, targetFile: File): RemoteOperationResult { + val result: RemoteOperationResult + var it: Iterator + var fos: FileOutputStream? = null + var bis: BufferedInputStream? = null + var savedFile = false + + val getMethod = GetMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath))) + + try { + val status = client.executeHttpMethod(getMethod) + + if (isSuccess(status)) { + targetFile.createNewFile() + bis = BufferedInputStream(getMethod.getResponseBodyAsStream()) + fos = FileOutputStream(targetFile) + var transferred: Long = 0 + val contentLength = getMethod.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER) + val totalToTransfer = + if (contentLength != null && contentLength.isNotEmpty()) contentLength.toLong() else 0 + val bytes = ByteArray(4096) + var readResult: Int + while (bis.read(bytes).also { readResult = it } != -1) { + synchronized(mCancellationRequested) { + if (mCancellationRequested.get()) { + getMethod.abort() + throw OperationCancelledException() + } + } + fos.write(bytes, 0, readResult) + transferred += readResult.toLong() + synchronized(mDataTransferListeners) { + it = mDataTransferListeners.iterator() + while (it.hasNext()) { + it.next().onTransferProgress( + readResult.toLong(), transferred, totalToTransfer, + targetFile.name + ) + } + } + } + + if (transferred == totalToTransfer) { // Check if the file is completed + savedFile = true + val modificationTime = + getMethod.getResponseHeaders()?.get("Last-Modified") + ?: getMethod.getResponseHeader("last-modified") + + if (modificationTime != null) { + val modificationDate = WebdavUtils.parseResponseDate(modificationTime) + modificationTimestamp = modificationDate?.time ?: 0 + } else { + Timber.e("Could not read modification time from response downloading %s", remotePath) + } + etag = WebdavUtils.getEtagFromResponse(getMethod) + + // Get rid of extra quotas + etag = etag.replace("\"", "") + if (etag.isEmpty()) { + Timber.e("Could not read eTag from response downloading %s", remotePath) + } + } else { + Timber.e("Content-Length not equal to transferred bytes.") + Timber.d("totalToTransfer = $totalToTransfer, transferred = $transferred") + client.exhaustResponse(getMethod.getResponseBodyAsStream()) + // TODO some kind of error control! + } + + } else if (status != FORBIDDEN_ERROR && status != SERVICE_UNAVAILABLE_ERROR) { + client.exhaustResponse(getMethod.getResponseBodyAsStream()) + } // else, body read by RemoteOperationResult constructor + + result = + if (isSuccess(status)) { + RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + } else { + RemoteOperationResult(getMethod) + } + } finally { + fos?.close() + bis?.close() + if (!savedFile && targetFile.exists()) { + targetFile.delete() + } + } + return result + } + + private fun isSuccess(status: Int) = status == HttpConstants.HTTP_OK + + private val tmpPath: String = localFolderPath + remotePath + + fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) { + synchronized(mDataTransferListeners) { mDataTransferListeners.add(listener) } + } + + fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener?) { + synchronized(mDataTransferListeners) { mDataTransferListeners.remove(listener) } + } + + fun cancel() { + mCancellationRequested.set(true) // atomic set; there is no need of synchronizing it + } + + companion object { + private const val FORBIDDEN_ERROR = 403 + private const val SERVICE_UNAVAILABLE_ERROR = 503 + } + +} From 56248af6ec33a2806cee052c47bc68c8c75893d4 Mon Sep 17 00:00:00 2001 From: agarcia Date: Thu, 13 Aug 2020 12:57:32 +0200 Subject: [PATCH 19/62] Add download file to file service --- .../files/DownloadRemoteFileOperation.kt | 15 +++++---------- .../lib/resources/files/services/FileService.kt | 5 +++++ .../services/implementation/OCFileService.kt | 11 +++++++++++ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt index 7bf535b5..c00ea76f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt @@ -61,10 +61,10 @@ class DownloadRemoteFileOperation( override fun run(client: OwnCloudClient): RemoteOperationResult { var result: RemoteOperationResult - /// download will be performed to a temporal file, then moved to the final location + // download will be performed to a temporal file, then moved to the final location val tmpFile = File(tmpPath) - /// perform the download + // perform the download try { tmpFile.mkdirs() result = downloadFile(client, tmpFile) @@ -145,15 +145,15 @@ class DownloadRemoteFileOperation( // TODO some kind of error control! } - } else if (status != FORBIDDEN_ERROR && status != SERVICE_UNAVAILABLE_ERROR) { + } else if (status != HttpConstants.HTTP_FORBIDDEN && status != HttpConstants.HTTP_SERVICE_UNAVAILABLE) { client.exhaustResponse(getMethod.getResponseBodyAsStream()) } // else, body read by RemoteOperationResult constructor result = if (isSuccess(status)) { - RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + RemoteOperationResult(RemoteOperationResult.ResultCode.OK) } else { - RemoteOperationResult(getMethod) + RemoteOperationResult(getMethod) } } finally { fos?.close() @@ -181,9 +181,4 @@ class DownloadRemoteFileOperation( mCancellationRequested.set(true) // atomic set; there is no need of synchronizing it } - companion object { - private const val FORBIDDEN_ERROR = 403 - private const val SERVICE_UNAVAILABLE_ERROR = 503 - } - } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index 904496c0..ec0b7421 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -41,6 +41,11 @@ interface FileService : Service { isChunkFolder: Boolean = false ): RemoteOperationResult + fun downloadFile( + remotePath: String, + localTempPath: String + ): RemoteOperationResult + fun refreshFolder( remotePath: String ): RemoteOperationResult> diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index a21c6fe0..d49533bb 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -27,12 +27,14 @@ import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation +import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation import com.owncloud.android.lib.resources.files.RemoteFile import com.owncloud.android.lib.resources.files.services.FileService class OCFileService(override val client: OwnCloudClient) : FileService { + override fun checkPathExistence( path: String, isUserLogged: Boolean @@ -56,6 +58,15 @@ class OCFileService(override val client: OwnCloudClient) : FileService { isChunksFolder = isChunkFolder ).execute(client) + override fun downloadFile( + remotePath: String, + localTempPath: String + ): RemoteOperationResult = + DownloadRemoteFileOperation( + remotePath = remotePath, + localFolderPath = localTempPath + ).execute(client) + override fun refreshFolder(remotePath: String): RemoteOperationResult> = ReadRemoteFolderOperation( remotePath = remotePath From ec71fa6c232c6a4ae353f9ec534cca0373c6266a Mon Sep 17 00:00:00 2001 From: agarcia Date: Thu, 3 Sep 2020 11:18:53 +0200 Subject: [PATCH 20/62] Create parent folder when downloading a file if possible --- .../android/lib/resources/files/DownloadRemoteFileOperation.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt index c00ea76f..fddeb692 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt @@ -66,7 +66,7 @@ class DownloadRemoteFileOperation( // perform the download try { - tmpFile.mkdirs() + tmpFile.parentFile?.mkdirs() result = downloadFile(client, tmpFile) Timber.i("Download of $remotePath to $tmpPath: ${result.logMessage}") } catch (e: Exception) { From 3a996ef58324e5e89d34f81516ecb7db2f70c75d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 17 Nov 2020 16:15:40 +0100 Subject: [PATCH 21/62] Download file operation will return unit instead of Any --- .../lib/resources/files/DownloadRemoteFileOperation.kt | 10 +++++----- .../lib/resources/files/services/FileService.kt | 2 +- .../files/services/implementation/OCFileService.kt | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt index fddeb692..793cca5b 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt @@ -48,7 +48,7 @@ import java.util.concurrent.atomic.AtomicBoolean class DownloadRemoteFileOperation( private val remotePath: String, localFolderPath: String -) : RemoteOperation() { +) : RemoteOperation() { private val mCancellationRequested = AtomicBoolean(false) private val mDataTransferListeners: MutableSet = HashSet() @@ -58,8 +58,8 @@ class DownloadRemoteFileOperation( var etag: String = "" private set - override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult // download will be performed to a temporal file, then moved to the final location val tmpFile = File(tmpPath) @@ -77,8 +77,8 @@ class DownloadRemoteFileOperation( } @Throws(Exception::class) - private fun downloadFile(client: OwnCloudClient, targetFile: File): RemoteOperationResult { - val result: RemoteOperationResult + private fun downloadFile(client: OwnCloudClient, targetFile: File): RemoteOperationResult { + val result: RemoteOperationResult var it: Iterator var fos: FileOutputStream? = null var bis: BufferedInputStream? = null diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index ec0b7421..0a43d0f8 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -44,7 +44,7 @@ interface FileService : Service { fun downloadFile( remotePath: String, localTempPath: String - ): RemoteOperationResult + ): RemoteOperationResult fun refreshFolder( remotePath: String diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index d49533bb..55892f4a 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -61,7 +61,7 @@ class OCFileService(override val client: OwnCloudClient) : FileService { override fun downloadFile( remotePath: String, localTempPath: String - ): RemoteOperationResult = + ): RemoteOperationResult = DownloadRemoteFileOperation( remotePath = remotePath, localFolderPath = localTempPath From 039245742c2f791eb98a9af76d6d4a6433daa72f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Thu, 22 Apr 2021 09:36:34 +0200 Subject: [PATCH 22/62] Apply code review suggestions --- .../files/DownloadRemoteFileOperation.kt | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt index 793cca5b..80b9adc7 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/DownloadRemoteFileOperation.kt @@ -50,8 +50,9 @@ class DownloadRemoteFileOperation( localFolderPath: String ) : RemoteOperation() { - private val mCancellationRequested = AtomicBoolean(false) - private val mDataTransferListeners: MutableSet = HashSet() + private val cancellationRequested = AtomicBoolean(false) + private val dataTransferListeners: MutableSet = HashSet() + var modificationTimestamp: Long = 0 private set @@ -59,21 +60,20 @@ class DownloadRemoteFileOperation( private set override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult - // download will be performed to a temporal file, then moved to the final location val tmpFile = File(tmpPath) // perform the download - try { + return try { tmpFile.parentFile?.mkdirs() - result = downloadFile(client, tmpFile) - Timber.i("Download of $remotePath to $tmpPath: ${result.logMessage}") + downloadFile(client, tmpFile).also { result -> + Timber.i("Download of $remotePath to $tmpPath: ${result.logMessage}") + } } catch (e: Exception) { - result = RemoteOperationResult(e) - Timber.e(e, "Download of $remotePath to $tmpPath: ${result.logMessage}") + RemoteOperationResult(e).also { result -> + Timber.e(e, "Download of $remotePath to $tmpPath: ${result.logMessage}") + } } - return result } @Throws(Exception::class) @@ -95,26 +95,27 @@ class DownloadRemoteFileOperation( fos = FileOutputStream(targetFile) var transferred: Long = 0 val contentLength = getMethod.getResponseHeader(HttpConstants.CONTENT_LENGTH_HEADER) - val totalToTransfer = - if (contentLength != null && contentLength.isNotEmpty()) contentLength.toLong() else 0 + val totalToTransfer = if (!contentLength.isNullOrEmpty()) { + contentLength.toLong() + } else { + 0 + } val bytes = ByteArray(4096) var readResult: Int while (bis.read(bytes).also { readResult = it } != -1) { - synchronized(mCancellationRequested) { - if (mCancellationRequested.get()) { + synchronized(cancellationRequested) { + if (cancellationRequested.get()) { getMethod.abort() throw OperationCancelledException() } } fos.write(bytes, 0, readResult) transferred += readResult.toLong() - synchronized(mDataTransferListeners) { - it = mDataTransferListeners.iterator() + synchronized(dataTransferListeners) { + it = dataTransferListeners.iterator() while (it.hasNext()) { - it.next().onTransferProgress( - readResult.toLong(), transferred, totalToTransfer, - targetFile.name - ) + it.next() + .onTransferProgress(readResult.toLong(), transferred, totalToTransfer, targetFile.name) } } } @@ -170,15 +171,14 @@ class DownloadRemoteFileOperation( private val tmpPath: String = localFolderPath + remotePath fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) { - synchronized(mDataTransferListeners) { mDataTransferListeners.add(listener) } + synchronized(dataTransferListeners) { dataTransferListeners.add(listener) } } fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener?) { - synchronized(mDataTransferListeners) { mDataTransferListeners.remove(listener) } + synchronized(dataTransferListeners) { dataTransferListeners.remove(listener) } } fun cancel() { - mCancellationRequested.set(true) // atomic set; there is no need of synchronizing it + cancellationRequested.set(true) // atomic set; there is no need of synchronizing it } - } From 03303ac4f2f182d8832885e8c37627cf77091534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Wed, 28 Apr 2021 17:13:03 +0200 Subject: [PATCH 23/62] Replace kotlin android extensions with kotlin-parcelize --- owncloudComLibrary/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudComLibrary/build.gradle b/owncloudComLibrary/build.gradle index 8153da36..34391930 100644 --- a/owncloudComLibrary/build.gradle +++ b/owncloudComLibrary/build.gradle @@ -1,7 +1,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-parcelize' dependencies { api 'com.squareup.okhttp3:okhttp:4.6.0' From f706595a9a1e0117c00e5a7c666228d94d1b8144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Thu, 29 Apr 2021 08:58:53 +0200 Subject: [PATCH 24/62] Converting RemoveRemoteFileOperation from java to kotlin. Intermediate step to preserve git history. --- ...emoveRemoteFileOperation.java => RemoveRemoteFileOperation.kt} | 0 ...sFolderOperation.java => RemoveRemoteChunksFolderOperation.kt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/{RemoveRemoteFileOperation.java => RemoveRemoteFileOperation.kt} (100%) rename owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/{RemoveRemoteChunksFolderOperation.java => RemoveRemoteChunksFolderOperation.kt} (100%) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt similarity index 100% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.java rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.kt similarity index 100% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.java rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.kt From 62dbdb102160a8570d2b23c4c4f01a1c086950e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Thu, 29 Apr 2021 09:17:26 +0200 Subject: [PATCH 25/62] Converting RemoveRemoteFileOperation from java to kotlin. Final step. --- .../files/RemoveRemoteFileOperation.kt | 94 ++++++++----------- .../RemoveRemoteChunksFolderOperation.kt | 22 ++--- 2 files changed, 44 insertions(+), 72 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt index 4789d015..23b16e7e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt @@ -24,19 +24,18 @@ package com.owncloud.android.lib.resources.files; -import android.net.Uri; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.http.HttpConstants; -import com.owncloud.android.lib.common.http.methods.nonwebdav.DeleteMethod; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import timber.log.Timber; - -import java.net.URL; - -import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK; +import android.net.Uri +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants.HTTP_NO_CONTENT +import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK +import com.owncloud.android.lib.common.http.methods.nonwebdav.DeleteMethod +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.common.utils.isOneOf +import timber.log.Timber +import java.net.URL /** * Remote operation performing the removal of a remote file or folder in the ownCloud server. @@ -44,53 +43,34 @@ import static com.owncloud.android.lib.common.operations.RemoteOperationResult.R * @author David A. Velasco * @author masensio * @author David González Verdugo + * @author Abel García de Prada */ -public class RemoveRemoteFileOperation extends RemoteOperation { - private String mRemotePath; - - protected boolean removeChunksFolder = false; - - /** - * Constructor - * - * @param remotePath RemotePath of the remote file or folder to remove from the server - */ - public RemoveRemoteFileOperation(String remotePath) { - mRemotePath = remotePath; - } - - /** - * Performs the rename operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result; +open class RemoveRemoteFileOperation( + private val mRemotePath: String +) : RemoteOperation() { + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult try { - Uri srcWebDavUri = removeChunksFolder ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri(); - - DeleteMethod deleteMethod = new DeleteMethod( - new URL(srcWebDavUri + WebdavUtils.encodePath(mRemotePath))); - - int status = client.executeHttpMethod(deleteMethod); - - result = isSuccess(status) ? - new RemoteOperationResult<>(OK) : - new RemoteOperationResult<>(deleteMethod); - - Timber.i("Remove " + mRemotePath + ": " + result.getLogMessage()); - - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - Timber.e(e, "Remove " + mRemotePath + ": " + result.getLogMessage()); + val srcWebDavUri = getSrcWebDavUriForClient(client) + val deleteMethod = DeleteMethod( + URL(srcWebDavUri.toString() + WebdavUtils.encodePath(mRemotePath)) + ) + val status = client.executeHttpMethod(deleteMethod) + result = if (isSuccess(status)) RemoteOperationResult(ResultCode.OK) else RemoteOperationResult(deleteMethod) + Timber.i("Remove $mRemotePath: ${result.logMessage}") + } catch (e: Exception) { + result = RemoteOperationResult(e) + Timber.e(e, "Remove $mRemotePath: ${result.logMessage}") } - - return result; + return result } - private boolean isSuccess(int status) { - return status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_NO_CONTENT; - } -} \ No newline at end of file + /** + * For standard removals, we will use [OwnCloudClient.getUserFilesWebDavUri]. + * In case we need a different source Uri, override this method. + */ + open fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.userFilesWebDavUri + + private fun isSuccess(status: Int) = status.isOneOf(HTTP_OK, HTTP_NO_CONTENT) +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.kt index 16d2b8bd..150a4dd1 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/RemoveRemoteChunksFolderOperation.kt @@ -22,20 +22,12 @@ * THE SOFTWARE. * */ +package com.owncloud.android.lib.resources.files.chunks -package com.owncloud.android.lib.resources.files.chunks; +import android.net.Uri +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation -import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation; - -public class RemoveRemoteChunksFolderOperation extends RemoveRemoteFileOperation { - - /** - * Constructor - * - * @param remotePath RemotePath of the remote file or folder to remove from the server - */ - public RemoveRemoteChunksFolderOperation(String remotePath) { - super(remotePath); - removeChunksFolder = true; - } -} \ No newline at end of file +class RemoveRemoteChunksFolderOperation(remotePath: String) : RemoveRemoteFileOperation(remotePath) { + override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri +} From 86f16c3460dfde380d45b8e398b950645ed130d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Thu, 29 Apr 2021 09:32:19 +0200 Subject: [PATCH 26/62] Add removeFile to FileService --- .../files/RemoveRemoteFileOperation.kt | 10 +++--- .../resources/files/services/ChunkService.kt | 34 ++++++++++++++++++ .../resources/files/services/FileService.kt | 4 +++ .../services/implementation/OCChunkService.kt | 36 +++++++++++++++++++ .../services/implementation/OCFileService.kt | 4 +++ 5 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt index 23b16e7e..fa9702ad 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt @@ -22,7 +22,7 @@ * */ -package com.owncloud.android.lib.resources.files; +package com.owncloud.android.lib.resources.files import android.net.Uri import com.owncloud.android.lib.common.OwnCloudClient @@ -46,7 +46,7 @@ import java.net.URL * @author Abel García de Prada */ open class RemoveRemoteFileOperation( - private val mRemotePath: String + private val remotePath: String ) : RemoteOperation() { override fun run(client: OwnCloudClient): RemoteOperationResult { @@ -54,14 +54,14 @@ open class RemoveRemoteFileOperation( try { val srcWebDavUri = getSrcWebDavUriForClient(client) val deleteMethod = DeleteMethod( - URL(srcWebDavUri.toString() + WebdavUtils.encodePath(mRemotePath)) + URL(srcWebDavUri.toString() + WebdavUtils.encodePath(remotePath)) ) val status = client.executeHttpMethod(deleteMethod) result = if (isSuccess(status)) RemoteOperationResult(ResultCode.OK) else RemoteOperationResult(deleteMethod) - Timber.i("Remove $mRemotePath: ${result.logMessage}") + Timber.i("Remove $remotePath: ${result.logMessage}") } catch (e: Exception) { result = RemoteOperationResult(e) - Timber.e(e, "Remove $mRemotePath: ${result.logMessage}") + Timber.e(e, "Remove $remotePath: ${result.logMessage}") } return result } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt new file mode 100644 index 00000000..011dbe35 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt @@ -0,0 +1,34 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2021 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.resources.files.services + +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.Service + +interface ChunkService : Service { + fun removeFile( + remotePath: String + ): RemoteOperationResult +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index 0a43d0f8..cf27ab1e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -49,4 +49,8 @@ interface FileService : Service { fun refreshFolder( remotePath: String ): RemoteOperationResult> + + fun removeFile( + remotePath: String + ): RemoteOperationResult } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt new file mode 100644 index 00000000..d7ca75e8 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt @@ -0,0 +1,36 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2021 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.resources.files.services.implementation + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.files.chunks.RemoveRemoteChunksFolderOperation +import com.owncloud.android.lib.resources.files.services.ChunkService + +class OCChunkService(override val client: OwnCloudClient) : ChunkService { + + override fun removeFile(remotePath: String): RemoteOperationResult = + RemoveRemoteChunksFolderOperation(remotePath = remotePath).execute(client) +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index 55892f4a..46d02a27 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -31,6 +31,7 @@ import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation import com.owncloud.android.lib.resources.files.RemoteFile +import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation import com.owncloud.android.lib.resources.files.services.FileService class OCFileService(override val client: OwnCloudClient) : FileService { @@ -71,4 +72,7 @@ class OCFileService(override val client: OwnCloudClient) : FileService { ReadRemoteFolderOperation( remotePath = remotePath ).execute(client) + + override fun removeFile(remotePath: String): RemoteOperationResult = + RemoveRemoteFileOperation(remotePath = remotePath).execute(client) } From d99444e279bd9dd16ca19ce328a7100a2b6f5814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Fri, 7 May 2021 10:00:31 +0200 Subject: [PATCH 27/62] Apply code review suggestions --- .../lib/resources/files/RemoveRemoteFileOperation.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt index fa9702ad..a451256a 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoveRemoteFileOperation.kt @@ -57,7 +57,12 @@ open class RemoveRemoteFileOperation( URL(srcWebDavUri.toString() + WebdavUtils.encodePath(remotePath)) ) val status = client.executeHttpMethod(deleteMethod) - result = if (isSuccess(status)) RemoteOperationResult(ResultCode.OK) else RemoteOperationResult(deleteMethod) + + result = if (isSuccess(status)) { + RemoteOperationResult(ResultCode.OK) + } else { + RemoteOperationResult(deleteMethod) + } Timber.i("Remove $remotePath: ${result.logMessage}") } catch (e: Exception) { result = RemoteOperationResult(e) From d736c7cdbf1c0e3bba5b3848f30fc7131a4d1fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 11 May 2021 16:47:08 +0200 Subject: [PATCH 28/62] Migrate MoveRemoteFileOperation to kotlin. First step to keep git history --- .../{MoveRemoteFileOperation.java => MoveRemoteFileOperation.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/{MoveRemoteFileOperation.java => MoveRemoteFileOperation.kt} (100%) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt similarity index 100% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.java rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt From 5beb30e07a3b1fd3da55bb16c2d5d517e239e253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 11 May 2021 17:08:15 +0200 Subject: [PATCH 29/62] Migrate MoveRemoteFileOperation to kotlin. Second step to keep git history --- .../files/MoveRemoteFileOperation.kt | 149 ++++++++---------- ....java => MoveRemoteChunksFileOperation.kt} | 48 +++--- 2 files changed, 95 insertions(+), 102 deletions(-) rename owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/{MoveRemoteChunksFileOperation.java => MoveRemoteChunksFileOperation.kt} (54%) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt index 46181e8f..373574d1 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. + * Copyright (C) 2021 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,126 +21,109 @@ * THE SOFTWARE. * */ +package com.owncloud.android.lib.resources.files -package com.owncloud.android.lib.resources.files; - -import android.net.Uri; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.http.HttpConstants; -import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; -import timber.log.Timber; - -import java.net.URL; -import java.util.concurrent.TimeUnit; +import android.net.Uri +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.common.utils.isOneOf +import timber.log.Timber +import java.net.URL +import java.util.concurrent.TimeUnit /** * Remote operation moving a remote file or folder in the ownCloud server to a different folder * in the same account. - *

+ * * Allows renaming the moving file/folder at the same time. * * @author David A. Velasco * @author David González Verdugo + * @author Abel García de Prada */ -public class MoveRemoteFileOperation extends RemoteOperation { - - private static final int MOVE_READ_TIMEOUT = 600000; - private static final int MOVE_CONNECTION_TIMEOUT = 5000; - - private String mSrcRemotePath; - private String mTargetRemotePath; - private boolean mOverwrite; - - protected boolean moveChunkedFile = false; - protected String mFileLastModifTimestamp; - protected long mFileLength; - - /** - * Constructor. - *

- * TODO Paths should finish in "/" in the case of folders. ? - * - * @param srcRemotePath Remote path of the file/folder to move. - * @param targetRemotePath Remote path desired for the file/folder after moving it. - */ - public MoveRemoteFileOperation(String srcRemotePath, - String targetRemotePath, - boolean overwrite) { - - mSrcRemotePath = srcRemotePath; - mTargetRemotePath = targetRemotePath; - mOverwrite = overwrite; - } +open class MoveRemoteFileOperation( + private val sourceRemotePath: String, + private val targetRemotePath: String, + private val forceOverwrite: Boolean +) : RemoteOperation() { /** * Performs the rename operation. * * @param client Client object to communicate with the remote ownCloud server. */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - if (mTargetRemotePath.equals(mSrcRemotePath)) { + override fun run(client: OwnCloudClient): RemoteOperationResult { + if (targetRemotePath == sourceRemotePath) { // nothing to do! - return new RemoteOperationResult<>(ResultCode.OK); + return RemoteOperationResult(ResultCode.OK) } - if (mTargetRemotePath.startsWith(mSrcRemotePath)) { - return new RemoteOperationResult<>(ResultCode.INVALID_MOVE_INTO_DESCENDANT); + if (targetRemotePath.startsWith(sourceRemotePath)) { + return RemoteOperationResult(ResultCode.INVALID_MOVE_INTO_DESCENDANT) } /// perform remote operation - RemoteOperationResult result; + var result: RemoteOperationResult try { // After finishing a chunked upload, we have to move the resulting file from uploads folder to files one, // so this uri has to be customizable - Uri srcWebDavUri = moveChunkedFile ? client.getUploadsWebDavUri() : client.getUserFilesWebDavUri(); - - final MoveMethod move = new MoveMethod( - new URL(srcWebDavUri + WebdavUtils.encodePath(mSrcRemotePath)), - client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath), - mOverwrite); - - if (moveChunkedFile) { - move.addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, mFileLastModifTimestamp); - move.addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, String.valueOf(mFileLength)); + val srcWebDavUri = getSrcWebDavUriForClient(client) + val moveMethod = MoveMethod( + url = URL(srcWebDavUri.toString() + WebdavUtils.encodePath(sourceRemotePath)), + destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath), + forceOverride = forceOverwrite + ).apply { + addRequestHeaders(this) + setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS) + setConnectionTimeout(MOVE_CONNECTION_TIMEOUT, TimeUnit.SECONDS) } - move.setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS); - move.setConnectionTimeout(MOVE_CONNECTION_TIMEOUT, TimeUnit.SECONDS); + val status = client.executeHttpMethod(moveMethod) - final int status = client.executeHttpMethod(move); - /// process response if (isSuccess(status)) { - result = new RemoteOperationResult<>(ResultCode.OK); - } else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !mOverwrite) { - - result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE); - client.exhaustResponse(move.getResponseBodyAsStream()); + result = RemoteOperationResult(ResultCode.OK) + } else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !forceOverwrite) { + result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE) + client.exhaustResponse(moveMethod.getResponseBodyAsStream()) /// for other errors that could be explicitly handled, check first: /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4 - } else { - result = new RemoteOperationResult<>(move); - client.exhaustResponse(move.getResponseBodyAsStream()); + result = RemoteOperationResult(moveMethod) + client.exhaustResponse(moveMethod.getResponseBodyAsStream()) } - Timber.i("Move " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage()); + Timber.i("Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}") + } catch (e: Exception) { + result = RemoteOperationResult(e) + Timber.e(e, "Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}") - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - Timber.e(e, "Move " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage()); } - - return result; + return result } - protected boolean isSuccess(int status) { - return status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT; + /** + * For standard moves, we will use [OwnCloudClient.getUserFilesWebDavUri]. + * In case we need a different source Uri, override this method. + */ + open fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.userFilesWebDavUri + + /** + * For standard moves, we won't need any special headers. + * In case new headers are needed, override this method + */ + open fun addRequestHeaders(moveMethod: MoveMethod) { + } + + protected fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT) + + companion object { + private const val MOVE_READ_TIMEOUT = 10L + private const val MOVE_CONNECTION_TIMEOUT = 6L } } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/MoveRemoteChunksFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/MoveRemoteChunksFileOperation.kt similarity index 54% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/MoveRemoteChunksFileOperation.java rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/MoveRemoteChunksFileOperation.kt index 767a20b8..fc8a0a96 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/MoveRemoteChunksFileOperation.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/MoveRemoteChunksFileOperation.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. + * Copyright (C) 2021 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,30 +21,40 @@ * THE SOFTWARE. * */ +package com.owncloud.android.lib.resources.files.chunks -package com.owncloud.android.lib.resources.files.chunks; - -import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation; +import android.net.Uri +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod +import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation /** * Remote operation to move the file built from chunks after uploading it * * @author David González Verdugo + * @author Abel García de Prada */ -public class MoveRemoteChunksFileOperation extends MoveRemoteFileOperation { +class MoveRemoteChunksFileOperation( + sourceRemotePath: String, + targetRemotePath: String, + overwrite: Boolean, + private val fileLastModificationTimestamp: String, + private val fileLength: Long +) : MoveRemoteFileOperation( + sourceRemotePath = sourceRemotePath, + targetRemotePath = targetRemotePath, + forceOverwrite = overwrite +) { - /** - * Constructor. - * - * @param srcRemotePath Remote path of the file/folder to move. - * @param targetRemotePath Remove path desired for the file/folder after moving it. - * @param overwrite - */ - public MoveRemoteChunksFileOperation(String srcRemotePath, String targetRemotePath, boolean overwrite, - String fileLastModifTimestamp, long fileLength) { - super(srcRemotePath, targetRemotePath, overwrite); - moveChunkedFile = true; - mFileLastModifTimestamp = fileLastModifTimestamp; - mFileLength = fileLength; + override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri + + override fun addRequestHeaders(moveMethod: MoveMethod) { + super.addRequestHeaders(moveMethod) + + moveMethod.apply { + addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, fileLastModificationTimestamp) + addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, fileLength.toString()) + } } -} \ No newline at end of file +} From b3cccfa007ccf00b32c10cac9adb1c73528fd089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 11 May 2021 17:34:34 +0200 Subject: [PATCH 30/62] Add move operation to chunk service --- .../lib/resources/files/services/ChunkService.kt | 8 ++++++++ .../services/implementation/OCChunkService.kt | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt index 011dbe35..895b3c14 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt @@ -31,4 +31,12 @@ interface ChunkService : Service { fun removeFile( remotePath: String ): RemoteOperationResult + + fun moveFile( + sourceRemotePath: String, + targetRemotePath: String, + overwrite: Boolean, + fileLastModificationTimestamp: String, + fileLength: Long + ): RemoteOperationResult } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt index d7ca75e8..78068410 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt @@ -26,6 +26,7 @@ package com.owncloud.android.lib.resources.files.services.implementation import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.files.chunks.MoveRemoteChunksFileOperation import com.owncloud.android.lib.resources.files.chunks.RemoveRemoteChunksFolderOperation import com.owncloud.android.lib.resources.files.services.ChunkService @@ -33,4 +34,19 @@ class OCChunkService(override val client: OwnCloudClient) : ChunkService { override fun removeFile(remotePath: String): RemoteOperationResult = RemoveRemoteChunksFolderOperation(remotePath = remotePath).execute(client) + + override fun moveFile( + sourceRemotePath: String, + targetRemotePath: String, + overwrite: Boolean, + fileLastModificationTimestamp: String, + fileLength: Long + ): RemoteOperationResult = + MoveRemoteChunksFileOperation( + sourceRemotePath = sourceRemotePath, + targetRemotePath = targetRemotePath, + overwrite = overwrite, + fileLastModificationTimestamp = fileLastModificationTimestamp, + fileLength = fileLength + ).execute(client) } From 7c825b2dc30faf75f740ef47fe4e53667f7a6122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 11 May 2021 17:37:25 +0200 Subject: [PATCH 31/62] Add move operation to file service --- .../lib/resources/files/services/FileService.kt | 6 ++++++ .../files/services/implementation/OCFileService.kt | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index cf27ab1e..cc88667c 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -46,6 +46,12 @@ interface FileService : Service { localTempPath: String ): RemoteOperationResult + fun moveFile( + sourceRemotePath: String, + targetRemotePath: String, + forceOverwrite: Boolean, + ): RemoteOperationResult + fun refreshFolder( remotePath: String ): RemoteOperationResult> diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index 46d02a27..5111f0b1 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -29,6 +29,7 @@ import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperatio import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation +import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation import com.owncloud.android.lib.resources.files.RemoteFile import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation @@ -68,6 +69,17 @@ class OCFileService(override val client: OwnCloudClient) : FileService { localFolderPath = localTempPath ).execute(client) + override fun moveFile( + sourceRemotePath: String, + targetRemotePath: String, + forceOverwrite: Boolean + ): RemoteOperationResult = + MoveRemoteFileOperation( + sourceRemotePath = sourceRemotePath, + targetRemotePath = targetRemotePath, + forceOverwrite = forceOverwrite + ).execute(client) + override fun refreshFolder(remotePath: String): RemoteOperationResult> = ReadRemoteFolderOperation( remotePath = remotePath From f849cd76d452d0192f076ebb419bf2a65e173ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 11 May 2021 18:16:19 +0200 Subject: [PATCH 32/62] Make overwrite option disabled by default for move operations --- .../common/http/methods/webdav/MoveMethod.kt | 2 +- .../files/MoveRemoteFileOperation.kt | 30 +++++++++++-------- .../chunks/MoveRemoteChunksFileOperation.kt | 2 -- .../resources/files/services/ChunkService.kt | 1 - .../resources/files/services/FileService.kt | 1 - .../services/implementation/OCChunkService.kt | 4 +-- .../services/implementation/OCFileService.kt | 2 -- 7 files changed, 19 insertions(+), 23 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MoveMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MoveMethod.kt index bc6d9d3e..8cb4c0e9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MoveMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/MoveMethod.kt @@ -36,7 +36,7 @@ import java.net.URL class MoveMethod( url: URL, private val destinationUrl: String, - private val forceOverride: Boolean + private val forceOverride: Boolean = false ) : DavMethod(url) { @Throws(Exception::class) override fun onDavExecute(davResource: DavOCResource): Int { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt index 373574d1..5741766a 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/MoveRemoteFileOperation.kt @@ -49,7 +49,6 @@ import java.util.concurrent.TimeUnit open class MoveRemoteFileOperation( private val sourceRemotePath: String, private val targetRemotePath: String, - private val forceOverwrite: Boolean ) : RemoteOperation() { /** @@ -76,7 +75,6 @@ open class MoveRemoteFileOperation( val moveMethod = MoveMethod( url = URL(srcWebDavUri.toString() + WebdavUtils.encodePath(sourceRemotePath)), destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath), - forceOverride = forceOverwrite ).apply { addRequestHeaders(this) setReadTimeout(MOVE_READ_TIMEOUT, TimeUnit.SECONDS) @@ -85,17 +83,21 @@ open class MoveRemoteFileOperation( val status = client.executeHttpMethod(moveMethod) - if (isSuccess(status)) { - result = RemoteOperationResult(ResultCode.OK) - } else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !forceOverwrite) { - result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE) - client.exhaustResponse(moveMethod.getResponseBodyAsStream()) + when { + isSuccess(status) -> { + result = RemoteOperationResult(ResultCode.OK) + } + isPreconditionFailed(status) -> { + result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE) + client.exhaustResponse(moveMethod.getResponseBodyAsStream()) - /// for other errors that could be explicitly handled, check first: - /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4 - } else { - result = RemoteOperationResult(moveMethod) - client.exhaustResponse(moveMethod.getResponseBodyAsStream()) + /// for other errors that could be explicitly handled, check first: + /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4 + } + else -> { + result = RemoteOperationResult(moveMethod) + client.exhaustResponse(moveMethod.getResponseBodyAsStream()) + } } Timber.i("Move $sourceRemotePath to $targetRemotePath: ${result.logMessage}") @@ -120,7 +122,9 @@ open class MoveRemoteFileOperation( open fun addRequestHeaders(moveMethod: MoveMethod) { } - protected fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT) + private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT) + + private fun isPreconditionFailed(status: Int) = status == HttpConstants.HTTP_PRECONDITION_FAILED companion object { private const val MOVE_READ_TIMEOUT = 10L diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/MoveRemoteChunksFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/MoveRemoteChunksFileOperation.kt index fc8a0a96..9bdccd17 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/MoveRemoteChunksFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/MoveRemoteChunksFileOperation.kt @@ -38,13 +38,11 @@ import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation class MoveRemoteChunksFileOperation( sourceRemotePath: String, targetRemotePath: String, - overwrite: Boolean, private val fileLastModificationTimestamp: String, private val fileLength: Long ) : MoveRemoteFileOperation( sourceRemotePath = sourceRemotePath, targetRemotePath = targetRemotePath, - forceOverwrite = overwrite ) { override fun getSrcWebDavUriForClient(client: OwnCloudClient): Uri = client.uploadsWebDavUri diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt index 895b3c14..10410b9e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/ChunkService.kt @@ -35,7 +35,6 @@ interface ChunkService : Service { fun moveFile( sourceRemotePath: String, targetRemotePath: String, - overwrite: Boolean, fileLastModificationTimestamp: String, fileLength: Long ): RemoteOperationResult diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index cc88667c..4df70a17 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -49,7 +49,6 @@ interface FileService : Service { fun moveFile( sourceRemotePath: String, targetRemotePath: String, - forceOverwrite: Boolean, ): RemoteOperationResult fun refreshFolder( diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt index 78068410..cf5d4820 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCChunkService.kt @@ -38,15 +38,13 @@ class OCChunkService(override val client: OwnCloudClient) : ChunkService { override fun moveFile( sourceRemotePath: String, targetRemotePath: String, - overwrite: Boolean, fileLastModificationTimestamp: String, fileLength: Long ): RemoteOperationResult = MoveRemoteChunksFileOperation( sourceRemotePath = sourceRemotePath, targetRemotePath = targetRemotePath, - overwrite = overwrite, fileLastModificationTimestamp = fileLastModificationTimestamp, - fileLength = fileLength + fileLength = fileLength, ).execute(client) } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index 5111f0b1..e85f71a4 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -72,12 +72,10 @@ class OCFileService(override val client: OwnCloudClient) : FileService { override fun moveFile( sourceRemotePath: String, targetRemotePath: String, - forceOverwrite: Boolean ): RemoteOperationResult = MoveRemoteFileOperation( sourceRemotePath = sourceRemotePath, targetRemotePath = targetRemotePath, - forceOverwrite = forceOverwrite ).execute(client) override fun refreshFolder(remotePath: String): RemoteOperationResult> = From 5f01b32b12e19ea92595ebd69175a087608efae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 11 May 2021 10:34:00 +0200 Subject: [PATCH 33/62] Migrate RenameRemoteFileOperation to kotlin. First step to keep git history --- ...enameRemoteFileOperation.java => RenameRemoteFileOperation.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/{RenameRemoteFileOperation.java => RenameRemoteFileOperation.kt} (100%) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt similarity index 100% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt From 24037a7a3e364723e9c77dc8013470f6d39853c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 11 May 2021 11:24:27 +0200 Subject: [PATCH 34/62] Migrate RenameRemoteFileOperation to kotlin. Second step to keep git history --- .../files/RenameRemoteFileOperation.kt | 141 ++++++++---------- 1 file changed, 64 insertions(+), 77 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt index 8d53a612..8669aded 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2019 ownCloud GmbH. + * Copyright (C) 2021 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,20 +22,20 @@ * */ -package com.owncloud.android.lib.resources.files; +package com.owncloud.android.lib.resources.files -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.http.HttpConstants; -import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; -import timber.log.Timber; - -import java.io.File; -import java.net.URL; -import java.util.concurrent.TimeUnit; +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.webdav.MoveMethod +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.common.utils.isOneOf +import timber.log.Timber +import java.io.File +import java.net.URL +import java.util.concurrent.TimeUnit /** * Remote operation performing the rename of a remote file or folder in the ownCloud server. @@ -43,77 +43,58 @@ import java.util.concurrent.TimeUnit; * @author David A. Velasco * @author masensio */ -public class RenameRemoteFileOperation extends RemoteOperation { +class RenameRemoteFileOperation( + private val oldName: String, + private val oldRemotePath: String, + private val newName: String, + isFolder: Boolean, +) : RemoteOperation() { - private static final int RENAME_READ_TIMEOUT = 600000; - private static final int RENAME_CONNECTION_TIMEOUT = 5000; + private var newRemotePath: String? = null - private String mOldName; - private String mOldRemotePath; - private String mNewName; - private String mNewRemotePath; - - /** - * Constructor - * - * @param oldName Old name of the file. - * @param oldRemotePath Old remote path of the file. - * @param newName New name to set as the name of file. - * @param isFolder 'true' for folder and 'false' for files - */ - public RenameRemoteFileOperation(String oldName, String oldRemotePath, String newName, - boolean isFolder) { - mOldName = oldName; - mOldRemotePath = oldRemotePath; - mNewName = newName; - - String parent = (new File(mOldRemotePath)).getParent(); - parent = (parent.endsWith(File.separator)) ? parent : parent + File.separator; - mNewRemotePath = parent + mNewName; + init { + var parent = (File(oldRemotePath)).parent!! + parent = if (parent.endsWith(File.separator)) parent else parent + File.separator + newRemotePath = parent + newName if (isFolder) { - mNewRemotePath += File.separator; + newRemotePath.plus(File.separator) } } - /** - * Performs the rename operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult try { - if (mNewName.equals(mOldName)) { - return new RemoteOperationResult<>(ResultCode.OK); + if (newName == oldName) { + return RemoteOperationResult(ResultCode.OK) } if (targetPathIsUsed(client)) { - return new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE); + return RemoteOperationResult(ResultCode.INVALID_OVERWRITE) } - final MoveMethod move = new MoveMethod( - new URL(client.getUserFilesWebDavUri() + - WebdavUtils.encodePath(mOldRemotePath)), - client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mNewRemotePath), false); + val moveMethod: MoveMethod = MoveMethod( + url = URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(oldRemotePath)), + destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(newRemotePath), + forceOverride = false + ).apply { + setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.MILLISECONDS) + setConnectionTimeout(RENAME_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS) + } + val status = client.executeHttpMethod(moveMethod) - move.setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.SECONDS); - move.setConnectionTimeout(RENAME_READ_TIMEOUT, TimeUnit.SECONDS); + result = if (isSuccess(status)) { + RemoteOperationResult(ResultCode.OK) + } else { + RemoteOperationResult(moveMethod) + } - final int status = client.executeHttpMethod(move); - final RemoteOperationResult result = - (status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT) - ? new RemoteOperationResult<>(ResultCode.OK) - : new RemoteOperationResult<>(move); - - Timber.i("Rename " + mOldRemotePath + " to " + mNewRemotePath + ": " + result.getLogMessage()); - client.exhaustResponse(move.getResponseBodyAsStream()); - return result; - } catch (Exception e) { - final RemoteOperationResult result = new RemoteOperationResult<>(e); - Timber.e(e, - "Rename " + mOldRemotePath + " to " + ((mNewRemotePath == null) ? mNewName : mNewRemotePath) + ":" + - " " + result.getLogMessage()); - return result; + Timber.i("Rename $oldRemotePath to $newRemotePath: ${result.logMessage}") + client.exhaustResponse(moveMethod.getResponseBodyAsStream()) + return result + } catch (exception: Exception) { + result = RemoteOperationResult(exception) + Timber.e(exception, "Rename $oldRemotePath to $newName: ${result.logMessage}") + return result } } @@ -122,10 +103,16 @@ public class RenameRemoteFileOperation extends RemoteOperation { * * @return 'True' if the target path is already used by an existing file. */ - private boolean targetPathIsUsed(OwnCloudClient client) { - CheckPathExistenceRemoteOperation checkPathExistenceRemoteOperation = - new CheckPathExistenceRemoteOperation(mNewRemotePath, false); - RemoteOperationResult exists = checkPathExistenceRemoteOperation.execute(client); - return exists.isSuccess(); + private fun targetPathIsUsed(client: OwnCloudClient): Boolean { + val checkPathExistenceRemoteOperation = CheckPathExistenceRemoteOperation(newRemotePath, false) + val exists = checkPathExistenceRemoteOperation.execute(client) + return exists.isSuccess } -} \ No newline at end of file + + private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT) + + companion object { + private const val RENAME_READ_TIMEOUT = 10_000L + private const val RENAME_CONNECTION_TIMEOUT = 5_000L + } +} From 6fb5350dea25c208c228c709f3c21b6f3e97ab26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 11 May 2021 11:28:35 +0200 Subject: [PATCH 35/62] Add rename file to FileService --- .../resources/files/services/FileService.kt | 7 +++++ .../services/implementation/OCFileService.kt | 26 ++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index 4df70a17..9215955c 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -58,4 +58,11 @@ interface FileService : Service { fun removeFile( remotePath: String ): RemoteOperationResult + + fun renameFile( + oldName: String, + oldRemotePath: String, + newName: String, + isFolder: Boolean, + ): RemoteOperationResult } diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index e85f71a4..6995d91f 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -33,6 +33,7 @@ import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation import com.owncloud.android.lib.resources.files.RemoteFile import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation +import com.owncloud.android.lib.resources.files.RenameRemoteFileOperation import com.owncloud.android.lib.resources.files.services.FileService class OCFileService(override val client: OwnCloudClient) : FileService { @@ -78,11 +79,30 @@ class OCFileService(override val client: OwnCloudClient) : FileService { targetRemotePath = targetRemotePath, ).execute(client) - override fun refreshFolder(remotePath: String): RemoteOperationResult> = + override fun refreshFolder( + remotePath: String + ): RemoteOperationResult> = ReadRemoteFolderOperation( remotePath = remotePath ).execute(client) - override fun removeFile(remotePath: String): RemoteOperationResult = - RemoveRemoteFileOperation(remotePath = remotePath).execute(client) + override fun removeFile( + remotePath: String + ): RemoteOperationResult = + RemoveRemoteFileOperation( + remotePath = remotePath + ).execute(client) + + override fun renameFile( + oldName: String, + oldRemotePath: String, + newName: String, + isFolder: Boolean + ): RemoteOperationResult = + RenameRemoteFileOperation( + oldName = oldName, + oldRemotePath = oldRemotePath, + newName = newName, + isFolder = isFolder + ).execute(client) } From 6f33dca67221128fd18f41bd144e26180645dbc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Thu, 20 May 2021 14:02:05 +0200 Subject: [PATCH 36/62] Remove nullability from a variable --- .../android/lib/resources/files/RenameRemoteFileOperation.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt index 8669aded..c89e12e8 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt @@ -50,7 +50,7 @@ class RenameRemoteFileOperation( isFolder: Boolean, ) : RemoteOperation() { - private var newRemotePath: String? = null + private var newRemotePath: String init { var parent = (File(oldRemotePath)).parent!! @@ -75,7 +75,6 @@ class RenameRemoteFileOperation( val moveMethod: MoveMethod = MoveMethod( url = URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(oldRemotePath)), destinationUrl = client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(newRemotePath), - forceOverride = false ).apply { setReadTimeout(RENAME_READ_TIMEOUT, TimeUnit.MILLISECONDS) setConnectionTimeout(RENAME_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS) From f7645c1648c70f3f5f92ede9d1e05bb86f390753 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Mon, 24 May 2021 11:27:44 +0200 Subject: [PATCH 37/62] Apply code review suggestions --- .../lib/resources/files/RenameRemoteFileOperation.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt index c89e12e8..8f669c69 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.kt @@ -53,9 +53,11 @@ class RenameRemoteFileOperation( private var newRemotePath: String init { - var parent = (File(oldRemotePath)).parent!! - parent = if (parent.endsWith(File.separator)) parent else parent + File.separator - newRemotePath = parent + newName + var parent = (File(oldRemotePath)).parent ?: throw IllegalArgumentException() + if (!parent.endsWith(File.separator)) { + parent = parent.plus(File.separator) + } + newRemotePath = parent.plus(newName) if (isFolder) { newRemotePath.plus(File.separator) } From 33d6141613d05499b8684be9ca09d0a29de1c174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Fri, 21 May 2021 13:47:44 +0200 Subject: [PATCH 38/62] Migrate CopyRemoteFileOperation to kotlin. First step to keep git history --- .../{CopyRemoteFileOperation.java => CopyRemoteFileOperation.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/{CopyRemoteFileOperation.java => CopyRemoteFileOperation.kt} (100%) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt similarity index 100% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.java rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt From 5449eb7480bbe0e21e6e3ff63806df31000311cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Fri, 21 May 2021 13:53:55 +0200 Subject: [PATCH 39/62] Migrate CopyRemoteFileOperation to kotlin. Second step to keep git history --- .../files/CopyRemoteFileOperation.kt | 128 ++++++++---------- 1 file changed, 53 insertions(+), 75 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt index 9c78425a..acb650d5 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt @@ -21,110 +21,88 @@ * THE SOFTWARE. * */ +package com.owncloud.android.lib.resources.files -package com.owncloud.android.lib.resources.files; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.http.HttpConstants; -import com.owncloud.android.lib.common.http.methods.webdav.CopyMethod; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; -import timber.log.Timber; - -import java.net.URL; -import java.util.concurrent.TimeUnit; +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.webdav.CopyMethod +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import timber.log.Timber +import java.net.URL +import java.util.concurrent.TimeUnit /** - * Remote operation moving a remote file or folder in the ownCloud server to a different folder + * Remote operation copying a remote file or folder in the ownCloud server to a different folder * in the same account. * - * Allows renaming the moving file/folder at the same time. + * Allows renaming the copying file/folder at the same time. * * @author David A. Velasco * @author Christian Schabesberger * @author David González V. + * + * @param srcRemotePath Remote path of the file/folder to move. + * @param targetRemotePath Remove path desired for the file/folder after moving it. */ -public class CopyRemoteFileOperation extends RemoteOperation { - - private static final int COPY_READ_TIMEOUT = 600000; - private static final int COPY_CONNECTION_TIMEOUT = 5000; - - private String mSrcRemotePath; - private String mTargetRemotePath; - - private boolean mOverwrite; - - /** - * Constructor. - *

- * TODO Paths should finish in "/" in the case of folders. ? - * - * @param srcRemotePath Remote path of the file/folder to move. - * @param targetRemotePath Remove path desired for the file/folder after moving it. - */ - public CopyRemoteFileOperation(String srcRemotePath, String targetRemotePath, boolean overwrite - ) { - mSrcRemotePath = srcRemotePath; - mTargetRemotePath = targetRemotePath; - mOverwrite = overwrite; - } - +class CopyRemoteFileOperation( + private val srcRemotePath: String, + private val targetRemotePath: String, + private val overwrite: Boolean +) : RemoteOperation() { /** * Performs the rename operation. * * @param client Client object to communicate with the remote ownCloud server. */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - - if (mTargetRemotePath.equals(mSrcRemotePath)) { + override fun run(client: OwnCloudClient): RemoteOperationResult { + if (targetRemotePath == srcRemotePath) { // nothing to do! - return new RemoteOperationResult<>(ResultCode.OK); + return RemoteOperationResult(ResultCode.OK) } - - if (mTargetRemotePath.startsWith(mSrcRemotePath)) { - return new RemoteOperationResult<>(ResultCode.INVALID_COPY_INTO_DESCENDANT); + if (targetRemotePath.startsWith(srcRemotePath)) { + return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT) } /// perform remote operation - RemoteOperationResult result; + var result: RemoteOperationResult try { - CopyMethod copyMethod = - new CopyMethod( - new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mSrcRemotePath)), - client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mTargetRemotePath), - mOverwrite); - - copyMethod.setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS); - copyMethod.setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS); - - final int status = client.executeHttpMethod(copyMethod); + val copyMethod = CopyMethod( + URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(srcRemotePath)), + client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath), + overwrite + ).apply { + setReadTimeout(COPY_READ_TIMEOUT.toLong(), TimeUnit.SECONDS) + setConnectionTimeout(COPY_CONNECTION_TIMEOUT.toLong(), TimeUnit.SECONDS) + } + val status = client.executeHttpMethod(copyMethod) if (status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT) { - String fileRemoteId = copyMethod.getResponseHeader(HttpConstants.OC_FILE_REMOTE_ID); - result = new RemoteOperationResult<>(ResultCode.OK); - result.setData(fileRemoteId); - } else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !mOverwrite) { - result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE); - client.exhaustResponse(copyMethod.getResponseBodyAsStream()); + val fileRemoteId = copyMethod.getResponseHeader(HttpConstants.OC_FILE_REMOTE_ID) + result = RemoteOperationResult(ResultCode.OK) + result.setData(fileRemoteId) + } else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !overwrite) { + result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE) + client.exhaustResponse(copyMethod.getResponseBodyAsStream()) /// for other errors that could be explicitly handled, check first: /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4 } else { - - result = new RemoteOperationResult<>(copyMethod); - client.exhaustResponse(copyMethod.getResponseBodyAsStream()); + result = RemoteOperationResult(copyMethod) + client.exhaustResponse(copyMethod.getResponseBodyAsStream()) } - - Timber.i("Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage()); - - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - Timber.e(e, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage()); + Timber.i("Copy " + srcRemotePath + " to " + targetRemotePath + ": " + result.logMessage) + } catch (e: Exception) { + result = RemoteOperationResult(e) + Timber.e(e, "Copy " + srcRemotePath + " to " + targetRemotePath + ": " + result.logMessage) } + return result + } - return result; + companion object { + private const val COPY_READ_TIMEOUT = 600_000 + private const val COPY_CONNECTION_TIMEOUT = 5_000 } } From 34cc4c87b18634f531854ae12393afedca175e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Fri, 21 May 2021 14:01:25 +0200 Subject: [PATCH 40/62] Refactor copy remote file operation. Make overwrite false as default to avoid some potential errors --- .../common/http/methods/webdav/CopyMethod.kt | 2 +- .../files/CopyRemoteFileOperation.kt | 54 ++++++++++--------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/CopyMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/CopyMethod.kt index d10718e0..217f3ca9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/CopyMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/CopyMethod.kt @@ -36,7 +36,7 @@ import java.net.URL class CopyMethod( val url: URL, private val destinationUrl: String, - private val forceOverride: Boolean + private val forceOverride: Boolean = false ) : DavMethod(url) { @Throws(Exception::class) public override fun onDavExecute(davResource: DavOCResource): Int { diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt index acb650d5..c1b77f11 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. + * Copyright (C) 2022 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,6 +30,7 @@ import com.owncloud.android.lib.common.network.WebdavUtils import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.common.utils.isOneOf import timber.log.Timber import java.net.URL import java.util.concurrent.TimeUnit @@ -44,13 +45,12 @@ import java.util.concurrent.TimeUnit * @author Christian Schabesberger * @author David González V. * - * @param srcRemotePath Remote path of the file/folder to move. - * @param targetRemotePath Remove path desired for the file/folder after moving it. + * @param srcRemotePath Remote path of the file/folder to copy. + * @param targetRemotePath Remote path desired for the file/folder to copy it. */ class CopyRemoteFileOperation( private val srcRemotePath: String, private val targetRemotePath: String, - private val overwrite: Boolean ) : RemoteOperation() { /** * Performs the rename operation. @@ -72,37 +72,43 @@ class CopyRemoteFileOperation( val copyMethod = CopyMethod( URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(srcRemotePath)), client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(targetRemotePath), - overwrite ).apply { - setReadTimeout(COPY_READ_TIMEOUT.toLong(), TimeUnit.SECONDS) - setConnectionTimeout(COPY_CONNECTION_TIMEOUT.toLong(), TimeUnit.SECONDS) + setReadTimeout(COPY_READ_TIMEOUT, TimeUnit.SECONDS) + setConnectionTimeout(COPY_CONNECTION_TIMEOUT, TimeUnit.SECONDS) } val status = client.executeHttpMethod(copyMethod) + when { + isSuccess(status) -> { + val fileRemoteId = copyMethod.getResponseHeader(HttpConstants.OC_FILE_REMOTE_ID) + result = RemoteOperationResult(ResultCode.OK) + result.setData(fileRemoteId) + } + isPreconditionFailed(status) -> { + result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE) + client.exhaustResponse(copyMethod.getResponseBodyAsStream()) - if (status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT) { - val fileRemoteId = copyMethod.getResponseHeader(HttpConstants.OC_FILE_REMOTE_ID) - result = RemoteOperationResult(ResultCode.OK) - result.setData(fileRemoteId) - } else if (status == HttpConstants.HTTP_PRECONDITION_FAILED && !overwrite) { - result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE) - client.exhaustResponse(copyMethod.getResponseBodyAsStream()) - - /// for other errors that could be explicitly handled, check first: - /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4 - } else { - result = RemoteOperationResult(copyMethod) - client.exhaustResponse(copyMethod.getResponseBodyAsStream()) + /// for other errors that could be explicitly handled, check first: + /// http://www.webdav.org/specs/rfc4918.html#rfc.section.9.9.4 + } + else -> { + result = RemoteOperationResult(copyMethod) + client.exhaustResponse(copyMethod.getResponseBodyAsStream()) + } } - Timber.i("Copy " + srcRemotePath + " to " + targetRemotePath + ": " + result.logMessage) + Timber.i("Copy $srcRemotePath to $targetRemotePath: ${result.logMessage}") } catch (e: Exception) { result = RemoteOperationResult(e) - Timber.e(e, "Copy " + srcRemotePath + " to " + targetRemotePath + ": " + result.logMessage) + Timber.e(e, "Copy $srcRemotePath to $targetRemotePath: ${result.logMessage}") } return result } + private fun isSuccess(status: Int) = status.isOneOf(HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT) + + private fun isPreconditionFailed(status: Int) = status == HttpConstants.HTTP_PRECONDITION_FAILED + companion object { - private const val COPY_READ_TIMEOUT = 600_000 - private const val COPY_CONNECTION_TIMEOUT = 5_000 + private const val COPY_READ_TIMEOUT = 10L + private const val COPY_CONNECTION_TIMEOUT = 6L } } From 4c39990edb0b9f1222fbfc80bd6366db2c465354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 25 May 2021 08:56:42 +0200 Subject: [PATCH 41/62] Add copy operation to the file service --- .../lib/resources/files/CopyRemoteFileOperation.kt | 6 +++--- .../lib/resources/files/services/FileService.kt | 5 +++++ .../files/services/implementation/OCFileService.kt | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt index c1b77f11..ce47a4fd 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/CopyRemoteFileOperation.kt @@ -51,13 +51,13 @@ import java.util.concurrent.TimeUnit class CopyRemoteFileOperation( private val srcRemotePath: String, private val targetRemotePath: String, -) : RemoteOperation() { +) : RemoteOperation() { /** * Performs the rename operation. * * @param client Client object to communicate with the remote ownCloud server. */ - override fun run(client: OwnCloudClient): RemoteOperationResult { + override fun run(client: OwnCloudClient): RemoteOperationResult { if (targetRemotePath == srcRemotePath) { // nothing to do! return RemoteOperationResult(ResultCode.OK) @@ -67,7 +67,7 @@ class CopyRemoteFileOperation( } /// perform remote operation - var result: RemoteOperationResult + var result: RemoteOperationResult try { val copyMethod = CopyMethod( URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(srcRemotePath)), diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index 9215955c..8b03f4b9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -35,6 +35,11 @@ interface FileService : Service { isUserLogged: Boolean ): RemoteOperationResult + fun copyFile( + sourceRemotePath: String, + targetRemotePath: String, + ): RemoteOperationResult + fun createFolder( remotePath: String, createFullPath: Boolean, diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index 6995d91f..37d258a0 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -26,6 +26,7 @@ package com.owncloud.android.lib.resources.files.services.implementation import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.files.CheckPathExistenceRemoteOperation +import com.owncloud.android.lib.resources.files.CopyRemoteFileOperation import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation @@ -50,6 +51,15 @@ class OCFileService(override val client: OwnCloudClient) : FileService { override fun getUrlToOpenInWeb(openWebEndpoint: String, fileId: String): RemoteOperationResult = GetUrlToOpenInWebRemoteOperation(openWithWebEndpoint = openWebEndpoint, fileId = fileId).execute(client) + override fun copyFile( + sourceRemotePath: String, + targetRemotePath: String + ): RemoteOperationResult = + CopyRemoteFileOperation( + srcRemotePath = sourceRemotePath, + targetRemotePath = targetRemotePath + ).execute(client) + override fun createFolder( remotePath: String, createFullPath: Boolean, From c6584d69fd7b0bc06a0276cf68ec1b43179ebfe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 18 Jan 2022 08:28:59 +0100 Subject: [PATCH 42/62] Migrate remote upload operation to kotlin --- .../UploadFileFromFileSystemOperation.kt | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt new file mode 100644 index 00000000..f8c6379a --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt @@ -0,0 +1,135 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.resources.files + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.HttpConstants +import com.owncloud.android.lib.common.http.methods.webdav.PutMethod +import com.owncloud.android.lib.common.network.FileRequestBody +import com.owncloud.android.lib.common.network.OnDatatransferProgressListener +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.OperationCancelledException +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import timber.log.Timber +import java.io.File +import java.net.URL +import java.util.HashSet +import java.util.concurrent.atomic.AtomicBoolean + +/** + * Remote operation performing the upload of a remote file to the ownCloud server. + * + * @author David A. Velasco + * @author masensio + * @author David González Verdugo + * @author Abel García de Prada + */ +open class UploadFileFromFileSystemOperation( + val localPath: String, + val remotePath: String, + val mimeType: String, + val lastModifiedTimestamp: String, + val requiredEtag: String, +) : RemoteOperation() { + + protected val cancellationRequested = AtomicBoolean(false) + protected var putMethod: PutMethod? = null + protected val dataTransferListener: MutableSet = HashSet() + protected var fileRequestBody: FileRequestBody? = null + + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + try { + if (cancellationRequested.get()) { + // the operation was cancelled before getting it's turn to be executed in the queue of uploads + result = RemoteOperationResult(OperationCancelledException()) + Timber.i("Upload of $localPath to $remotePath has been cancelled") + } else { + // perform the upload + result = uploadFile(client) + Timber.i("Upload of $localPath to $remotePath: ${result.logMessage}") + } + } catch (e: Exception) { + if (putMethod?.isAborted == true) { + result = RemoteOperationResult(OperationCancelledException()) + Timber.e(result.exception, "Upload of $localPath to $remotePath has been aborted with this message: ${result.logMessage}") + } else { + result = RemoteOperationResult(e) + Timber.e(result.exception, "Upload of $localPath to $remotePath has failed with this message: ${result.logMessage}") + } + } + return result + } + + @Throws(Exception::class) + protected open fun uploadFile(client: OwnCloudClient): RemoteOperationResult { + val fileToUpload = File(localPath) + val mediaType: MediaType? = mimeType.toMediaTypeOrNull() + + fileRequestBody = FileRequestBody(fileToUpload, mediaType).also { + synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) } + } + + putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)), fileRequestBody!!).apply { + setRetryOnConnectionFailure(false) + if (requiredEtag.isNotBlank()) { + addRequestHeader(HttpConstants.IF_MATCH_HEADER, requiredEtag) + } + addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, fileToUpload.length().toString()) + addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, lastModifiedTimestamp) + } + + val status = client.executeHttpMethod(putMethod) + return if (isSuccess(status)) { + RemoteOperationResult(ResultCode.OK) + } else { // synchronization failed + RemoteOperationResult(putMethod) + } + } + + fun addDataTransferProgressListener(listener: OnDatatransferProgressListener) { + synchronized(dataTransferListener) { dataTransferListener.add(listener) } + fileRequestBody?.addDatatransferProgressListener(listener) + } + + fun removeDataTransferProgressListener(listener: OnDatatransferProgressListener) { + synchronized(dataTransferListener) { dataTransferListener.remove(listener) } + fileRequestBody?.removeDatatransferProgressListener(listener) + } + + fun cancel() { + synchronized(cancellationRequested) { + cancellationRequested.set(true) + putMethod?.abort() + } + } + + fun isSuccess(status: Int): Boolean { + return status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT + } +} From 340e114a9176a4ba0c0ba6e224e69c4607c5f708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 18 Jan 2022 08:31:56 +0100 Subject: [PATCH 43/62] Use extension to simplify a little bit the code --- .../lib/resources/files/UploadFileFromFileSystemOperation.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt index f8c6379a..e4caa869 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt @@ -33,6 +33,7 @@ import com.owncloud.android.lib.common.operations.OperationCancelledException import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.common.utils.isOneOf import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaTypeOrNull import timber.log.Timber @@ -130,6 +131,6 @@ open class UploadFileFromFileSystemOperation( } fun isSuccess(status: Int): Boolean { - return status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT + return status.isOneOf(HttpConstants.HTTP_OK, HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT) } } From cbbeaab2518fee60360fb19cc32be03b53fb1697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 18 Jan 2022 09:05:36 +0100 Subject: [PATCH 44/62] Migrate remote chunk upload operation to kotlin --- .../lib/resources/files/FileUtils.java | 1 + .../ChunkedUploadFromFileSystemOperation.kt | 124 ++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/FileUtils.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/FileUtils.java index cc159611..1ac0a6fb 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/FileUtils.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/FileUtils.java @@ -32,6 +32,7 @@ public class FileUtils { public static final String FINAL_CHUNKS_FILE = ".file"; public static final String MIME_DIR = "DIR"; public static final String MIME_DIR_UNIX = "httpd/unix-directory"; + public static final String MODE_READ_ONLY = "r"; static String getParentPath(String remotePath) { String parentPath = new File(remotePath).getParent(); diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt new file mode 100644 index 00000000..cc42b6bf --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt @@ -0,0 +1,124 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.resources.files.chunks + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.http.methods.webdav.PutMethod +import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody +import com.owncloud.android.lib.common.operations.OperationCancelledException +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode +import com.owncloud.android.lib.resources.files.FileUtils.MODE_READ_ONLY +import com.owncloud.android.lib.resources.files.UploadFileFromFileSystemOperation +import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import timber.log.Timber +import java.io.File +import java.io.RandomAccessFile +import java.net.URL +import java.nio.channels.FileChannel +import java.util.concurrent.TimeUnit +import kotlin.math.ceil + +/** + * Remote operation performing the chunked upload of a remote file to the ownCloud server. + * + * @author David A. Velasco + * @author David González Verdugo + * @author Abel García de Prada + */ +class ChunkedUploadFromFileSystemOperation( + private val transferId: String, + localPath: String, + remotePath: String, + mimeType: String, + lastModifiedTimestamp: String, + requiredEtag: String, +) : UploadFileFromFileSystemOperation( + localPath = localPath, + remotePath = remotePath, + mimeType = mimeType, + lastModifiedTimestamp = lastModifiedTimestamp, + requiredEtag = requiredEtag +) { + + @Throws(Exception::class) + override fun uploadFile(client: OwnCloudClient): RemoteOperationResult { + lateinit var result: RemoteOperationResult + + val fileToUpload = File(localPath) + val mediaType: MediaType? = mimeType.toMediaTypeOrNull() + val raf: RandomAccessFile = RandomAccessFile(fileToUpload, MODE_READ_ONLY) + val channel: FileChannel = raf.channel + + fileRequestBody = ChunkFromFileRequestBody(fileToUpload, mediaType, channel, CHUNK_SIZE).also { + synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) } + } + + val uriPrefix = client.uploadsWebDavUri.toString() + File.separator + transferId + val totalLength = fileToUpload.length() + val chunkCount = ceil(totalLength.toDouble() / CHUNK_SIZE).toLong() + var chunkIndex = 0 + var offset: Long = 0 + + while (chunkIndex < chunkCount) { + (fileRequestBody as ChunkFromFileRequestBody).setOffset(offset) + + if (cancellationRequested.get()) { + result = RemoteOperationResult(OperationCancelledException()) + break + } else { + putMethod = PutMethod(URL(uriPrefix + File.separator + chunkIndex), fileRequestBody as ChunkFromFileRequestBody).apply { + if (chunkIndex.toLong() == chunkCount - 1) { + // Added a high timeout to the last chunk due to when the last chunk + // arrives to the server with the last PUT, all chunks get assembled + // within that PHP request, so last one takes longer. + setReadTimeout(LAST_CHUNK_TIMEOUT.toLong(), TimeUnit.MILLISECONDS) + } + } + + val status = client.executeHttpMethod(putMethod) + + Timber.d("Upload of $localPath to $remotePath, chunk index $chunkIndex, count $chunkCount, HTTP result status $status") + + if (isSuccess(status)) { + result = RemoteOperationResult(ResultCode.OK) + } else { + result = RemoteOperationResult(putMethod) + break + } + } + chunkIndex++ + offset += CHUNK_SIZE + } + channel.close() + raf.close() + return result + } + + companion object { + const val CHUNK_SIZE = 1_024_000L + private const val LAST_CHUNK_TIMEOUT = 900_000 // 15 mins. + } +} From 71e9bbd2dabb6b98098301ef93d4d38846228eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 18 Jan 2022 09:19:38 +0100 Subject: [PATCH 45/62] Delete old java operations. Use the kotlin ones from now on --- .../files/UploadRemoteFileOperation.java | 181 ------------------ .../ChunkedUploadRemoteFileOperation.java | 130 ------------- 2 files changed, 311 deletions(-) delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadRemoteFileOperation.java delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadRemoteFileOperation.java diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadRemoteFileOperation.java deleted file mode 100644 index 43181b28..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadRemoteFileOperation.java +++ /dev/null @@ -1,181 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.resources.files; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.http.HttpConstants; -import com.owncloud.android.lib.common.http.methods.webdav.PutMethod; -import com.owncloud.android.lib.common.network.FileRequestBody; -import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.OperationCancelledException; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import okhttp3.MediaType; -import timber.log.Timber; - -import java.io.File; -import java.net.URL; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; - -import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK; - -/** - * Remote operation performing the upload of a remote file to the ownCloud server. - * - * @author David A. Velasco - * @author masensio - * @author David González Verdugo - */ - -public class UploadRemoteFileOperation extends RemoteOperation { - - protected final AtomicBoolean mCancellationRequested = new AtomicBoolean(false); - protected String mLocalPath; - protected String mRemotePath; - protected String mMimeType; - protected String mFileLastModifTimestamp; - protected PutMethod mPutMethod = null; - protected String mRequiredEtag = null; - protected Set mDataTransferListeners = new HashSet<>(); - - protected FileRequestBody mFileRequestBody = null; - - public UploadRemoteFileOperation(String localPath, String remotePath, String mimeType, - String fileLastModifTimestamp) { - mLocalPath = localPath; - mRemotePath = remotePath; - mMimeType = mimeType; - mFileLastModifTimestamp = fileLastModifTimestamp; - } - - public UploadRemoteFileOperation(String localPath, String remotePath, String mimeType, - String requiredEtag, String fileLastModifTimestamp) { - this(localPath, remotePath, mimeType, fileLastModifTimestamp); - mRequiredEtag = requiredEtag; - } - - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result; - - try { - - if (mCancellationRequested.get()) { - // the operation was cancelled before getting it's turn to be executed in the queue of uploads - result = new RemoteOperationResult<>(new OperationCancelledException()); - } else { - // perform the upload - result = uploadFile(client); - Timber.i("Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage()); - } - - } catch (Exception e) { - - if (mPutMethod != null && mPutMethod.isAborted()) { - result = new RemoteOperationResult<>(new OperationCancelledException()); - Timber.e(result.getException(), - "Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage()); - } else { - result = new RemoteOperationResult<>(e); - Timber.e(e, "Upload of " + mLocalPath + " to " + mRemotePath + ": " + result.getLogMessage()); - } - } - - return result; - } - - protected RemoteOperationResult uploadFile(OwnCloudClient client) throws Exception { - - File fileToUpload = new File(mLocalPath); - - MediaType mediaType = MediaType.parse(mMimeType); - - mFileRequestBody = new FileRequestBody(fileToUpload, mediaType); - - synchronized (mDataTransferListeners) { - mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners); - } - - mPutMethod = new PutMethod( - new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), mFileRequestBody); - - mPutMethod.setRetryOnConnectionFailure(false); - - if (mRequiredEtag != null && mRequiredEtag.length() > 0) { - mPutMethod.addRequestHeader(HttpConstants.IF_MATCH_HEADER, mRequiredEtag); - } - - mPutMethod.addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, String.valueOf(fileToUpload.length())); - mPutMethod.addRequestHeader(HttpConstants.OC_X_OC_MTIME_HEADER, mFileLastModifTimestamp); - - int status = client.executeHttpMethod(mPutMethod); - - if (isSuccess(status)) { - return new RemoteOperationResult<>(OK); - - } else { // synchronization failed - return new RemoteOperationResult<>(mPutMethod); - } - } - - public Set getDataTransferListeners() { - return mDataTransferListeners; - } - - public void addDatatransferProgressListener(OnDatatransferProgressListener listener) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.add(listener); - } - if (mFileRequestBody != null) { - mFileRequestBody.addDatatransferProgressListener(listener); - } - } - - public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.remove(listener); - } - if (mFileRequestBody != null) { - mFileRequestBody.removeDatatransferProgressListener(listener); - } - } - - public void cancel() { - synchronized (mCancellationRequested) { - mCancellationRequested.set(true); - if (mPutMethod != null) { - mPutMethod.abort(); - } - } - } - - public boolean isSuccess(int status) { - return ((status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_CREATED || - status == HttpConstants.HTTP_NO_CONTENT)); - } -} \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadRemoteFileOperation.java deleted file mode 100644 index 10b8ea36..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadRemoteFileOperation.java +++ /dev/null @@ -1,130 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.resources.files.chunks; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.http.methods.webdav.PutMethod; -import com.owncloud.android.lib.common.network.ChunkFromFileRequestBody; -import com.owncloud.android.lib.common.operations.OperationCancelledException; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation; -import okhttp3.MediaType; -import timber.log.Timber; - -import java.io.File; -import java.io.RandomAccessFile; -import java.net.URL; -import java.nio.channels.FileChannel; -import java.util.concurrent.TimeUnit; - -import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK; - -/** - * Remote operation performing the chunked upload of a remote file to the ownCloud server. - * - * @author David A. Velasco - * @author David González Verdugo - */ -public class ChunkedUploadRemoteFileOperation extends UploadRemoteFileOperation { - - public static final long CHUNK_SIZE = 1024000; - private static final int LAST_CHUNK_TIMEOUT = 900000; //15 mins. - - private String mTransferId; - - public ChunkedUploadRemoteFileOperation(String transferId, String localPath, String remotePath, String mimeType, - String requiredEtag, String fileLastModifTimestamp) { - super(localPath, remotePath, mimeType, requiredEtag, fileLastModifTimestamp); - mTransferId = transferId; - } - - @Override - protected RemoteOperationResult uploadFile(OwnCloudClient client) throws Exception { - int status; - RemoteOperationResult result = null; - FileChannel channel; - RandomAccessFile raf; - - File fileToUpload = new File(mLocalPath); - MediaType mediaType = MediaType.parse(mMimeType); - - raf = new RandomAccessFile(fileToUpload, "r"); - channel = raf.getChannel(); - - mFileRequestBody = new ChunkFromFileRequestBody(fileToUpload, mediaType, channel, CHUNK_SIZE); - - synchronized (mDataTransferListeners) { - mFileRequestBody.addDatatransferProgressListeners(mDataTransferListeners); - } - - long offset = 0; - String uriPrefix = client.getUploadsWebDavUri() + File.separator + mTransferId; - long totalLength = fileToUpload.length(); - long chunkCount = (long) Math.ceil((double) totalLength / CHUNK_SIZE); - - for (int chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++, offset += CHUNK_SIZE) { - - ((ChunkFromFileRequestBody) mFileRequestBody).setOffset(offset); - - if (mCancellationRequested.get()) { - result = new RemoteOperationResult<>(new OperationCancelledException()); - break; - } else { - mPutMethod = new PutMethod( - new URL(uriPrefix + File.separator + chunkIndex), mFileRequestBody); - - if (chunkIndex == chunkCount - 1) { - // Added a high timeout to the last chunk due to when the last chunk - // arrives to the server with the last PUT, all chunks get assembled - // within that PHP request, so last one takes longer. - mPutMethod.setReadTimeout(LAST_CHUNK_TIMEOUT, TimeUnit.MILLISECONDS); - } - - status = client.executeHttpMethod(mPutMethod); - - Timber.d("Upload of " + mLocalPath + " to " + mRemotePath + - ", chunk index " + chunkIndex + ", count " + chunkCount + - ", HTTP result status " + status); - - if (isSuccess(status)) { - result = new RemoteOperationResult<>(OK); - } else { - result = new RemoteOperationResult<>(mPutMethod); - break; - } - } - } - - if (channel != null) { - channel.close(); - } - - if (raf != null) { - raf.close(); - } - - return result; - } -} \ No newline at end of file From 7e564127487203fff04cf0723382648fc894fe79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 18 Jan 2022 09:23:26 +0100 Subject: [PATCH 46/62] Move content uri request body to a new file --- .../common/network/ContentUriRequestBody.kt | 63 +++++++++++++++++++ .../UploadFileFromContentUriOperation.kt | 43 ++----------- 2 files changed, 67 insertions(+), 39 deletions(-) create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt new file mode 100644 index 00000000..248d8527 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt @@ -0,0 +1,63 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.common.network; + +import android.content.ContentResolver +import android.net.Uri +import android.provider.OpenableColumns +import okhttp3.MediaType +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.RequestBody +import okio.BufferedSink +import okio.source +import java.io.IOException + +class ContentUriRequestBody( + private val contentResolver: ContentResolver, + private val contentUri: Uri +) : RequestBody() { + + override fun contentType(): MediaType? { + val contentType = contentResolver.getType(contentUri) ?: return null + return contentType.toMediaTypeOrNull() + } + + override fun contentLength(): Long { + contentResolver.query(contentUri, null, null, null, null)?.use { cursor -> + val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) + cursor.moveToFirst() + return cursor.getLong(sizeIndex) + } ?: return -1 + } + + override fun writeTo(sink: BufferedSink) { + val inputStream = contentResolver.openInputStream(contentUri) + ?: throw IOException("Couldn't open content URI for reading: $contentUri") + + inputStream.source().use { source -> + sink.writeAll(source) + } + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromContentUriOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromContentUriOperation.kt index dfcef34d..a2f2cb26 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromContentUriOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromContentUriOperation.kt @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2021 ownCloud GmbH. + * Copyright (C) 2022 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,22 +24,15 @@ package com.owncloud.android.lib.resources.files -import android.content.ContentResolver -import android.net.Uri -import android.provider.OpenableColumns import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.methods.webdav.PutMethod +import com.owncloud.android.lib.common.network.ContentUriRequestBody import com.owncloud.android.lib.common.network.WebdavUtils import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult -import okhttp3.MediaType -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody -import okio.BufferedSink -import okio.source +import com.owncloud.android.lib.common.utils.isOneOf import timber.log.Timber -import java.io.IOException import java.net.URL class UploadFileFromContentUriOperation( @@ -70,34 +63,6 @@ class UploadFileFromContentUriOperation( } fun isSuccess(status: Int): Boolean { - return status == HttpConstants.HTTP_OK || status == HttpConstants.HTTP_CREATED || status == HttpConstants.HTTP_NO_CONTENT - } -} - -class ContentUriRequestBody( - private val contentResolver: ContentResolver, - private val contentUri: Uri -) : RequestBody() { - - override fun contentType(): MediaType? { - val contentType = contentResolver.getType(contentUri) ?: return null - return contentType.toMediaTypeOrNull() - } - - override fun contentLength(): Long { - contentResolver.query(contentUri, null, null, null, null)?.use { cursor -> - val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) - cursor.moveToFirst() - return cursor.getLong(sizeIndex) - } ?: return -1 - } - - override fun writeTo(sink: BufferedSink) { - val inputStream = contentResolver.openInputStream(contentUri) - ?: throw IOException("Couldn't open content URI for reading: $contentUri") - - inputStream.source().use { source -> - sink.writeAll(source) - } + return status.isOneOf(HttpConstants.HTTP_OK, HttpConstants.HTTP_CREATED, HttpConstants.HTTP_NO_CONTENT) } } From 12040a12612fdf61afdf4ce36bb106afe9d86471 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 18 Jan 2022 13:02:52 +0100 Subject: [PATCH 47/62] Migrate old request bodies from java to kotlin --- .../network/ChunkFromFileRequestBody.java | 114 ----------------- .../network/ChunkFromFileRequestBody.kt | 91 ++++++++++++++ .../lib/common/network/FileRequestBody.java | 119 ------------------ .../lib/common/network/FileRequestBody.kt | 97 ++++++++++++++ .../UploadFileFromFileSystemOperation.kt | 2 +- 5 files changed, 189 insertions(+), 234 deletions(-) delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.java create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.kt delete mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.java create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.kt diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.java deleted file mode 100644 index a43b2c37..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.java +++ /dev/null @@ -1,114 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.common.network; - -import okhttp3.MediaType; -import okio.BufferedSink; -import timber.log.Timber; - -import java.io.File; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Iterator; - -/** - * A Request body that represents a file chunk and include information about the progress when uploading it - * - * @author David González Verdugo - */ -public class ChunkFromFileRequestBody extends FileRequestBody { - - private final FileChannel mChannel; - private final long mChunkSize; - private long mOffset; - private long mTransferred; - private ByteBuffer mBuffer = ByteBuffer.allocate(4096); - - public ChunkFromFileRequestBody(File file, MediaType contentType, FileChannel channel, long chunkSize) { - super(file, contentType); - if (channel == null) { - throw new IllegalArgumentException("File may not be null"); - } - if (chunkSize <= 0) { - throw new IllegalArgumentException("Chunk size must be greater than zero"); - } - this.mChannel = channel; - this.mChunkSize = chunkSize; - mOffset = 0; - mTransferred = 0; - } - - @Override - public long contentLength() { - try { - return Math.min(mChunkSize, mChannel.size() - mChannel.position()); - } catch (IOException e) { - return mChunkSize; - } - } - - @Override - public void writeTo(BufferedSink sink) { - int readCount; - Iterator it; - - try { - mChannel.position(mOffset); - long size = mFile.length(); - if (size == 0) { - size = -1; - } - long maxCount = Math.min(mOffset + mChunkSize, mChannel.size()); - while (mChannel.position() < maxCount) { - - readCount = mChannel.read(mBuffer); - - int bytesToWriteInBuffer = (int) Math.min(readCount, mFile.length() - mTransferred); - sink.getBuffer().write(mBuffer.array(), 0, bytesToWriteInBuffer); - - sink.flush(); - - mBuffer.clear(); - if (mTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks - mTransferred += readCount; - } - synchronized (mDataTransferListeners) { - it = mDataTransferListeners.iterator(); - while (it.hasNext()) { - it.next().onTransferProgress(readCount, mTransferred, size, mFile.getAbsolutePath()); - } - } - } - - } catch (Exception exception) { - Timber.e(exception, "Transferred " + mTransferred + " bytes from a total of " + mFile.length()); - } - } - - public void setOffset(long offset) { - this.mOffset = offset; - } -} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.kt new file mode 100644 index 00000000..a1275bf9 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.kt @@ -0,0 +1,91 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.common.network + +import okhttp3.MediaType +import okio.BufferedSink +import timber.log.Timber +import java.io.File +import java.nio.ByteBuffer +import java.nio.channels.FileChannel + +/** + * A Request body that represents a file chunk and include information about the progress when uploading it + * + * @author David González Verdugo + */ +class ChunkFromFileRequestBody( + file: File, + contentType: MediaType?, + private val channel: FileChannel, + private val chunkSize: Long +) : FileRequestBody(file, contentType) { + + private var offset: Long = 0 + private var alreadyTransferred: Long = 0 + private val buffer = ByteBuffer.allocate(4_096) + + init { + require(chunkSize > 0) { "Chunk size must be greater than zero" } + } + + override fun contentLength(): Long { + return chunkSize.coerceAtMost(channel.size() - channel.position()) + } + + override fun writeTo(sink: BufferedSink) { + var readCount: Int + var iterator: Iterator + try { + channel.position(offset) + + val maxCount = (offset + chunkSize).coerceAtMost(channel.size()) + while (channel.position() < maxCount) { + readCount = channel.read(buffer) + val bytesToWriteInBuffer = readCount.toLong().coerceAtMost(file.length() - alreadyTransferred).toInt() + sink.buffer.write(buffer.array(), 0, bytesToWriteInBuffer) + sink.flush() + buffer.clear() + + if (alreadyTransferred < maxCount) { // condition to avoid accumulate progress for repeated chunks + alreadyTransferred += readCount.toLong() + } + + synchronized(dataTransferListeners) { + iterator = dataTransferListeners.iterator() + while (iterator.hasNext()) { + iterator.next().onTransferProgress(readCount.toLong(), alreadyTransferred, file.length(), file.absolutePath) + } + } + } + } catch (exception: Exception) { + Timber.e(exception, "Transferred " + alreadyTransferred + " bytes from a total of " + file.length()) + } + } + + fun setOffset(newOffset: Long) { + offset = newOffset + } + +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.java deleted file mode 100644 index 18746ba7..00000000 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.java +++ /dev/null @@ -1,119 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2020 ownCloud GmbH. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.common.network; - -import okhttp3.MediaType; -import okhttp3.RequestBody; -import okio.BufferedSink; -import okio.Okio; -import okio.Source; -import timber.log.Timber; - -import java.io.File; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; - -/** - * A Request body that represents a file and include information about the progress when uploading it - * - * @author David González Verdugo - */ -public class FileRequestBody extends RequestBody implements ProgressiveDataTransferer { - - final Set mDataTransferListeners = new HashSet<>(); - protected File mFile; - private MediaType mContentType; - - public FileRequestBody(File file, MediaType contentType) { - mFile = file; - mContentType = contentType; - } - - @Override - public boolean isOneShot() { - return true; - } - - @Override - public MediaType contentType() { - return mContentType; - } - - @Override - public long contentLength() { - return mFile.length(); - } - - @Override - public void writeTo(BufferedSink sink) { - Source source; - Iterator it; - try { - source = Okio.source(mFile); - - long transferred = 0; - long read; - - while ((read = source.read(sink.buffer(), 4096)) != -1) { - transferred += read; - sink.flush(); - synchronized (mDataTransferListeners) { - it = mDataTransferListeners.iterator(); - while (it.hasNext()) { - it.next().onTransferProgress(read, transferred, mFile.length(), mFile.getAbsolutePath()); - } - } - } - - Timber.d("File with name " + mFile.getName() + " and size " + mFile.length() + " written in request body"); - - } catch (Exception e) { - Timber.e(e); - } - } - - @Override - public void addDatatransferProgressListener(OnDatatransferProgressListener listener) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.add(listener); - } - } - - @Override - public void addDatatransferProgressListeners(Collection listeners) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.addAll(listeners); - } - } - - @Override - public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { - synchronized (mDataTransferListeners) { - mDataTransferListeners.remove(listener); - } - } -} \ No newline at end of file diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.kt new file mode 100644 index 00000000..fd5f8dd3 --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/FileRequestBody.kt @@ -0,0 +1,97 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2022 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ +package com.owncloud.android.lib.common.network + +import okhttp3.MediaType +import okhttp3.RequestBody +import okio.BufferedSink +import okio.Source +import okio.source +import timber.log.Timber +import java.io.File +import java.util.HashSet + +/** + * A Request body that represents a file and include information about the progress when uploading it + * + * @author David González Verdugo + */ +open class FileRequestBody( + val file: File, + private val contentType: MediaType?, +) : RequestBody(), ProgressiveDataTransferer { + + val dataTransferListeners: MutableSet = HashSet() + + override fun isOneShot(): Boolean = true + + override fun contentType(): MediaType? = contentType + + override fun contentLength(): Long = file.length() + + override fun writeTo(sink: BufferedSink) { + val source: Source + var it: Iterator + try { + source = file.source() + var transferred: Long = 0 + var read: Long + while (source.read(sink.buffer, BYTES_TO_READ).also { read = it } != -1L) { + transferred += read + sink.flush() + synchronized(dataTransferListeners) { + it = dataTransferListeners.iterator() + while (it.hasNext()) { + it.next().onTransferProgress(read, transferred, file.length(), file.absolutePath) + } + } + } + Timber.d("File with name ${file.name} and size ${file.length()} written in request body") + } catch (e: Exception) { + Timber.e(e) + } + } + + override fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) { + synchronized(dataTransferListeners) { + dataTransferListeners.add(listener) + } + } + + override fun addDatatransferProgressListeners(listeners: Collection) { + synchronized(dataTransferListeners) { + dataTransferListeners.addAll(listeners) + } + } + + override fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener) { + synchronized(dataTransferListeners) { + dataTransferListeners.remove(listener) + } + } + + companion object { + private const val BYTES_TO_READ = 4_096L + } +} diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt index e4caa869..e532c678 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt @@ -26,9 +26,9 @@ package com.owncloud.android.lib.resources.files import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.methods.webdav.PutMethod -import com.owncloud.android.lib.common.network.FileRequestBody import com.owncloud.android.lib.common.network.OnDatatransferProgressListener import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.network.FileRequestBody import com.owncloud.android.lib.common.operations.OperationCancelledException import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult From 1f499fb67d418f32871267d677e55015e42459b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 18 Jan 2022 13:11:02 +0100 Subject: [PATCH 48/62] Make requireEtag nullable --- .../lib/resources/files/UploadFileFromFileSystemOperation.kt | 4 ++-- .../files/chunks/ChunkedUploadFromFileSystemOperation.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt index e532c678..4a32403d 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt @@ -55,7 +55,7 @@ open class UploadFileFromFileSystemOperation( val remotePath: String, val mimeType: String, val lastModifiedTimestamp: String, - val requiredEtag: String, + val requiredEtag: String?, ) : RemoteOperation() { protected val cancellationRequested = AtomicBoolean(false) @@ -98,7 +98,7 @@ open class UploadFileFromFileSystemOperation( putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)), fileRequestBody!!).apply { setRetryOnConnectionFailure(false) - if (requiredEtag.isNotBlank()) { + if (!requiredEtag.isNullOrBlank()) { addRequestHeader(HttpConstants.IF_MATCH_HEADER, requiredEtag) } addRequestHeader(HttpConstants.OC_TOTAL_LENGTH_HEADER, fileToUpload.length().toString()) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt index cc42b6bf..a7268123 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt @@ -54,7 +54,7 @@ class ChunkedUploadFromFileSystemOperation( remotePath: String, mimeType: String, lastModifiedTimestamp: String, - requiredEtag: String, + requiredEtag: String?, ) : UploadFileFromFileSystemOperation( localPath = localPath, remotePath = remotePath, From dc7022c5316c13fdf4d4ab54a50d62ee9f041436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garci=CC=81a=20de=20Prada?= Date: Tue, 18 Jan 2022 13:50:03 +0100 Subject: [PATCH 49/62] Pleasure the ktlint --- .../android/lib/common/network/ContentUriRequestBody.kt | 2 +- .../lib/resources/files/UploadFileFromFileSystemOperation.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt index 248d8527..7f4b6aa4 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt @@ -22,7 +22,7 @@ * */ -package com.owncloud.android.lib.common.network; +package com.owncloud.android.lib.common.network import android.content.ContentResolver import android.net.Uri diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt index 4a32403d..cde54cdb 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt @@ -26,9 +26,9 @@ package com.owncloud.android.lib.resources.files import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.methods.webdav.PutMethod +import com.owncloud.android.lib.common.network.FileRequestBody import com.owncloud.android.lib.common.network.OnDatatransferProgressListener import com.owncloud.android.lib.common.network.WebdavUtils -import com.owncloud.android.lib.common.network.FileRequestBody import com.owncloud.android.lib.common.operations.OperationCancelledException import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult From ba37bce0e12d070a5a247d0e715f198765f028a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Thu, 26 May 2022 15:08:12 +0200 Subject: [PATCH 50/62] Add transfer listeners to content uri worker --- .../common/network/ContentUriRequestBody.kt | 70 ++++++++++++++++--- 1 file changed, 62 insertions(+), 8 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt index 7f4b6aa4..ca4e31a9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ContentUriRequestBody.kt @@ -31,13 +31,23 @@ import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody import okio.BufferedSink +import okio.Source import okio.source +import timber.log.Timber import java.io.IOException class ContentUriRequestBody( private val contentResolver: ContentResolver, private val contentUri: Uri -) : RequestBody() { +) : RequestBody(), ProgressiveDataTransferer { + + private val dataTransferListeners: MutableSet = HashSet() + + val fileSize: Long = contentResolver.query(contentUri, null, null, null, null)?.use { cursor -> + val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) + cursor.moveToFirst() + cursor.getLong(sizeIndex) + } ?: -1 override fun contentType(): MediaType? { val contentType = contentResolver.getType(contentUri) ?: return null @@ -45,19 +55,63 @@ class ContentUriRequestBody( } override fun contentLength(): Long { - contentResolver.query(contentUri, null, null, null, null)?.use { cursor -> - val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE) - cursor.moveToFirst() - return cursor.getLong(sizeIndex) - } ?: return -1 + return fileSize } override fun writeTo(sink: BufferedSink) { val inputStream = contentResolver.openInputStream(contentUri) ?: throw IOException("Couldn't open content URI for reading: $contentUri") - inputStream.source().use { source -> - sink.writeAll(source) + val previousTime = System.currentTimeMillis() + + sink.writeAndUpdateProgress(inputStream.source()) + inputStream.source().close() + + val laterTime = System.currentTimeMillis() + + Timber.d("Difference - ${laterTime - previousTime} milliseconds") + } + + private fun BufferedSink.writeAndUpdateProgress(source: Source) { + var iterator: Iterator + + try { + var totalBytesRead = 0L + var read: Long + while (source.read(this.buffer, BYTES_TO_READ).also { read = it } != -1L) { + totalBytesRead += read + this.flush() + synchronized(dataTransferListeners) { + iterator = dataTransferListeners.iterator() + while (iterator.hasNext()) { + iterator.next().onTransferProgress(read, totalBytesRead, fileSize, contentUri.toString()) + } + } + } + } catch (e: Exception) { + Timber.e(e) } } + + override fun addDatatransferProgressListener(listener: OnDatatransferProgressListener) { + synchronized(dataTransferListeners) { + dataTransferListeners.add(listener) + } + } + + override fun addDatatransferProgressListeners(listeners: MutableCollection) { + synchronized(dataTransferListeners) { + dataTransferListeners.addAll(listeners) + } + } + + override fun removeDatatransferProgressListener(listener: OnDatatransferProgressListener) { + synchronized(dataTransferListeners) { + dataTransferListeners.remove(listener) + } + } + + companion object { + private const val BYTES_TO_READ = 4_096L + } } From c36eedd481b996755e3299f63ff27430f6e43317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Thu, 2 Jun 2022 12:08:27 +0200 Subject: [PATCH 51/62] Return Unit when the upload operation succeeds --- .../lib/resources/files/UploadFileFromFileSystemOperation.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt index cde54cdb..3c33347e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt @@ -107,7 +107,7 @@ open class UploadFileFromFileSystemOperation( val status = client.executeHttpMethod(putMethod) return if (isSuccess(status)) { - RemoteOperationResult(ResultCode.OK) + RemoteOperationResult(ResultCode.OK).apply { data = Unit } } else { // synchronization failed RemoteOperationResult(putMethod) } From 79173af930901d91cc627adb9db07c0ed7a04a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Wed, 8 Jun 2022 17:08:24 +0200 Subject: [PATCH 52/62] Polish a little bit the code --- .../lib/common/network/ChunkFromFileRequestBody.kt | 3 ++- .../chunks/ChunkedUploadFromFileSystemOperation.kt | 14 ++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.kt index a1275bf9..b1404b9c 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/network/ChunkFromFileRequestBody.kt @@ -23,6 +23,7 @@ */ package com.owncloud.android.lib.common.network +import com.owncloud.android.lib.resources.files.chunks.ChunkedUploadFromFileSystemOperation.Companion.CHUNK_SIZE import okhttp3.MediaType import okio.BufferedSink import timber.log.Timber @@ -39,7 +40,7 @@ class ChunkFromFileRequestBody( file: File, contentType: MediaType?, private val channel: FileChannel, - private val chunkSize: Long + private val chunkSize: Long = CHUNK_SIZE ) : FileRequestBody(file, contentType) { private var offset: Long = 0 diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt index a7268123..b9b3a2d9 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt @@ -69,28 +69,27 @@ class ChunkedUploadFromFileSystemOperation( val fileToUpload = File(localPath) val mediaType: MediaType? = mimeType.toMediaTypeOrNull() - val raf: RandomAccessFile = RandomAccessFile(fileToUpload, MODE_READ_ONLY) + val raf = RandomAccessFile(fileToUpload, MODE_READ_ONLY) val channel: FileChannel = raf.channel - fileRequestBody = ChunkFromFileRequestBody(fileToUpload, mediaType, channel, CHUNK_SIZE).also { + val fileRequestBody = ChunkFromFileRequestBody(fileToUpload, mediaType, channel).also { synchronized(dataTransferListener) { it.addDatatransferProgressListeners(dataTransferListener) } } val uriPrefix = client.uploadsWebDavUri.toString() + File.separator + transferId val totalLength = fileToUpload.length() val chunkCount = ceil(totalLength.toDouble() / CHUNK_SIZE).toLong() - var chunkIndex = 0 var offset: Long = 0 - while (chunkIndex < chunkCount) { - (fileRequestBody as ChunkFromFileRequestBody).setOffset(offset) + for (chunkIndex in 0..chunkCount) { + fileRequestBody.setOffset(offset) if (cancellationRequested.get()) { result = RemoteOperationResult(OperationCancelledException()) break } else { - putMethod = PutMethod(URL(uriPrefix + File.separator + chunkIndex), fileRequestBody as ChunkFromFileRequestBody).apply { - if (chunkIndex.toLong() == chunkCount - 1) { + putMethod = PutMethod(URL(uriPrefix + File.separator + chunkIndex), fileRequestBody).apply { + if (chunkIndex == chunkCount - 1) { // Added a high timeout to the last chunk due to when the last chunk // arrives to the server with the last PUT, all chunks get assembled // within that PHP request, so last one takes longer. @@ -109,7 +108,6 @@ class ChunkedUploadFromFileSystemOperation( break } } - chunkIndex++ offset += CHUNK_SIZE } channel.close() From b25fbc46048ec0174fefd2acf3182af47e9f6207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Thu, 9 Jun 2022 11:25:37 +0200 Subject: [PATCH 53/62] Fix an error after rebasing with latest version --- .../lib/resources/files/UploadFileFromFileSystemOperation.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt index 3c33347e..a82b8d93 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt @@ -97,7 +97,7 @@ open class UploadFileFromFileSystemOperation( } putMethod = PutMethod(URL(client.userFilesWebDavUri.toString() + WebdavUtils.encodePath(remotePath)), fileRequestBody!!).apply { - setRetryOnConnectionFailure(false) + retryOnConnectionFailure = false if (!requiredEtag.isNullOrBlank()) { addRequestHeader(HttpConstants.IF_MATCH_HEADER, requiredEtag) } From 2b79175b5a0dc16f7ba98ff14fedc6692717094d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Thu, 16 Jun 2022 08:24:22 +0200 Subject: [PATCH 54/62] Migrate ReadRemoteFileOperation to kotlin. First step to keep git history --- .../{ReadRemoteFileOperation.java => ReadRemoteFileOperation.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/{ReadRemoteFileOperation.java => ReadRemoteFileOperation.kt} (100%) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt similarity index 100% rename from owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.java rename to owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt From 17821e5760454fd67c149da82540e39f32c4ee72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Thu, 16 Jun 2022 08:51:25 +0200 Subject: [PATCH 55/62] Migrate ReadRemoteFileOperation to kotlin. Second step to keep git history --- .../http/methods/webdav/PropfindMethod.kt | 2 +- .../files/ReadRemoteFileOperation.kt | 114 ++++++++---------- 2 files changed, 52 insertions(+), 64 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PropfindMethod.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PropfindMethod.kt index 6bce3472..6f987f1d 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PropfindMethod.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/methods/webdav/PropfindMethod.kt @@ -53,7 +53,7 @@ class PropfindMethod( depth = depth, reqProp = propertiesToRequest, listOfHeaders = super.getRequestHeadersAsHashMap(), - callback = { response: Response, hrefRelation: HrefRelation? -> + callback = { response: Response, hrefRelation: HrefRelation -> when (hrefRelation) { HrefRelation.MEMBER -> members.add(response) HrefRelation.SELF -> this.root = response diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt index 237ce877..644e65e5 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt @@ -23,21 +23,20 @@ */ package com.owncloud.android.lib.resources.files; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.accounts.AccountUtils; -import com.owncloud.android.lib.common.http.HttpConstants; -import com.owncloud.android.lib.common.http.methods.webdav.DavUtils; -import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import timber.log.Timber; - -import java.net.URL; -import java.util.concurrent.TimeUnit; - -import static com.owncloud.android.lib.common.http.methods.webdav.DavConstants.DEPTH_0; -import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK; +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.accounts.AccountUtils +import com.owncloud.android.lib.common.http.HttpConstants.HTTP_MULTI_STATUS +import com.owncloud.android.lib.common.http.HttpConstants.HTTP_OK +import com.owncloud.android.lib.common.http.methods.webdav.DavConstants.DEPTH_0 +import com.owncloud.android.lib.common.http.methods.webdav.DavUtils +import com.owncloud.android.lib.common.http.methods.webdav.PropfindMethod +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.isOneOf +import timber.log.Timber +import java.net.URL +import java.util.concurrent.TimeUnit /** * Remote operation performing the read a file from the ownCloud server. @@ -47,21 +46,7 @@ import static com.owncloud.android.lib.common.operations.RemoteOperationResult.R * @author David González Verdugo */ -public class ReadRemoteFileOperation extends RemoteOperation { - - private static final int SYNC_READ_TIMEOUT = 40000; - private static final int SYNC_CONNECTION_TIMEOUT = 5000; - - private String mRemotePath; - - /** - * Constructor - * - * @param remotePath Remote path of the file. - */ - public ReadRemoteFileOperation(String remotePath) { - mRemotePath = remotePath; - } +class ReadRemoteFileOperation(val remotePath: String) : RemoteOperation() { /** * Performs the read operation. @@ -69,41 +54,44 @@ public class ReadRemoteFileOperation extends RemoteOperation { * @param client Client object to communicate with the remote ownCloud server. */ @Override - protected RemoteOperationResult run(OwnCloudClient client) { - PropfindMethod propfind; - RemoteOperationResult result; - - /// take the duty of check the server for the current state of the file there + override fun run(client: OwnCloudClient): RemoteOperationResult { try { - // remote request - propfind = new PropfindMethod( - new URL(client.getUserFilesWebDavUri() + WebdavUtils.encodePath(mRemotePath)), - DEPTH_0, - DavUtils.getAllPropset()); - - propfind.setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS); - propfind.setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS); - final int status = client.executeHttpMethod(propfind); - - if (status == HttpConstants.HTTP_MULTI_STATUS - || status == HttpConstants.HTTP_OK) { - - final RemoteFile file = RemoteFile.Companion.getRemoteFileFromDav(propfind.getRoot(), - AccountUtils.getUserId(mAccount, mContext), mAccount.name); - - result = new RemoteOperationResult<>(OK); - result.setData(file); - - } else { - result = new RemoteOperationResult<>(propfind); - client.exhaustResponse(propfind.getResponseBodyAsStream()); + val propFind = PropfindMethod( + url = URL("${client.userFilesWebDavUri}${WebdavUtils.encodePath(remotePath)}"), + depth = DEPTH_0, + propertiesToRequest = DavUtils.allPropset + ).apply { + setReadTimeout(SYNC_READ_TIMEOUT, TimeUnit.SECONDS) + setConnectionTimeout(SYNC_CONNECTION_TIMEOUT, TimeUnit.SECONDS) } - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - Timber.e(e, "Synchronizing file %s", mRemotePath); - } + val status = client.executeHttpMethod(propFind) + Timber.i("Read remote file $remotePath with status ${propFind.statusCode}") - return result; + return if (isSuccess(status)) { + // TODO: Remove that !! + val remoteFile = RemoteFile.getRemoteFileFromDav( + propFind.root!!, + AccountUtils.getUserId(mAccount, mContext), mAccount.name + ) + + RemoteOperationResult(RemoteOperationResult.ResultCode.OK).apply { + data = remoteFile + } + } else { + RemoteOperationResult(propFind).also { + client.exhaustResponse(propFind.getResponseBodyAsStream()) + } + } + } catch (exception: Exception) { + return RemoteOperationResult(exception) + } } -} \ No newline at end of file + + private fun isSuccess(status: Int) = status.isOneOf(HTTP_MULTI_STATUS, HTTP_OK) + + companion object { + private const val SYNC_READ_TIMEOUT = 40_000L + private const val SYNC_CONNECTION_TIMEOUT = 5_000L + } +} From b59a4e6947c111b0f11004cb92e8b2d7eb18c5d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Thu, 16 Jun 2022 08:54:20 +0200 Subject: [PATCH 56/62] Read remote file function added to the file service --- .../android/lib/resources/files/services/FileService.kt | 4 ++++ .../files/services/implementation/OCFileService.kt | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt index 8b03f4b9..51a85c46 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/FileService.kt @@ -56,6 +56,10 @@ interface FileService : Service { targetRemotePath: String, ): RemoteOperationResult + fun readFile( + remotePath: String + ): RemoteOperationResult + fun refreshFolder( remotePath: String ): RemoteOperationResult> diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt index 37d258a0..32ac22ca 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/services/implementation/OCFileService.kt @@ -31,6 +31,7 @@ import com.owncloud.android.lib.resources.files.CreateRemoteFolderOperation import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation import com.owncloud.android.lib.resources.files.GetUrlToOpenInWebRemoteOperation import com.owncloud.android.lib.resources.files.MoveRemoteFileOperation +import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation import com.owncloud.android.lib.resources.files.RemoteFile import com.owncloud.android.lib.resources.files.RemoveRemoteFileOperation @@ -89,6 +90,13 @@ class OCFileService(override val client: OwnCloudClient) : FileService { targetRemotePath = targetRemotePath, ).execute(client) + override fun readFile( + remotePath: String + ): RemoteOperationResult = + ReadRemoteFileOperation( + remotePath = remotePath + ).execute(client) + override fun refreshFolder( remotePath: String ): RemoteOperationResult> = From 959cb7b015ff430238000c632b592430a5b83979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Fri, 24 Jun 2022 10:37:59 +0200 Subject: [PATCH 57/62] Retrieve Etag from successful upload --- .../files/UploadFileFromFileSystemOperation.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt index a82b8d93..93a2357a 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/UploadFileFromFileSystemOperation.kt @@ -39,7 +39,6 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull import timber.log.Timber import java.io.File import java.net.URL -import java.util.HashSet import java.util.concurrent.atomic.AtomicBoolean /** @@ -63,6 +62,8 @@ open class UploadFileFromFileSystemOperation( protected val dataTransferListener: MutableSet = HashSet() protected var fileRequestBody: FileRequestBody? = null + var etag: String = "" + override fun run(client: OwnCloudClient): RemoteOperationResult { var result: RemoteOperationResult try { @@ -107,6 +108,14 @@ open class UploadFileFromFileSystemOperation( val status = client.executeHttpMethod(putMethod) return if (isSuccess(status)) { + etag = WebdavUtils.getEtagFromResponse(putMethod) + // Get rid of extra quotas + etag = etag.replace("\"", "") + if (etag.isEmpty()) { + Timber.e("Could not read eTag from response uploading %s", localPath) + } else { + Timber.d("File uploaded successfully. New etag for file ${fileToUpload.name} is $etag") + } RemoteOperationResult(ResultCode.OK).apply { data = Unit } } else { // synchronization failed RemoteOperationResult(putMethod) From 2b27b9657c60ee17346afceee9ee696431501879 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote <57049315+JuancaG05@users.noreply.github.com> Date: Wed, 28 Sep 2022 13:23:58 +0200 Subject: [PATCH 58/62] Increased the chunk size to 10 MB --- .../files/chunks/ChunkedUploadFromFileSystemOperation.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt index b9b3a2d9..01a6b086 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/chunks/ChunkedUploadFromFileSystemOperation.kt @@ -116,7 +116,7 @@ class ChunkedUploadFromFileSystemOperation( } companion object { - const val CHUNK_SIZE = 1_024_000L + const val CHUNK_SIZE = 10_240_000L // 10 MB private const val LAST_CHUNK_TIMEOUT = 900_000 // 15 mins. } } From b7d3cc2687ca5829afe25f655c3be3ddbe0978d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Fri, 30 Sep 2022 10:01:25 +0200 Subject: [PATCH 59/62] Remove legacy owncloud version from the client. --- .../android/lib/common/OwnCloudClient.java | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java index e332b8fa..1281ec13 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/OwnCloudClient.java @@ -36,7 +36,6 @@ import com.owncloud.android.lib.common.http.HttpClient; import com.owncloud.android.lib.common.http.HttpConstants; import com.owncloud.android.lib.common.http.methods.HttpBaseMethod; import com.owncloud.android.lib.common.utils.RandomUtils; -import com.owncloud.android.lib.resources.status.OwnCloudVersion; import okhttp3.Cookie; import okhttp3.HttpUrl; import timber.log.Timber; @@ -59,7 +58,6 @@ public class OwnCloudClient extends HttpClient { private OwnCloudCredentials mCredentials = null; private int mInstanceNumber; private Uri mBaseUri; - private OwnCloudVersion mVersion = null; private OwnCloudAccount mAccount; private final ConnectionValidator mConnectionValidator; private Object mRequestMutex = new Object(); @@ -185,7 +183,7 @@ public class OwnCloudClient extends HttpClient { return (mCredentials instanceof OwnCloudAnonymousCredentials || mAccount == null) ? Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0) : Uri.parse(mBaseUri + WEBDAV_FILES_PATH_4_0 + AccountUtils.getUserId( - mAccount.getSavedAccount(), getContext() + mAccount.getSavedAccount(), getContext() ) ); } @@ -194,7 +192,7 @@ public class OwnCloudClient extends HttpClient { return mCredentials instanceof OwnCloudAnonymousCredentials ? Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0) : Uri.parse(mBaseUri + WEBDAV_UPLOADS_PATH_4_0 + AccountUtils.getUserId( - mAccount.getSavedAccount(), getContext() + mAccount.getSavedAccount(), getContext() ) ); } @@ -241,14 +239,6 @@ public class OwnCloudClient extends HttpClient { HttpUrl.parse(mBaseUri.toString())); } - public OwnCloudVersion getOwnCloudVersion() { - return mVersion; - } - - public void setOwnCloudVersion(OwnCloudVersion version) { - mVersion = version; - } - public OwnCloudAccount getAccount() { return mAccount; } @@ -260,4 +250,4 @@ public class OwnCloudClient extends HttpClient { public void setFollowRedirects(boolean followRedirects) { this.mFollowRedirects = followRedirects; } -} \ No newline at end of file +} From 1ecb8020b149280530c3e078c66fbf71d2400baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Tue, 4 Oct 2022 08:10:54 +0200 Subject: [PATCH 60/62] Remove legacy KEY_OC_VERSION constant --- .../lib/common/accounts/AccountUtils.java | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/accounts/AccountUtils.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/accounts/AccountUtils.java index 9db87f4b..535104df 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/accounts/AccountUtils.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/accounts/AccountUtils.java @@ -94,26 +94,6 @@ public class AccountUtils { return username; } - /** - * Get the stored server version corresponding to an OC account. - * - * @param account An OC account - * @param context Application context - * @return Version of the OC server, according to last check - */ - public static OwnCloudVersion getServerVersionForAccount(Account account, Context context) { - AccountManager ama = AccountManager.get(context); - OwnCloudVersion version = null; - try { - String versionString = ama.getUserData(account, Constants.KEY_OC_VERSION); - version = new OwnCloudVersion(versionString); - - } catch (Exception e) { - Timber.e(e, "Couldn't get a the server version for an account"); - } - return version; - } - /** * @return * @throws IOException @@ -209,11 +189,6 @@ public class AccountUtils { } public static class Constants { - /** - * Version should be 3 numbers separated by dot so it can be parsed by - * {@link OwnCloudVersion} - */ - public static final String KEY_OC_VERSION = "oc_version"; /** * Base url should point to owncloud installation without trailing / ie: * http://server/path or https://owncloud.server From 9adadbddcc8fc7d289122402692e3e7f48ec0220 Mon Sep 17 00:00:00 2001 From: Juan Carlos Garrote Date: Wed, 2 Nov 2022 12:49:30 +0100 Subject: [PATCH 61/62] Fix lint reports --- .../android/lib/resources/files/ReadRemoteFileOperation.kt | 2 +- .../java/com/owncloud/android/lib/resources/files/RemoteFile.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt index 644e65e5..539ea92c 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/ReadRemoteFileOperation.kt @@ -21,7 +21,7 @@ * THE SOFTWARE. * */ -package com.owncloud.android.lib.resources.files; +package com.owncloud.android.lib.resources.files import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.accounts.AccountUtils diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt index 715bdf1e..87a6663e 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/files/RemoteFile.kt @@ -43,10 +43,10 @@ import at.bitfire.dav4jvm.property.QuotaUsedBytes import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.http.HttpConstants import com.owncloud.android.lib.common.http.methods.webdav.properties.OCShareTypes +import com.owncloud.android.lib.common.utils.isOneOf import com.owncloud.android.lib.resources.shares.ShareType import com.owncloud.android.lib.resources.shares.ShareType.Companion.fromValue import kotlinx.parcelize.Parcelize -import com.owncloud.android.lib.common.utils.isOneOf import okhttp3.HttpUrl import timber.log.Timber import java.io.File From f9bc792ded0f52f46491c36fd0ee49b9e9213fc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abel=20Garc=C3=ADa=20de=20Prada?= Date: Mon, 14 Nov 2022 09:32:14 +0100 Subject: [PATCH 62/62] Do data field not mandatory on ocs response --- .../lib/resources/CommonOcsResponse.kt | 2 +- .../shares/GetRemoteShareesOperation.kt | 6 ++-- .../shares/responses/ShareeResponseTest.kt | 34 +++++++++---------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/CommonOcsResponse.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/CommonOcsResponse.kt index 7a37dc82..321d06f6 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/CommonOcsResponse.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/CommonOcsResponse.kt @@ -36,7 +36,7 @@ data class CommonOcsResponse( @JsonClass(generateAdapter = true) data class OCSResponse( val meta: MetaData, - val data: T + val data: T? ) @JsonClass(generateAdapter = true) diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareesOperation.kt b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareesOperation.kt index 10154820..a03cac72 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareesOperation.kt +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/resources/shares/GetRemoteShareesOperation.kt @@ -99,11 +99,11 @@ class GetRemoteShareesOperation .appendQueryParameter(PARAM_PER_PAGE, perPage.toString()) .build() - private fun parseResponse(response: String): ShareeOcsResponse? { + private fun parseResponse(response: String?): ShareeOcsResponse? { val moshi = Moshi.Builder().build() val type: Type = Types.newParameterizedType(CommonOcsResponse::class.java, ShareeOcsResponse::class.java) val adapter: JsonAdapter> = moshi.adapter(type) - return adapter.fromJson(response)!!.ocs.data + return response?.let { adapter.fromJson(it)?.ocs?.data } } private fun onResultUnsuccessful( @@ -123,7 +123,7 @@ class GetRemoteShareesOperation private fun onRequestSuccessful(response: String?): RemoteOperationResult { val result = RemoteOperationResult(OK) Timber.d("Successful response: $response") - result.data = parseResponse(response!!) + result.data = parseResponse(response) Timber.d("*** Get Users or groups completed ") return result } diff --git a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt index 374429c3..11c51764 100644 --- a/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt +++ b/owncloudComLibrary/src/test/java/com/owncloud/android/lib/resources/shares/responses/ShareeResponseTest.kt @@ -63,31 +63,31 @@ class ShareeResponseTest { @Test fun `example response - ok - correct sturcture`() { val response = loadResponses(EXAMPLE_RESPONSE_JSON)!! - assertEquals(2, response.ocs.data.groups.size) - assertEquals(0, response.ocs.data.remotes.size) - assertEquals(2, response.ocs.data.users.size) - assertEquals(0, response.ocs.data.exact?.groups?.size) - assertEquals(0, response.ocs.data.exact?.remotes?.size) - assertEquals(1, response.ocs.data.exact?.users?.size) - assertEquals("user1@user1.com", response.ocs.data.users.get(0).value.additionalInfo) - assertNull(response.ocs.data.users[1].value.additionalInfo) + assertEquals(2, response.ocs.data?.groups?.size) + assertEquals(0, response.ocs.data?.remotes?.size) + assertEquals(2, response.ocs.data?.users?.size) + assertEquals(0, response.ocs.data?.exact?.groups?.size) + assertEquals(0, response.ocs.data?.exact?.remotes?.size) + assertEquals(1, response.ocs.data?.exact?.users?.size) + assertEquals("user1@user1.com", response.ocs.data?.users?.get(0)?.value?.additionalInfo) + assertNull(response.ocs.data?.users?.get(1)?.value?.additionalInfo) } @Test fun `check empty response - ok - parsing ok`() { val response = loadResponses(EMPTY_RESPONSE_JSON)!! - assertTrue(response.ocs.data.exact?.groups?.isEmpty()!!) - assertTrue(response.ocs.data.exact?.remotes?.isEmpty()!!) - assertTrue(response.ocs.data.exact?.users?.isEmpty()!!) - assertTrue(response.ocs.data.groups.isEmpty()) - assertTrue(response.ocs.data.remotes.isEmpty()) - assertTrue(response.ocs.data.users.isEmpty()) + assertTrue(response.ocs.data?.exact?.groups?.isEmpty()!!) + assertTrue(response.ocs.data?.exact?.remotes?.isEmpty()!!) + assertTrue(response.ocs.data?.exact?.users?.isEmpty()!!) + assertTrue(response.ocs.data?.groups?.isEmpty()!!) + assertTrue(response.ocs.data?.remotes?.isEmpty()!!) + assertTrue(response.ocs.data?.users?.isEmpty()!!) } companion object { - val RESOURCES_PATH = + private const val RESOURCES_PATH = "src/test/responses/com.owncloud.android.lib.resources.sharees.responses" - val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/example_sharee_response.json" - val EMPTY_RESPONSE_JSON = "$RESOURCES_PATH/empty_sharee_response.json" + const val EXAMPLE_RESPONSE_JSON = "$RESOURCES_PATH/example_sharee_response.json" + const val EMPTY_RESPONSE_JSON = "$RESOURCES_PATH/empty_sharee_response.json" } }