From f6c11edf7d5c6234e2e1f64c50029c1cc738da1e Mon Sep 17 00:00:00 2001 From: A117870935 Date: Mon, 2 Jun 2025 18:47:20 +0530 Subject: [PATCH 01/15] Album remote operations. Signed-off-by: A117870935 Signed-off-by: Surinder Kumar # Conflicts: # library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt --- .../lib/common/network/WebdavUtils.java | 15 ++ .../lib/common/utils/WebDavFileUtils.java | 28 +++ .../CopyFileToAlbumRemoteOperation.java | 159 ++++++++++++++++++ .../albums/CreateNewAlbumRemoteOperation.java | 72 ++++++++ .../lib/resources/albums/PhotoAlbumEntry.kt | 106 ++++++++++++ .../albums/ReadAlbumItemsRemoteOperation.java | 99 +++++++++++ .../albums/ReadAlbumsRemoteOperation.java | 99 +++++++++++ .../RemoveAlbumFileRemoteOperation.java | 62 +++++++ .../albums/RemoveAlbumRemoteOperation.java | 64 +++++++ .../albums/RenameAlbumRemoteOperation.java | 72 ++++++++ .../ToggleAlbumFavoriteRemoteOperation.java | 80 +++++++++ 11 files changed, 856 insertions(+) create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.java create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.java create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.java create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.java create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.java create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.java create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.java create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.java diff --git a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java index 650655519a..bec0baefb0 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java +++ b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java @@ -1,6 +1,7 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors * SPDX-FileCopyrightText: 2023 Alper Ozturk * SPDX-FileCopyrightText: 2022 Álvaro Brey @@ -224,6 +225,20 @@ public static DavPropertyNameSet getChunksPropSet() { return propSet; } + public static DavPropertyNameSet getAlbumPropSet() { + DavPropertyNameSet propertySet = new DavPropertyNameSet(); + Namespace ncNamespace = Namespace.getNamespace("nc",WebdavEntry.NAMESPACE_NC); + + propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_LAST_PHOTO,ncNamespace)); + propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_NB_ITEMS, ncNamespace)); + propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_LOCATION, ncNamespace)); + propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_DATE_RANGE, ncNamespace)); + propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_COLLABORATORS, ncNamespace)); + + return propertySet; + } + + /** * * @param rawEtag diff --git a/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java b/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java index bb19408b84..80bc1c511c 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java +++ b/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java @@ -1,12 +1,15 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors * SPDX-FileCopyrightText: 2017 Mario Danic * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.common.utils; +import android.net.Uri; + import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.network.WebdavEntry; import com.owncloud.android.lib.resources.files.model.RemoteFile; @@ -59,4 +62,29 @@ public ArrayList readData(MultiStatus remoteData, return mFolderAndFiles; } + + /** + * Read the data retrieved from the server about the contents of the target folder + * + * @param remoteData Full response got from the server with the data of the target + * folder and its direct children. + * @param client Client instance to the remote server where the data were + * retrieved. + * @return + */ + public ArrayList readAlbumData(MultiStatus remoteData, OwnCloudClient client) { + String url = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId(); + + ArrayList mFolderAndFiles = new ArrayList<>(); + + // loop to update every child + // reading from 1 as 0th item will be just the root album path + for (int i = 1; i < remoteData.getResponses().length; ++i) { + /// new OCFile instance with the data from the server + WebdavEntry we = new WebdavEntry(remoteData.getResponses()[i], Uri.parse(url).getEncodedPath()); + RemoteFile remoteFile = new RemoteFile(we); + mFolderAndFiles.add(remoteFile); + } + return mFolderAndFiles; + } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.java new file mode 100644 index 0000000000..15e23a9e1e --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.java @@ -0,0 +1,159 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.lib.resources.albums; + +import android.util.Log; + +import com.nextcloud.common.SessionTimeOut; +import com.nextcloud.common.SessionTimeOutKt; +import com.owncloud.android.lib.common.OwnCloudClient; +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 org.apache.commons.httpclient.HttpStatus; +import org.apache.jackrabbit.webdav.DavException; +import org.apache.jackrabbit.webdav.MultiStatusResponse; +import org.apache.jackrabbit.webdav.Status; +import org.apache.jackrabbit.webdav.client.methods.CopyMethod; + +import java.io.IOException; + + +/** + * 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. + */ +public class CopyFileToAlbumRemoteOperation extends RemoteOperation { + private static final String TAG = CopyFileToAlbumRemoteOperation.class.getSimpleName(); + + private final String mSrcRemotePath; + private final String mTargetRemotePath; + + private final SessionTimeOut sessionTimeOut; + + public CopyFileToAlbumRemoteOperation(String srcRemotePath, String targetRemotePath) { + this(srcRemotePath, targetRemotePath, SessionTimeOutKt.getDefaultSessionTimeOut()); + } + + public CopyFileToAlbumRemoteOperation(String srcRemotePath, String targetRemotePath, SessionTimeOut sessionTimeOut) { + mSrcRemotePath = srcRemotePath; + mTargetRemotePath = targetRemotePath; + this.sessionTimeOut = sessionTimeOut; + } + + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Override + protected RemoteOperationResult run(OwnCloudClient client) { + + /// check parameters + if (mTargetRemotePath.equals(mSrcRemotePath)) { + // nothing to do! + return new RemoteOperationResult<>(ResultCode.OK); + } + + if (mTargetRemotePath.startsWith(mSrcRemotePath)) { + return new RemoteOperationResult<>(ResultCode.INVALID_COPY_INTO_DESCENDANT); + } + + /// perform remote operation + CopyMethod copyMethod = null; + RemoteOperationResult result; + try { + copyMethod = new CopyMethod( + client.getFilesDavUri(this.mSrcRemotePath), + client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(mTargetRemotePath), + false + ); + int status = client.executeMethod(copyMethod, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); + + /// process response + if (status == HttpStatus.SC_MULTI_STATUS) { + result = processPartialError(copyMethod); + + } else if (status == HttpStatus.SC_PRECONDITION_FAILED) { + + result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE); + client.exhaustResponse(copyMethod.getResponseBodyAsStream()); + + } else { + result = new RemoteOperationResult<>(isSuccess(status), copyMethod); + client.exhaustResponse(copyMethod.getResponseBodyAsStream()); + } + + Log.i(TAG, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage()); + + } catch (Exception e) { + result = new RemoteOperationResult<>(e); + Log.e(TAG, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage(), e); + + } finally { + if (copyMethod != null) { + copyMethod.releaseConnection(); + } + } + + return result; + } + + + /** + * Analyzes a multistatus response from the OC server to generate an appropriate result. + *

+ * In WebDAV, a COPY request on collections (folders) can be PARTIALLY successful: some + * children are copied, some other aren't. + *

+ * According to the WebDAV specification, a multistatus response SHOULD NOT include partial + * successes (201, 204) nor for descendants of already failed children (424) in the response + * entity. But SHOULD NOT != MUST NOT, so take carefully. + * + * @param copyMethod Copy operation just finished with a multistatus response + * @return A result for the {@link CopyFileToAlbumRemoteOperation} caller + * @throws java.io.IOException If the response body could not be parsed + * @throws org.apache.jackrabbit.webdav.DavException If the status code is other than MultiStatus or if obtaining + * the response XML document fails + */ + private RemoteOperationResult processPartialError(CopyMethod copyMethod) + throws IOException, DavException { + // Adding a list of failed descendants to the result could be interesting; or maybe not. + // For the moment, let's take the easy way. + + /// check that some error really occurred + MultiStatusResponse[] responses = copyMethod.getResponseBodyAsMultiStatus().getResponses(); + Status[] status; + boolean failFound = false; + for (int i = 0; i < responses.length && !failFound; i++) { + status = responses[i].getStatus(); + failFound = ( + status != null && + status.length > 0 && + status[0].getStatusCode() > 299 + ); + } + + RemoteOperationResult result; + if (failFound) { + result = new RemoteOperationResult<>(ResultCode.PARTIAL_COPY_DONE); + } else { + result = new RemoteOperationResult<>(true, copyMethod); + } + + return result; + } + + protected boolean isSuccess(int status) { + return status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT; + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.java new file mode 100644 index 0000000000..dbaa5d5cf7 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.java @@ -0,0 +1,72 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.lib.resources.albums; + +import com.nextcloud.common.SessionTimeOut; +import com.nextcloud.common.SessionTimeOutKt; +import com.owncloud.android.lib.common.OwnCloudClient; +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.Log_OC; + +import org.apache.jackrabbit.webdav.client.methods.MkColMethod; + +public class CreateNewAlbumRemoteOperation extends RemoteOperation { + private static final String TAG = CreateNewAlbumRemoteOperation.class.getSimpleName(); + private final String newAlbumName; + private final SessionTimeOut sessionTimeOut; + + public CreateNewAlbumRemoteOperation(String newAlbumName) { + this(newAlbumName, SessionTimeOutKt.getDefaultSessionTimeOut()); + } + + public CreateNewAlbumRemoteOperation(String newAlbumName, SessionTimeOut sessionTimeOut) { + this.newAlbumName = newAlbumName; + this.sessionTimeOut = sessionTimeOut; + } + + public String getNewAlbumName() { + return newAlbumName; + } + + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Override + protected RemoteOperationResult run(OwnCloudClient client) { + MkColMethod mkCol = null; + RemoteOperationResult result; + try { + mkCol = new MkColMethod(client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(newAlbumName)); + client.executeMethod(mkCol, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); + if (405 == mkCol.getStatusCode()) { + result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS); + } else { + result = new RemoteOperationResult<>(mkCol.succeeded(), mkCol); + result.setResultData(null); + } + + Log_OC.d(TAG, "Create album " + newAlbumName + ": " + result.getLogMessage()); + client.exhaustResponse(mkCol.getResponseBodyAsStream()); + } catch (Exception e) { + result = new RemoteOperationResult<>(e); + Log_OC.e(TAG, "Create album " + newAlbumName + ": " + result.getLogMessage(), e); + } finally { + if (mkCol != null) { + mkCol.releaseConnection(); + } + + } + + return result; + } + +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt new file mode 100644 index 0000000000..2204c24af8 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt @@ -0,0 +1,106 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.lib.resources.albums + +import com.owncloud.android.lib.common.network.WebdavEntry +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.MultiStatusResponse +import org.apache.jackrabbit.webdav.property.DavPropertyName +import org.apache.jackrabbit.webdav.property.DavPropertySet +import org.apache.jackrabbit.webdav.xml.Namespace +import org.json.JSONException +import org.json.JSONObject +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale + +class PhotoAlbumEntry(response: MultiStatusResponse) { + val href: String + val lastPhoto: Long + val nbItems: Int + val location: String? + private val dateRange: String? + + companion object { + val dateFormat = SimpleDateFormat("MMM yyyy", Locale.US) + } + + init { + + href = response.href + + val properties = response.getProperties(HttpStatus.SC_OK) + + this.lastPhoto = parseLong(parseString(properties, WebdavEntry.PROPERTY_LAST_PHOTO)) + this.nbItems = parseInt(parseString(properties, WebdavEntry.PROPERTY_NB_ITEMS)) + this.location = parseString(properties, WebdavEntry.PROPERTY_LOCATION) + this.dateRange = parseString(properties, WebdavEntry.PROPERTY_DATE_RANGE) + } + + private fun parseString(props: DavPropertySet, name: String): String? { + val propName = DavPropertyName.create(name, Namespace.getNamespace("nc", WebdavEntry.NAMESPACE_NC)) + val prop = props[propName] + return if (prop != null && prop.value != null) prop.value.toString() else null + } + + private fun parseInt(value: String?): Int { + return try { + value?.toInt() ?: 0 + } catch (e: NumberFormatException) { + 0 + } + } + + private fun parseLong(value: String?): Long { + return try { + value?.toLong() ?: 0L + } catch (e: NumberFormatException) { + 0L + } + } + + val albumName: String + get() { + var href = href + if (href.isEmpty()) { + return "" + } + + // Remove trailing slash if present + if (href.endsWith("/")) { + href = href.substring(0, href.length - 1) + } + + // Split and return last part + val parts = href.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() + return if (parts.isNotEmpty()) parts[parts.size - 1] else "" + } + + val createdDate: String + get() { + val jsonRange = dateRange + val currentDate = Date(System.currentTimeMillis()) + if (jsonRange.isNullOrEmpty()) { + return dateFormat.format(currentDate) + } + + try { + val obj = JSONObject(jsonRange) + val startTimestamp = obj.optLong("start", 0) + + if (startTimestamp > 0) { + val date = Date(startTimestamp * 1000L) // Convert to milliseconds + return dateFormat.format(date) + } + } catch (e: JSONException) { + e.printStackTrace() + return dateFormat.format(currentDate) + } + return dateFormat.format(currentDate) + } +} \ No newline at end of file diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.java new file mode 100644 index 0000000000..9e267ff5e4 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.java @@ -0,0 +1,99 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.lib.resources.albums; + +import com.nextcloud.common.SessionTimeOut; +import com.nextcloud.common.SessionTimeOutKt; +import com.owncloud.android.lib.common.OwnCloudClient; +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.Log_OC; +import com.owncloud.android.lib.common.utils.WebDavFileUtils; +import com.owncloud.android.lib.resources.files.model.RemoteFile; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.jackrabbit.webdav.DavConstants; +import org.apache.jackrabbit.webdav.MultiStatus; +import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; + +import java.util.ArrayList; +import java.util.List; + +public class ReadAlbumItemsRemoteOperation extends RemoteOperation> { + + private static final String TAG = ReadAlbumItemsRemoteOperation.class.getSimpleName(); + private final String mRemotePath; + private final SessionTimeOut sessionTimeOut; + + public ReadAlbumItemsRemoteOperation(String mRemotePath) { + this(mRemotePath, SessionTimeOutKt.getDefaultSessionTimeOut()); + } + + public ReadAlbumItemsRemoteOperation(String mRemotePath, SessionTimeOut sessionTimeOut) { + this.mRemotePath = mRemotePath; + this.sessionTimeOut = sessionTimeOut; + } + + protected RemoteOperationResult> run(OwnCloudClient client) { + RemoteOperationResult> result = null; + PropFindMethod query = null; + String url = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(mRemotePath); + try { + // remote request + query = new PropFindMethod(url, + WebdavUtils.getAllPropSet(), // PropFind Properties + DavConstants.DEPTH_1); + int status = client.executeMethod(query, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); + + // check and process response + boolean isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK); + + if (isSuccess) { + // get data from remote folder + MultiStatus dataInServer = query.getResponseBodyAsMultiStatus(); + ArrayList mFolderAndFiles = new WebDavFileUtils().readAlbumData(dataInServer, client); + + // Result of the operation + result = new RemoteOperationResult<>(true, query); + // Add data to the result + if (result.isSuccess()) { + result.setResultData(mFolderAndFiles); + } + } else { + // synchronization failed + client.exhaustResponse(query.getResponseBodyAsStream()); + result = new RemoteOperationResult<>(false, query); + } + } catch (Exception e) { + result = new RemoteOperationResult<>(e); + } finally { + if (query != null) { + query.releaseConnection(); // let the connection available for other methods + } + + if (result == null) { + result = new RemoteOperationResult(new Exception("unknown error")); + Log_OC.e(TAG, "Synchronized " + mRemotePath + ": failed"); + } else { + if (result.isSuccess()) { + Log_OC.i(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage()); + } else { + if (result.isException()) { + Log_OC.e(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage(), + result.getException()); + } else { + Log_OC.e(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage()); + } + } + } + } + + return result; + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.java new file mode 100644 index 0000000000..2a09e243d4 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.java @@ -0,0 +1,99 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.lib.resources.albums; + +import static org.apache.commons.httpclient.HttpStatus.SC_MULTI_STATUS; +import static org.apache.commons.httpclient.HttpStatus.SC_OK; + +import android.text.TextUtils; + +import androidx.annotation.Nullable; + +import com.nextcloud.common.SessionTimeOut; +import com.nextcloud.common.SessionTimeOutKt; +import com.owncloud.android.lib.common.OwnCloudClient; +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.Log_OC; + +import org.apache.jackrabbit.webdav.DavConstants; +import org.apache.jackrabbit.webdav.MultiStatus; +import org.apache.jackrabbit.webdav.MultiStatusResponse; +import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; + +import java.util.ArrayList; +import java.util.List; + +public class ReadAlbumsRemoteOperation extends RemoteOperation> { + + private static final String TAG = ReadAlbumsRemoteOperation.class.getSimpleName(); + private final String mAlbumRemotePath; + private final SessionTimeOut sessionTimeOut; + + + public ReadAlbumsRemoteOperation() { + this(null); + } + + public ReadAlbumsRemoteOperation(@Nullable String mAlbumRemotePath) { + this(mAlbumRemotePath, SessionTimeOutKt.getDefaultSessionTimeOut()); + } + + public ReadAlbumsRemoteOperation(@Nullable String mAlbumRemotePath, SessionTimeOut sessionTimeOut) { + this.mAlbumRemotePath = mAlbumRemotePath; + this.sessionTimeOut = sessionTimeOut; + } + + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Override + protected RemoteOperationResult> run(OwnCloudClient client) { + PropFindMethod propfind = null; + RemoteOperationResult> result; + String url = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums"; + if (!TextUtils.isEmpty(mAlbumRemotePath)) { + url += WebdavUtils.encodePath(mAlbumRemotePath); + } + try { + propfind = new PropFindMethod(url, WebdavUtils.getAlbumPropSet(), DavConstants.DEPTH_1); + int status = client.executeMethod(propfind, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); + boolean isSuccess = status == SC_MULTI_STATUS || status == SC_OK; + if (isSuccess) { + MultiStatus multiStatus = propfind.getResponseBodyAsMultiStatus(); + List albumsList = new ArrayList<>(); + for (MultiStatusResponse response : multiStatus.getResponses()) { + int st = response.getStatus()[0].getStatusCode(); + if (st == SC_OK) { + PhotoAlbumEntry entry = new PhotoAlbumEntry(response); + albumsList.add(entry); + } + } + result = new RemoteOperationResult(true, propfind); + result.setResultData(albumsList); + } else { + result = new RemoteOperationResult(false, propfind); + client.exhaustResponse(propfind.getResponseBodyAsStream()); + } + } catch (Exception var13) { + Exception e = var13; + result = new RemoteOperationResult(e); + Log_OC.e(TAG, "Read album " + " failed: " + result.getLogMessage(), result.getException()); + } finally { + if (propfind != null) { + propfind.releaseConnection(); + } + + } + + return result; + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.java new file mode 100644 index 0000000000..52296a5651 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.java @@ -0,0 +1,62 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.lib.resources.albums; + +import android.net.Uri; + +import com.nextcloud.common.SessionTimeOut; +import com.nextcloud.common.SessionTimeOutKt; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; + +public class RemoveAlbumFileRemoteOperation extends RemoteOperation { + private static final String TAG = RemoveFileRemoteOperation.class.getSimpleName(); + private final String mRemotePath; + private final SessionTimeOut sessionTimeOut; + + public RemoveAlbumFileRemoteOperation(String remotePath) { + this(remotePath, SessionTimeOutKt.getDefaultSessionTimeOut()); + } + + public RemoveAlbumFileRemoteOperation(String remotePath, SessionTimeOut sessionTimeOut) { + this.mRemotePath = remotePath; + this.sessionTimeOut = sessionTimeOut; + } + + protected RemoteOperationResult run(OwnCloudClient client) { + RemoteOperationResult result; + DeleteMethod delete = null; + String webDavUrl = client.getDavUri().toString()+"/photos/"; + String encodedPath = (client.getUserId() + Uri.encode(this.mRemotePath)).replace("%2F", "/"); + String fullFilePath = webDavUrl + encodedPath; + + try { + delete = new DeleteMethod(fullFilePath); + int status = client.executeMethod(delete, this.sessionTimeOut.getReadTimeOut(), this.sessionTimeOut.getConnectionTimeOut()); + delete.getResponseBodyAsString(); + result = new RemoteOperationResult(delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, delete); + Log_OC.i(TAG, "Remove " + this.mRemotePath + ": " + result.getLogMessage()); + } catch (Exception e) { + result = new RemoteOperationResult(e); + Log_OC.e(TAG, "Remove " + this.mRemotePath + ": " + result.getLogMessage(), e); + } finally { + if (delete != null) { + delete.releaseConnection(); + } + + } + + return result; + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.java new file mode 100644 index 0000000000..f86e6fc919 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.java @@ -0,0 +1,64 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.lib.resources.albums; + +import com.nextcloud.common.SessionTimeOut; +import com.nextcloud.common.SessionTimeOutKt; +import com.owncloud.android.lib.common.OwnCloudClient; +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.Log_OC; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; + +public class RemoveAlbumRemoteOperation extends RemoteOperation { + private static final String TAG = RemoveAlbumRemoteOperation.class.getSimpleName(); + private final String albumName; + private final SessionTimeOut sessionTimeOut; + + public RemoveAlbumRemoteOperation(String albumName) { + this(albumName, SessionTimeOutKt.getDefaultSessionTimeOut()); + } + + public RemoveAlbumRemoteOperation(String albumName, SessionTimeOut sessionTimeOut) { + this.albumName = albumName; + this.sessionTimeOut = sessionTimeOut; + } + + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Override + protected RemoteOperationResult run(OwnCloudClient client) { + RemoteOperationResult result; + DeleteMethod delete = null; + + try { + delete = new DeleteMethod(client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(albumName)); + int status = client.executeMethod(delete, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); + delete.getResponseBodyAsString(); + result = new RemoteOperationResult(delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, delete); + Log_OC.i(TAG, "Remove " + this.albumName + ": " + result.getLogMessage()); + } catch (Exception e) { + result = new RemoteOperationResult(e); + Log_OC.e(TAG, "Remove " + this.albumName + ": " + result.getLogMessage(), e); + } finally { + if (delete != null) { + delete.releaseConnection(); + } + + } + + return result; + } + +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.java new file mode 100644 index 0000000000..7c17cb0324 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.java @@ -0,0 +1,72 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.lib.resources.albums; + +import com.nextcloud.common.SessionTimeOut; +import com.nextcloud.common.SessionTimeOutKt; +import com.owncloud.android.lib.common.OwnCloudClient; +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.Log_OC; + +import org.apache.jackrabbit.webdav.client.methods.MoveMethod; + +public class RenameAlbumRemoteOperation extends RemoteOperation { + private static final String TAG = RenameAlbumRemoteOperation.class.getSimpleName(); + private final String mOldRemotePath; + private final String mNewAlbumName; + private final SessionTimeOut sessionTimeOut; + + public RenameAlbumRemoteOperation(String mOldRemotePath, String mNewAlbumName) { + this(mOldRemotePath, mNewAlbumName, SessionTimeOutKt.getDefaultSessionTimeOut()); + } + + public RenameAlbumRemoteOperation(String mOldRemotePath, String mNewAlbumName, SessionTimeOut sessionTimeOut) { + this.mOldRemotePath = mOldRemotePath; + this.mNewAlbumName = mNewAlbumName; + this.sessionTimeOut = sessionTimeOut; + } + + public String getNewAlbumName() { + return mNewAlbumName; + } + + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Override + protected RemoteOperationResult run(OwnCloudClient client) { + RemoteOperationResult result = null; + MoveMethod move = null; + String url = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums"; + try { + if (!this.mNewAlbumName.equals(this.mOldRemotePath)) { + move = new MoveMethod(url + WebdavUtils.encodePath(mOldRemotePath), url + WebdavUtils.encodePath(mNewAlbumName), true); + client.executeMethod(move, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); + result = new RemoteOperationResult(move.succeeded(), move); + Log_OC.i(TAG, "Rename " + this.mOldRemotePath + " to " + this.mNewAlbumName + ": " + result.getLogMessage()); + client.exhaustResponse(move.getResponseBodyAsStream()); + return result; + } + } catch (Exception e) { + result = new RemoteOperationResult(e); + Log_OC.e(TAG, "Rename " + this.mOldRemotePath + " to " + this.mNewAlbumName + ": " + result.getLogMessage(), e); + return result; + } finally { + if (move != null) { + move.releaseConnection(); + } + } + + return result; + } + +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.java new file mode 100644 index 0000000000..d03d612505 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.java @@ -0,0 +1,80 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.lib.resources.albums; + +import android.net.Uri; + +import com.nextcloud.common.SessionTimeOut; +import com.nextcloud.common.SessionTimeOutKt; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.network.WebdavEntry; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; + +import org.apache.commons.httpclient.HttpStatus; +import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod; +import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; +import org.apache.jackrabbit.webdav.property.DavPropertySet; +import org.apache.jackrabbit.webdav.property.DefaultDavProperty; +import org.apache.jackrabbit.webdav.xml.Namespace; + +import java.io.IOException; + +public class ToggleAlbumFavoriteRemoteOperation extends RemoteOperation { + private boolean makeItFavorited; + private String filePath; + private final SessionTimeOut sessionTimeOut; + + public ToggleAlbumFavoriteRemoteOperation(boolean makeItFavorited, String filePath) { + this(makeItFavorited, filePath, SessionTimeOutKt.getDefaultSessionTimeOut()); + } + + public ToggleAlbumFavoriteRemoteOperation(boolean makeItFavorited, String filePath, SessionTimeOut sessionTimeOut) { + this.makeItFavorited = makeItFavorited; + this.filePath = filePath; + this.sessionTimeOut = sessionTimeOut; + } + + protected RemoteOperationResult run(OwnCloudClient client) { + RemoteOperationResult result; + PropPatchMethod propPatchMethod = null; + DavPropertySet newProps = new DavPropertySet(); + DavPropertyNameSet removeProperties = new DavPropertyNameSet(); + if (this.makeItFavorited) { + DefaultDavProperty favoriteProperty = new DefaultDavProperty("oc:favorite", "1", Namespace.getNamespace(WebdavEntry.NAMESPACE_OC)); + newProps.add(favoriteProperty); + } else { + removeProperties.add("oc:favorite", Namespace.getNamespace(WebdavEntry.NAMESPACE_OC)); + } + + String webDavUrl = client.getDavUri().toString() + "/photos/"; + String encodedPath = (client.getUserId() + Uri.encode(this.filePath)).replace("%2F", "/"); + String fullFilePath = webDavUrl + encodedPath; + + try { + propPatchMethod = new PropPatchMethod(fullFilePath, newProps, removeProperties); + int status = client.executeMethod(propPatchMethod, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); + boolean isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK); + if (isSuccess) { + result = new RemoteOperationResult(true, status, propPatchMethod.getResponseHeaders()); + } else { + client.exhaustResponse(propPatchMethod.getResponseBodyAsStream()); + result = new RemoteOperationResult(false, status, propPatchMethod.getResponseHeaders()); + } + } catch (IOException e) { + result = new RemoteOperationResult(e); + } finally { + if (propPatchMethod != null) { + propPatchMethod.releaseConnection(); + } + + } + + return result; + } +} \ No newline at end of file From bb8af76ebd256e4c0d9f724873238f4a42ae500c Mon Sep 17 00:00:00 2001 From: A117870935 Date: Tue, 3 Jun 2025 19:13:43 +0530 Subject: [PATCH 02/15] Code refactoring and unit test for PhotoAlbumEntry Signed-off-by: Surinder Kumar --- .../lib/common/utils/WebDavFileUtils.java | 26 +++++---- .../lib/resources/albums/PhotoAlbumEntry.kt | 37 ++++-------- .../albums/ReadAlbumItemsRemoteOperation.java | 2 +- .../resources/albums/PhotoAlbumEntryTest.kt | 57 +++++++++++++++++++ 4 files changed, 85 insertions(+), 37 deletions(-) create mode 100644 library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt diff --git a/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java b/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java index 80bc1c511c..bcfb80c55d 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java +++ b/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java @@ -18,6 +18,8 @@ import org.apache.jackrabbit.webdav.MultiStatusResponse; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * WebDav helper. @@ -72,19 +74,23 @@ public ArrayList readData(MultiStatus remoteData, * retrieved. * @return */ - public ArrayList readAlbumData(MultiStatus remoteData, OwnCloudClient client) { - String url = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId(); + public List readAlbumData(MultiStatus remoteData, OwnCloudClient client) { + String baseUrl = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId(); + String encodedPath = Uri.parse(baseUrl).getEncodedPath(); + if (encodedPath == null) { + return Collections.emptyList(); + } - ArrayList mFolderAndFiles = new ArrayList<>(); + List files = new ArrayList<>(); + final var responses = remoteData.getResponses(); - // loop to update every child // reading from 1 as 0th item will be just the root album path - for (int i = 1; i < remoteData.getResponses().length; ++i) { - /// new OCFile instance with the data from the server - WebdavEntry we = new WebdavEntry(remoteData.getResponses()[i], Uri.parse(url).getEncodedPath()); - RemoteFile remoteFile = new RemoteFile(we); - mFolderAndFiles.add(remoteFile); + for (int i = 1; i < responses.length; i++) { + WebdavEntry entry = new WebdavEntry(responses[i], encodedPath); + RemoteFile remoteFile = new RemoteFile(entry); + files.add(remoteFile); } - return mFolderAndFiles; + + return files; } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt index 2204c24af8..ba0b57e83e 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt @@ -66,41 +66,26 @@ class PhotoAlbumEntry(response: MultiStatusResponse) { val albumName: String get() { - var href = href - if (href.isEmpty()) { - return "" - } - - // Remove trailing slash if present - if (href.endsWith("/")) { - href = href.substring(0, href.length - 1) - } - - // Split and return last part - val parts = href.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() - return if (parts.isNotEmpty()) parts[parts.size - 1] else "" + return href + .removeSuffix("/") + .substringAfterLast("/") + .takeIf { it.isNotEmpty() } ?: "" } val createdDate: String get() { - val jsonRange = dateRange val currentDate = Date(System.currentTimeMillis()) - if (jsonRange.isNullOrEmpty()) { - return dateFormat.format(currentDate) - } - try { - val obj = JSONObject(jsonRange) + return try { + val obj = JSONObject(dateRange ?: return dateFormat.format(currentDate)) val startTimestamp = obj.optLong("start", 0) - - if (startTimestamp > 0) { - val date = Date(startTimestamp * 1000L) // Convert to milliseconds - return dateFormat.format(date) - } + if (startTimestamp > 0) + dateFormat.format(Date(startTimestamp * 1000L)) + else + dateFormat.format(currentDate) } catch (e: JSONException) { e.printStackTrace() - return dateFormat.format(currentDate) + dateFormat.format(currentDate) } - return dateFormat.format(currentDate) } } \ No newline at end of file diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.java index 9e267ff5e4..5459ea807b 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.java +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.java @@ -57,7 +57,7 @@ protected RemoteOperationResult> run(OwnCloudClient client) { if (isSuccess) { // get data from remote folder MultiStatus dataInServer = query.getResponseBodyAsMultiStatus(); - ArrayList mFolderAndFiles = new WebDavFileUtils().readAlbumData(dataInServer, client); + List mFolderAndFiles = new WebDavFileUtils().readAlbumData(dataInServer, client); // Result of the operation result = new RemoteOperationResult<>(true, query); diff --git a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt new file mode 100644 index 0000000000..f7757395d8 --- /dev/null +++ b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt @@ -0,0 +1,57 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.lib.resources.albums + +import org.apache.jackrabbit.webdav.MultiStatusResponse +import org.junit.Assert.assertEquals +import org.junit.Test + +class PhotoAlbumEntryTest { + + @Test + fun testAlbumName_withTrailingSlash() { + val entry = createTestEntry("/remote.php/dav/photos/user_id/albums/vacation2024/") + assertEquals("vacation2024", entry.albumName) + } + + @Test + fun testAlbumName_withoutTrailingSlash() { + val entry = createTestEntry("/remote.php/dav/photos/user_id/albums/vacation2024") + assertEquals("vacation2024", entry.albumName) + } + + @Test + fun testAlbumName_nestedPath() { + val entry = createTestEntry("/remote.php/dav/photos/user_id/albums/travel/europe/") + assertEquals("europe", entry.albumName) + } + + @Test + fun testAlbumName_singleSlash() { + val entry = createTestEntry("/") + assertEquals("", entry.albumName) + } + + @Test + fun testAlbumName_onlySlashes() { + val entry = createTestEntry("///") + assertEquals("", entry.albumName) + } + + @Test + fun testAlbumName_noSlash() { + val entry = createTestEntry("holiday") + assertEquals("holiday", entry.albumName) + } + + // Helper method to create a stub entry + private fun createTestEntry(href: String): PhotoAlbumEntry { + val response = MultiStatusResponse(href, 200) + return PhotoAlbumEntry(response) + } +} \ No newline at end of file From c01490445ed8629af54c76b6ea5a54f80f4151bf Mon Sep 17 00:00:00 2001 From: A117870935 Date: Wed, 4 Jun 2025 11:32:12 +0530 Subject: [PATCH 03/15] Rename .java to .kt Signed-off-by: Surinder Kumar --- ...lbumRemoteOperation.java => CopyFileToAlbumRemoteOperation.kt} | 0 ...AlbumRemoteOperation.java => CreateNewAlbumRemoteOperation.kt} | 0 ...ItemsRemoteOperation.java => ReadAlbumItemsRemoteOperation.kt} | 0 ...eadAlbumsRemoteOperation.java => ReadAlbumsRemoteOperation.kt} | 0 ...FileRemoteOperation.java => RemoveAlbumFileRemoteOperation.kt} | 0 ...oveAlbumRemoteOperation.java => RemoveAlbumRemoteOperation.kt} | 0 ...ameAlbumRemoteOperation.java => RenameAlbumRemoteOperation.kt} | 0 ...RemoteOperation.java => ToggleAlbumFavoriteRemoteOperation.kt} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename library/src/main/java/com/owncloud/android/lib/resources/albums/{CopyFileToAlbumRemoteOperation.java => CopyFileToAlbumRemoteOperation.kt} (100%) rename library/src/main/java/com/owncloud/android/lib/resources/albums/{CreateNewAlbumRemoteOperation.java => CreateNewAlbumRemoteOperation.kt} (100%) rename library/src/main/java/com/owncloud/android/lib/resources/albums/{ReadAlbumItemsRemoteOperation.java => ReadAlbumItemsRemoteOperation.kt} (100%) rename library/src/main/java/com/owncloud/android/lib/resources/albums/{ReadAlbumsRemoteOperation.java => ReadAlbumsRemoteOperation.kt} (100%) rename library/src/main/java/com/owncloud/android/lib/resources/albums/{RemoveAlbumFileRemoteOperation.java => RemoveAlbumFileRemoteOperation.kt} (100%) rename library/src/main/java/com/owncloud/android/lib/resources/albums/{RemoveAlbumRemoteOperation.java => RemoveAlbumRemoteOperation.kt} (100%) rename library/src/main/java/com/owncloud/android/lib/resources/albums/{RenameAlbumRemoteOperation.java => RenameAlbumRemoteOperation.kt} (100%) rename library/src/main/java/com/owncloud/android/lib/resources/albums/{ToggleAlbumFavoriteRemoteOperation.java => ToggleAlbumFavoriteRemoteOperation.kt} (100%) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt similarity index 100% rename from library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.java rename to library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt similarity index 100% rename from library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.java rename to library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt similarity index 100% rename from library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.java rename to library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt similarity index 100% rename from library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.java rename to library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt similarity index 100% rename from library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.java rename to library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt similarity index 100% rename from library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.java rename to library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt similarity index 100% rename from library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.java rename to library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.java b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt similarity index 100% rename from library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.java rename to library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt From 6cef9cc25476dcd8394a9c0f65698e09c68c9f2c Mon Sep 17 00:00:00 2001 From: A117870935 Date: Wed, 4 Jun 2025 11:32:14 +0530 Subject: [PATCH 04/15] Operations migrated to kotlin. Signed-off-by: Surinder Kumar --- .../albums/CopyFileToAlbumRemoteOperation.kt | 193 +++++++++--------- .../albums/CreateNewAlbumRemoteOperation.kt | 96 +++++---- .../albums/ReadAlbumItemsRemoteOperation.kt | 131 ++++++------ .../albums/ReadAlbumsRemoteOperation.kt | 128 +++++------- .../albums/RemoveAlbumFileRemoteOperation.kt | 94 ++++----- .../albums/RemoveAlbumRemoteOperation.kt | 89 ++++---- .../albums/RenameAlbumRemoteOperation.kt | 105 +++++----- .../ToggleAlbumFavoriteRemoteOperation.kt | 109 +++++----- 8 files changed, 456 insertions(+), 489 deletions(-) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt index 15e23a9e1e..0af703b924 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt @@ -4,156 +4,149 @@ * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-License-Identifier: AGPL-3.0-or-later */ - -package com.owncloud.android.lib.resources.albums; - -import android.util.Log; - -import com.nextcloud.common.SessionTimeOut; -import com.nextcloud.common.SessionTimeOutKt; -import com.owncloud.android.lib.common.OwnCloudClient; -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 org.apache.commons.httpclient.HttpStatus; -import org.apache.jackrabbit.webdav.DavException; -import org.apache.jackrabbit.webdav.MultiStatusResponse; -import org.apache.jackrabbit.webdav.Status; -import org.apache.jackrabbit.webdav.client.methods.CopyMethod; - -import java.io.IOException; - +package com.owncloud.android.lib.resources.albums + +import android.util.Log +import com.nextcloud.common.SessionTimeOut +import com.nextcloud.common.defaultSessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +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.resources.albums.CopyFileToAlbumRemoteOperation +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.DavException +import org.apache.jackrabbit.webdav.Status +import org.apache.jackrabbit.webdav.client.methods.CopyMethod +import java.io.IOException /** * 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. */ -public class CopyFileToAlbumRemoteOperation extends RemoteOperation { - private static final String TAG = CopyFileToAlbumRemoteOperation.class.getSimpleName(); - - private final String mSrcRemotePath; - private final String mTargetRemotePath; - - private final SessionTimeOut sessionTimeOut; - - public CopyFileToAlbumRemoteOperation(String srcRemotePath, String targetRemotePath) { - this(srcRemotePath, targetRemotePath, SessionTimeOutKt.getDefaultSessionTimeOut()); - } - - public CopyFileToAlbumRemoteOperation(String srcRemotePath, String targetRemotePath, SessionTimeOut sessionTimeOut) { - mSrcRemotePath = srcRemotePath; - mTargetRemotePath = targetRemotePath; - this.sessionTimeOut = sessionTimeOut; - } - +class CopyFileToAlbumRemoteOperation @JvmOverloads constructor( + private val mSrcRemotePath: String, + private val mTargetRemotePath: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut +) : + RemoteOperation() { /** * Performs the operation. * * @param client Client object to communicate with the remote ownCloud server. */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + /** check parameters */ - /// check parameters - if (mTargetRemotePath.equals(mSrcRemotePath)) { + if (mTargetRemotePath == mSrcRemotePath) { // nothing to do! - return new RemoteOperationResult<>(ResultCode.OK); + return RemoteOperationResult(ResultCode.OK) } if (mTargetRemotePath.startsWith(mSrcRemotePath)) { - return new RemoteOperationResult<>(ResultCode.INVALID_COPY_INTO_DESCENDANT); + return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT) } - /// perform remote operation - CopyMethod copyMethod = null; - RemoteOperationResult result; + /** perform remote operation */ + var copyMethod: CopyMethod? = null + var result: RemoteOperationResult try { - copyMethod = new CopyMethod( + copyMethod = CopyMethod( client.getFilesDavUri(this.mSrcRemotePath), - client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(mTargetRemotePath), + "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ + WebdavUtils.encodePath( + mTargetRemotePath + ) + }", false - ); - int status = client.executeMethod(copyMethod, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); - - /// process response + ) + val status = client.executeMethod( + copyMethod, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + + /** process response */ if (status == HttpStatus.SC_MULTI_STATUS) { - result = processPartialError(copyMethod); - + result = processPartialError(copyMethod) } else if (status == HttpStatus.SC_PRECONDITION_FAILED) { - - result = new RemoteOperationResult<>(ResultCode.INVALID_OVERWRITE); - client.exhaustResponse(copyMethod.getResponseBodyAsStream()); - + result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE) + client.exhaustResponse(copyMethod.responseBodyAsStream) } else { - result = new RemoteOperationResult<>(isSuccess(status), copyMethod); - client.exhaustResponse(copyMethod.getResponseBodyAsStream()); + result = RemoteOperationResult(isSuccess(status), copyMethod) + client.exhaustResponse(copyMethod.responseBodyAsStream) } - Log.i(TAG, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage()); - - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - Log.e(TAG, "Copy " + mSrcRemotePath + " to " + mTargetRemotePath + ": " + result.getLogMessage(), e); - + Log.i( + TAG, + "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}" + ) + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log.e( + TAG, + "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}", e + ) } finally { - if (copyMethod != null) { - copyMethod.releaseConnection(); - } + copyMethod?.releaseConnection() } - return result; + return result } - /** * Analyzes a multistatus response from the OC server to generate an appropriate result. - *

+ * + * * In WebDAV, a COPY request on collections (folders) can be PARTIALLY successful: some * children are copied, some other aren't. - *

+ * + * * According to the WebDAV specification, a multistatus response SHOULD NOT include partial * successes (201, 204) nor for descendants of already failed children (424) in the response * entity. But SHOULD NOT != MUST NOT, so take carefully. * * @param copyMethod Copy operation just finished with a multistatus response - * @return A result for the {@link CopyFileToAlbumRemoteOperation} caller + * @return A result for the [CopyFileToAlbumRemoteOperation] caller * @throws java.io.IOException If the response body could not be parsed * @throws org.apache.jackrabbit.webdav.DavException If the status code is other than MultiStatus or if obtaining - * the response XML document fails + * the response XML document fails */ - private RemoteOperationResult processPartialError(CopyMethod copyMethod) - throws IOException, DavException { + @Throws(IOException::class, DavException::class) + private fun processPartialError(copyMethod: CopyMethod): RemoteOperationResult { // Adding a list of failed descendants to the result could be interesting; or maybe not. // For the moment, let's take the easy way. - - /// check that some error really occurred - MultiStatusResponse[] responses = copyMethod.getResponseBodyAsMultiStatus().getResponses(); - Status[] status; - boolean failFound = false; - for (int i = 0; i < responses.length && !failFound; i++) { - status = responses[i].getStatus(); - failFound = ( - status != null && - status.length > 0 && - status[0].getStatusCode() > 299 - ); + /** check that some error really occurred */ + + val responses = copyMethod.responseBodyAsMultiStatus.responses + var status: Array? + var failFound = false + var i = 0 + while (i < responses.size && !failFound) { + status = responses[i].status + failFound = (!status.isNullOrEmpty() && status[0].statusCode > 299 + ) + i++ } - - RemoteOperationResult result; - if (failFound) { - result = new RemoteOperationResult<>(ResultCode.PARTIAL_COPY_DONE); + val result: RemoteOperationResult = if (failFound) { + RemoteOperationResult(ResultCode.PARTIAL_COPY_DONE) } else { - result = new RemoteOperationResult<>(true, copyMethod); + RemoteOperationResult(true, copyMethod) } - return result; + return result + } + + private fun isSuccess(status: Int): Boolean { + return status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT } - protected boolean isSuccess(int status) { - return status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT; + companion object { + private val TAG: String = CopyFileToAlbumRemoteOperation::class.java.simpleName } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt index dbaa5d5cf7..615384fea6 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt @@ -4,69 +4,67 @@ * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-License-Identifier: AGPL-3.0-or-later */ +package com.owncloud.android.lib.resources.albums -package com.owncloud.android.lib.resources.albums; - -import com.nextcloud.common.SessionTimeOut; -import com.nextcloud.common.SessionTimeOutKt; -import com.owncloud.android.lib.common.OwnCloudClient; -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.Log_OC; - -import org.apache.jackrabbit.webdav.client.methods.MkColMethod; - -public class CreateNewAlbumRemoteOperation extends RemoteOperation { - private static final String TAG = CreateNewAlbumRemoteOperation.class.getSimpleName(); - private final String newAlbumName; - private final SessionTimeOut sessionTimeOut; - - public CreateNewAlbumRemoteOperation(String newAlbumName) { - this(newAlbumName, SessionTimeOutKt.getDefaultSessionTimeOut()); - } - - public CreateNewAlbumRemoteOperation(String newAlbumName, SessionTimeOut sessionTimeOut) { - this.newAlbumName = newAlbumName; - this.sessionTimeOut = sessionTimeOut; - } - - public String getNewAlbumName() { - return newAlbumName; - } +import com.nextcloud.common.SessionTimeOut +import com.nextcloud.common.defaultSessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +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.Log_OC +import com.owncloud.android.lib.resources.albums.CreateNewAlbumRemoteOperation +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.client.methods.MkColMethod +class CreateNewAlbumRemoteOperation @JvmOverloads constructor( + val newAlbumName: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut +) : + RemoteOperation() { /** * Performs the operation. * * @param client Client object to communicate with the remote ownCloud server. */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - MkColMethod mkCol = null; - RemoteOperationResult result; + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + var mkCol: MkColMethod? = null + var result: RemoteOperationResult try { - mkCol = new MkColMethod(client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(newAlbumName)); - client.executeMethod(mkCol, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); - if (405 == mkCol.getStatusCode()) { - result = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS); + mkCol = MkColMethod( + "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ + WebdavUtils.encodePath( + newAlbumName + ) + }" + ) + client.executeMethod( + mkCol, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + if (HttpStatus.SC_METHOD_NOT_ALLOWED == mkCol.statusCode) { + result = + RemoteOperationResult(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS) } else { - result = new RemoteOperationResult<>(mkCol.succeeded(), mkCol); - result.setResultData(null); + result = RemoteOperationResult(mkCol.succeeded(), mkCol) + result.setResultData(null) } - Log_OC.d(TAG, "Create album " + newAlbumName + ": " + result.getLogMessage()); - client.exhaustResponse(mkCol.getResponseBodyAsStream()); - } catch (Exception e) { - result = new RemoteOperationResult<>(e); - Log_OC.e(TAG, "Create album " + newAlbumName + ": " + result.getLogMessage(), e); + Log_OC.d(TAG, "Create album $newAlbumName : ${result.logMessage}") + client.exhaustResponse(mkCol.responseBodyAsStream) + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e(TAG, "Create album $newAlbumName : ${result.logMessage}", e) } finally { - if (mkCol != null) { - mkCol.releaseConnection(); - } - + mkCol?.releaseConnection() } - return result; + return result } + companion object { + private val TAG: String = CreateNewAlbumRemoteOperation::class.java.simpleName + } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt index 5459ea807b..37b54c14ce 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt @@ -4,96 +4,99 @@ * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-License-Identifier: AGPL-3.0-or-later */ +package com.owncloud.android.lib.resources.albums -package com.owncloud.android.lib.resources.albums; +import com.nextcloud.common.SessionTimeOut +import com.nextcloud.common.defaultSessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +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.Log_OC +import com.owncloud.android.lib.common.utils.WebDavFileUtils +import com.owncloud.android.lib.resources.albums.ReadAlbumItemsRemoteOperation +import com.owncloud.android.lib.resources.files.model.RemoteFile +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.DavConstants +import org.apache.jackrabbit.webdav.client.methods.PropFindMethod -import com.nextcloud.common.SessionTimeOut; -import com.nextcloud.common.SessionTimeOutKt; -import com.owncloud.android.lib.common.OwnCloudClient; -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.Log_OC; -import com.owncloud.android.lib.common.utils.WebDavFileUtils; -import com.owncloud.android.lib.resources.files.model.RemoteFile; - -import org.apache.commons.httpclient.HttpStatus; -import org.apache.jackrabbit.webdav.DavConstants; -import org.apache.jackrabbit.webdav.MultiStatus; -import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; - -import java.util.ArrayList; -import java.util.List; - -public class ReadAlbumItemsRemoteOperation extends RemoteOperation> { - - private static final String TAG = ReadAlbumItemsRemoteOperation.class.getSimpleName(); - private final String mRemotePath; - private final SessionTimeOut sessionTimeOut; - - public ReadAlbumItemsRemoteOperation(String mRemotePath) { - this(mRemotePath, SessionTimeOutKt.getDefaultSessionTimeOut()); - } - - public ReadAlbumItemsRemoteOperation(String mRemotePath, SessionTimeOut sessionTimeOut) { - this.mRemotePath = mRemotePath; - this.sessionTimeOut = sessionTimeOut; - } - - protected RemoteOperationResult> run(OwnCloudClient client) { - RemoteOperationResult> result = null; - PropFindMethod query = null; - String url = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(mRemotePath); +class ReadAlbumItemsRemoteOperation @JvmOverloads constructor( + private val mRemotePath: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut +) : + RemoteOperation>() { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult> { + var result: RemoteOperationResult>? = null + var query: PropFindMethod? = null + val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ + WebdavUtils.encodePath( + mRemotePath + ) + }" try { // remote request - query = new PropFindMethod(url, - WebdavUtils.getAllPropSet(), // PropFind Properties - DavConstants.DEPTH_1); - int status = client.executeMethod(query, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); + query = PropFindMethod( + url, + WebdavUtils.getAllPropSet(), // PropFind Properties + DavConstants.DEPTH_1 + ) + val status = client.executeMethod( + query, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) // check and process response - boolean isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK); + val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) if (isSuccess) { // get data from remote folder - MultiStatus dataInServer = query.getResponseBodyAsMultiStatus(); - List mFolderAndFiles = new WebDavFileUtils().readAlbumData(dataInServer, client); + val dataInServer = query.responseBodyAsMultiStatus + val mFolderAndFiles = WebDavFileUtils().readAlbumData(dataInServer, client) // Result of the operation - result = new RemoteOperationResult<>(true, query); + result = RemoteOperationResult(true, query) // Add data to the result - if (result.isSuccess()) { - result.setResultData(mFolderAndFiles); + if (result.isSuccess) { + result.resultData = mFolderAndFiles } } else { // synchronization failed - client.exhaustResponse(query.getResponseBodyAsStream()); - result = new RemoteOperationResult<>(false, query); + client.exhaustResponse(query.responseBodyAsStream) + result = RemoteOperationResult(false, query) } - } catch (Exception e) { - result = new RemoteOperationResult<>(e); + } catch (e: Exception) { + result = RemoteOperationResult(e) } finally { - if (query != null) { - query.releaseConnection(); // let the connection available for other methods - } + query?.releaseConnection() if (result == null) { - result = new RemoteOperationResult(new Exception("unknown error")); - Log_OC.e(TAG, "Synchronized " + mRemotePath + ": failed"); + result = RemoteOperationResult>(Exception("unknown error")) + Log_OC.e( + TAG, + "Synchronized $mRemotePath: failed" + ) } else { - if (result.isSuccess()) { - Log_OC.i(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage()); + if (result.isSuccess) { + Log_OC.i(TAG, "Synchronized $mRemotePath : ${result.logMessage}") } else { - if (result.isException()) { - Log_OC.e(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage(), - result.getException()); + if (result.isException) { + Log_OC.e( + TAG, "Synchronized $mRemotePath : ${result.logMessage}", + result.exception + ) } else { - Log_OC.e(TAG, "Synchronized " + mRemotePath + ": " + result.getLogMessage()); + Log_OC.e(TAG, "Synchronized $mRemotePath : ${result.logMessage}") } } } } - return result; + return result + } + + companion object { + private val TAG: String = ReadAlbumItemsRemoteOperation::class.java.simpleName } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt index 2a09e243d4..9ce71d7f77 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt @@ -4,96 +4,74 @@ * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-License-Identifier: AGPL-3.0-or-later */ - -package com.owncloud.android.lib.resources.albums; - -import static org.apache.commons.httpclient.HttpStatus.SC_MULTI_STATUS; -import static org.apache.commons.httpclient.HttpStatus.SC_OK; - -import android.text.TextUtils; - -import androidx.annotation.Nullable; - -import com.nextcloud.common.SessionTimeOut; -import com.nextcloud.common.SessionTimeOutKt; -import com.owncloud.android.lib.common.OwnCloudClient; -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.Log_OC; - -import org.apache.jackrabbit.webdav.DavConstants; -import org.apache.jackrabbit.webdav.MultiStatus; -import org.apache.jackrabbit.webdav.MultiStatusResponse; -import org.apache.jackrabbit.webdav.client.methods.PropFindMethod; - -import java.util.ArrayList; -import java.util.List; - -public class ReadAlbumsRemoteOperation extends RemoteOperation> { - - private static final String TAG = ReadAlbumsRemoteOperation.class.getSimpleName(); - private final String mAlbumRemotePath; - private final SessionTimeOut sessionTimeOut; - - - public ReadAlbumsRemoteOperation() { - this(null); - } - - public ReadAlbumsRemoteOperation(@Nullable String mAlbumRemotePath) { - this(mAlbumRemotePath, SessionTimeOutKt.getDefaultSessionTimeOut()); - } - - public ReadAlbumsRemoteOperation(@Nullable String mAlbumRemotePath, SessionTimeOut sessionTimeOut) { - this.mAlbumRemotePath = mAlbumRemotePath; - this.sessionTimeOut = sessionTimeOut; - } - +package com.owncloud.android.lib.resources.albums + +import android.text.TextUtils +import com.nextcloud.common.SessionTimeOut +import com.nextcloud.common.defaultSessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +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.Log_OC +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.DavConstants +import org.apache.jackrabbit.webdav.client.methods.PropFindMethod + +class ReadAlbumsRemoteOperation @JvmOverloads constructor( + private val mAlbumRemotePath: String? = null, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut +) : + RemoteOperation>() { /** * Performs the operation. * * @param client Client object to communicate with the remote ownCloud server. */ - @Override - protected RemoteOperationResult> run(OwnCloudClient client) { - PropFindMethod propfind = null; - RemoteOperationResult> result; - String url = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums"; + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult> { + var propfind: PropFindMethod? = null + var result: RemoteOperationResult> + var url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" if (!TextUtils.isEmpty(mAlbumRemotePath)) { - url += WebdavUtils.encodePath(mAlbumRemotePath); + url += WebdavUtils.encodePath(mAlbumRemotePath) } try { - propfind = new PropFindMethod(url, WebdavUtils.getAlbumPropSet(), DavConstants.DEPTH_1); - int status = client.executeMethod(propfind, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); - boolean isSuccess = status == SC_MULTI_STATUS || status == SC_OK; + propfind = PropFindMethod(url, WebdavUtils.getAlbumPropSet(), DavConstants.DEPTH_1) + val status = client.executeMethod( + propfind, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + val isSuccess = status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK if (isSuccess) { - MultiStatus multiStatus = propfind.getResponseBodyAsMultiStatus(); - List albumsList = new ArrayList<>(); - for (MultiStatusResponse response : multiStatus.getResponses()) { - int st = response.getStatus()[0].getStatusCode(); - if (st == SC_OK) { - PhotoAlbumEntry entry = new PhotoAlbumEntry(response); - albumsList.add(entry); + val multiStatus = propfind.responseBodyAsMultiStatus + val albumsList: MutableList = ArrayList() + for (response in multiStatus.responses) { + val st = response.status[0].statusCode + if (st == HttpStatus.SC_OK) { + val entry = PhotoAlbumEntry(response) + albumsList.add(entry) } } - result = new RemoteOperationResult(true, propfind); - result.setResultData(albumsList); + result = RemoteOperationResult>(true, propfind) + result.setResultData(albumsList) } else { - result = new RemoteOperationResult(false, propfind); - client.exhaustResponse(propfind.getResponseBodyAsStream()); + result = RemoteOperationResult>(false, propfind) + client.exhaustResponse(propfind.responseBodyAsStream) } - } catch (Exception var13) { - Exception e = var13; - result = new RemoteOperationResult(e); - Log_OC.e(TAG, "Read album " + " failed: " + result.getLogMessage(), result.getException()); + } catch (var13: Exception) { + val e = var13 + result = RemoteOperationResult>(e) + Log_OC.e(TAG, "Read album failed: ${result.logMessage}", result.exception) } finally { - if (propfind != null) { - propfind.releaseConnection(); - } - + propfind?.releaseConnection() } - return result; + return result + } + + companion object { + private val TAG: String = ReadAlbumsRemoteOperation::class.java.simpleName } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt index 52296a5651..8ffa6f1af5 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt @@ -4,59 +4,55 @@ * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-License-Identifier: AGPL-3.0-or-later */ - -package com.owncloud.android.lib.resources.albums; - -import android.net.Uri; - -import com.nextcloud.common.SessionTimeOut; -import com.nextcloud.common.SessionTimeOutKt; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation; - -import org.apache.commons.httpclient.HttpStatus; -import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; - -public class RemoveAlbumFileRemoteOperation extends RemoteOperation { - private static final String TAG = RemoveFileRemoteOperation.class.getSimpleName(); - private final String mRemotePath; - private final SessionTimeOut sessionTimeOut; - - public RemoveAlbumFileRemoteOperation(String remotePath) { - this(remotePath, SessionTimeOutKt.getDefaultSessionTimeOut()); - } - - public RemoveAlbumFileRemoteOperation(String remotePath, SessionTimeOut sessionTimeOut) { - this.mRemotePath = remotePath; - this.sessionTimeOut = sessionTimeOut; - } - - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result; - DeleteMethod delete = null; - String webDavUrl = client.getDavUri().toString()+"/photos/"; - String encodedPath = (client.getUserId() + Uri.encode(this.mRemotePath)).replace("%2F", "/"); - String fullFilePath = webDavUrl + encodedPath; +package com.owncloud.android.lib.resources.albums + +import android.net.Uri +import com.nextcloud.common.SessionTimeOut +import com.nextcloud.common.defaultSessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.client.methods.DeleteMethod + +class RemoveAlbumFileRemoteOperation @JvmOverloads constructor( + private val mRemotePath: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut +) : + RemoteOperation() { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + var delete: DeleteMethod? = null + val webDavUrl = "${client.davUri}/photos/" + val encodedPath = ("${client.userId}${Uri.encode(this.mRemotePath)}").replace("%2F", "/") + val fullFilePath = "$webDavUrl$encodedPath" try { - delete = new DeleteMethod(fullFilePath); - int status = client.executeMethod(delete, this.sessionTimeOut.getReadTimeOut(), this.sessionTimeOut.getConnectionTimeOut()); - delete.getResponseBodyAsString(); - result = new RemoteOperationResult(delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, delete); - Log_OC.i(TAG, "Remove " + this.mRemotePath + ": " + result.getLogMessage()); - } catch (Exception e) { - result = new RemoteOperationResult(e); - Log_OC.e(TAG, "Remove " + this.mRemotePath + ": " + result.getLogMessage(), e); + delete = DeleteMethod(fullFilePath) + val status = client.executeMethod( + delete, + sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut + ) + delete.responseBodyAsString + result = RemoteOperationResult( + delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, + delete + ) + Log_OC.i(TAG, "Remove ${this.mRemotePath} : ${result.logMessage}") + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e(TAG, "Remove ${this.mRemotePath} : ${result.logMessage}", e) } finally { - if (delete != null) { - delete.releaseConnection(); - } - + delete?.releaseConnection() } - return result; + return result + } + + companion object { + private val TAG: String = RemoveFileRemoteOperation::class.java.simpleName } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt index f86e6fc919..bff94b2040 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt @@ -4,61 +4,64 @@ * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-License-Identifier: AGPL-3.0-or-later */ +package com.owncloud.android.lib.resources.albums -package com.owncloud.android.lib.resources.albums; - -import com.nextcloud.common.SessionTimeOut; -import com.nextcloud.common.SessionTimeOutKt; -import com.owncloud.android.lib.common.OwnCloudClient; -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.Log_OC; - -import org.apache.commons.httpclient.HttpStatus; -import org.apache.jackrabbit.webdav.client.methods.DeleteMethod; - -public class RemoveAlbumRemoteOperation extends RemoteOperation { - private static final String TAG = RemoveAlbumRemoteOperation.class.getSimpleName(); - private final String albumName; - private final SessionTimeOut sessionTimeOut; - - public RemoveAlbumRemoteOperation(String albumName) { - this(albumName, SessionTimeOutKt.getDefaultSessionTimeOut()); - } - - public RemoveAlbumRemoteOperation(String albumName, SessionTimeOut sessionTimeOut) { - this.albumName = albumName; - this.sessionTimeOut = sessionTimeOut; - } +import com.nextcloud.common.SessionTimeOut +import com.nextcloud.common.defaultSessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +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.Log_OC +import com.owncloud.android.lib.resources.albums.RemoveAlbumRemoteOperation +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.client.methods.DeleteMethod +class RemoveAlbumRemoteOperation @JvmOverloads constructor( + private val albumName: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut +) : + RemoteOperation() { /** * Performs the operation. * * @param client Client object to communicate with the remote ownCloud server. */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result; - DeleteMethod delete = null; + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + var delete: DeleteMethod? = null try { - delete = new DeleteMethod(client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums" + WebdavUtils.encodePath(albumName)); - int status = client.executeMethod(delete, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); - delete.getResponseBodyAsString(); - result = new RemoteOperationResult(delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, delete); - Log_OC.i(TAG, "Remove " + this.albumName + ": " + result.getLogMessage()); - } catch (Exception e) { - result = new RemoteOperationResult(e); - Log_OC.e(TAG, "Remove " + this.albumName + ": " + result.getLogMessage(), e); + delete = DeleteMethod( + "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ + WebdavUtils.encodePath( + albumName + ) + }" + ) + val status = client.executeMethod( + delete, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + delete.responseBodyAsString + result = RemoteOperationResult( + delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, + delete + ) + Log_OC.i(TAG, "Remove ${this.albumName} : ${result.logMessage}") + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e(TAG, "Remove ${this.albumName} : ${result.logMessage}", e) } finally { - if (delete != null) { - delete.releaseConnection(); - } - + delete?.releaseConnection() } - return result; + return result } + companion object { + private val TAG: String = RemoveAlbumRemoteOperation::class.java.simpleName + } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt index 7c17cb0324..fe1def4129 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt @@ -4,69 +4,72 @@ * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-License-Identifier: AGPL-3.0-or-later */ +package com.owncloud.android.lib.resources.albums -package com.owncloud.android.lib.resources.albums; - -import com.nextcloud.common.SessionTimeOut; -import com.nextcloud.common.SessionTimeOutKt; -import com.owncloud.android.lib.common.OwnCloudClient; -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.Log_OC; - -import org.apache.jackrabbit.webdav.client.methods.MoveMethod; - -public class RenameAlbumRemoteOperation extends RemoteOperation { - private static final String TAG = RenameAlbumRemoteOperation.class.getSimpleName(); - private final String mOldRemotePath; - private final String mNewAlbumName; - private final SessionTimeOut sessionTimeOut; - - public RenameAlbumRemoteOperation(String mOldRemotePath, String mNewAlbumName) { - this(mOldRemotePath, mNewAlbumName, SessionTimeOutKt.getDefaultSessionTimeOut()); - } - - public RenameAlbumRemoteOperation(String mOldRemotePath, String mNewAlbumName, SessionTimeOut sessionTimeOut) { - this.mOldRemotePath = mOldRemotePath; - this.mNewAlbumName = mNewAlbumName; - this.sessionTimeOut = sessionTimeOut; - } - - public String getNewAlbumName() { - return mNewAlbumName; - } +import com.nextcloud.common.SessionTimeOut +import com.nextcloud.common.defaultSessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +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.Log_OC +import com.owncloud.android.lib.resources.albums.RenameAlbumRemoteOperation +import org.apache.jackrabbit.webdav.client.methods.MoveMethod +class RenameAlbumRemoteOperation @JvmOverloads constructor( + private val mOldRemotePath: String, + val newAlbumName: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut +) : + RemoteOperation() { /** * Performs the operation. * * @param client Client object to communicate with the remote ownCloud server. */ - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result = null; - MoveMethod move = null; - String url = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId() + "/albums"; + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult? { + var result: RemoteOperationResult? = null + var move: MoveMethod? = null + val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" try { - if (!this.mNewAlbumName.equals(this.mOldRemotePath)) { - move = new MoveMethod(url + WebdavUtils.encodePath(mOldRemotePath), url + WebdavUtils.encodePath(mNewAlbumName), true); - client.executeMethod(move, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); - result = new RemoteOperationResult(move.succeeded(), move); - Log_OC.i(TAG, "Rename " + this.mOldRemotePath + " to " + this.mNewAlbumName + ": " + result.getLogMessage()); - client.exhaustResponse(move.getResponseBodyAsStream()); - return result; + if (this.newAlbumName != this.mOldRemotePath) { + move = MoveMethod( + "$url${WebdavUtils.encodePath(mOldRemotePath)}", "$url${ + WebdavUtils.encodePath( + newAlbumName + ) + }", true + ) + client.executeMethod( + move, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + result = RemoteOperationResult(move.succeeded(), move) + Log_OC.i( + TAG, + "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}" + ) + client.exhaustResponse(move.responseBodyAsStream) + return result } - } catch (Exception e) { - result = new RemoteOperationResult(e); - Log_OC.e(TAG, "Rename " + this.mOldRemotePath + " to " + this.mNewAlbumName + ": " + result.getLogMessage(), e); - return result; + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e( + TAG, + "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}", + e + ) + return result } finally { - if (move != null) { - move.releaseConnection(); - } + move?.releaseConnection() } - return result; + return result } + companion object { + private val TAG: String = RenameAlbumRemoteOperation::class.java.simpleName + } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt index d03d612505..be1952b338 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt @@ -4,77 +4,70 @@ * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-License-Identifier: AGPL-3.0-or-later */ +package com.owncloud.android.lib.resources.albums -package com.owncloud.android.lib.resources.albums; +import android.net.Uri +import com.nextcloud.common.SessionTimeOut +import com.nextcloud.common.defaultSessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.network.WebdavEntry +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod +import org.apache.jackrabbit.webdav.property.DavPropertyNameSet +import org.apache.jackrabbit.webdav.property.DavPropertySet +import org.apache.jackrabbit.webdav.property.DefaultDavProperty +import org.apache.jackrabbit.webdav.xml.Namespace +import java.io.IOException -import android.net.Uri; - -import com.nextcloud.common.SessionTimeOut; -import com.nextcloud.common.SessionTimeOutKt; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.network.WebdavEntry; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; - -import org.apache.commons.httpclient.HttpStatus; -import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod; -import org.apache.jackrabbit.webdav.property.DavPropertyNameSet; -import org.apache.jackrabbit.webdav.property.DavPropertySet; -import org.apache.jackrabbit.webdav.property.DefaultDavProperty; -import org.apache.jackrabbit.webdav.xml.Namespace; - -import java.io.IOException; - -public class ToggleAlbumFavoriteRemoteOperation extends RemoteOperation { - private boolean makeItFavorited; - private String filePath; - private final SessionTimeOut sessionTimeOut; - - public ToggleAlbumFavoriteRemoteOperation(boolean makeItFavorited, String filePath) { - this(makeItFavorited, filePath, SessionTimeOutKt.getDefaultSessionTimeOut()); - } - - public ToggleAlbumFavoriteRemoteOperation(boolean makeItFavorited, String filePath, SessionTimeOut sessionTimeOut) { - this.makeItFavorited = makeItFavorited; - this.filePath = filePath; - this.sessionTimeOut = sessionTimeOut; - } - - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result; - PropPatchMethod propPatchMethod = null; - DavPropertySet newProps = new DavPropertySet(); - DavPropertyNameSet removeProperties = new DavPropertyNameSet(); +class ToggleAlbumFavoriteRemoteOperation @JvmOverloads constructor( + private val makeItFavorited: Boolean, + private val filePath: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut +) : + RemoteOperation() { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + var propPatchMethod: PropPatchMethod? = null + val newProps = DavPropertySet() + val removeProperties = DavPropertyNameSet() if (this.makeItFavorited) { - DefaultDavProperty favoriteProperty = new DefaultDavProperty("oc:favorite", "1", Namespace.getNamespace(WebdavEntry.NAMESPACE_OC)); - newProps.add(favoriteProperty); + val favoriteProperty = DefaultDavProperty( + "oc:favorite", + "1", + Namespace.getNamespace(WebdavEntry.NAMESPACE_OC) + ) + newProps.add(favoriteProperty) } else { - removeProperties.add("oc:favorite", Namespace.getNamespace(WebdavEntry.NAMESPACE_OC)); + removeProperties.add("oc:favorite", Namespace.getNamespace(WebdavEntry.NAMESPACE_OC)) } - String webDavUrl = client.getDavUri().toString() + "/photos/"; - String encodedPath = (client.getUserId() + Uri.encode(this.filePath)).replace("%2F", "/"); - String fullFilePath = webDavUrl + encodedPath; + val webDavUrl = "${client.davUri}/photos/" + val encodedPath = ("${client.userId}${Uri.encode(this.filePath)}").replace("%2F", "/") + val fullFilePath = "$webDavUrl$encodedPath" try { - propPatchMethod = new PropPatchMethod(fullFilePath, newProps, removeProperties); - int status = client.executeMethod(propPatchMethod, sessionTimeOut.getReadTimeOut(), sessionTimeOut.getConnectionTimeOut()); - boolean isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK); + propPatchMethod = PropPatchMethod(fullFilePath, newProps, removeProperties) + val status = client.executeMethod( + propPatchMethod, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) if (isSuccess) { - result = new RemoteOperationResult(true, status, propPatchMethod.getResponseHeaders()); + result = RemoteOperationResult(true, status, propPatchMethod.responseHeaders) } else { - client.exhaustResponse(propPatchMethod.getResponseBodyAsStream()); - result = new RemoteOperationResult(false, status, propPatchMethod.getResponseHeaders()); + client.exhaustResponse(propPatchMethod.responseBodyAsStream) + result = RemoteOperationResult(false, status, propPatchMethod.responseHeaders) } - } catch (IOException e) { - result = new RemoteOperationResult(e); + } catch (e: IOException) { + result = RemoteOperationResult(e) } finally { - if (propPatchMethod != null) { - propPatchMethod.releaseConnection(); - } - + propPatchMethod?.releaseConnection() } - return result; + return result } } \ No newline at end of file From 69dbcf3dacfc853468167cf44ca51361dad3fa17 Mon Sep 17 00:00:00 2001 From: A117870935 Date: Thu, 5 Jun 2025 22:11:18 +0530 Subject: [PATCH 05/15] Code refactoring Signed-off-by: Surinder Kumar --- .../albums/CopyFileToAlbumRemoteOperation.kt | 1 - .../albums/CreateNewAlbumRemoteOperation.kt | 2 +- .../lib/resources/albums/PhotoAlbumEntry.kt | 6 +++--- .../albums/ReadAlbumItemsRemoteOperation.kt | 1 - .../albums/ReadAlbumsRemoteOperation.kt | 17 +++++------------ .../albums/RemoveAlbumRemoteOperation.kt | 1 - .../albums/RenameAlbumRemoteOperation.kt | 3 --- 7 files changed, 9 insertions(+), 22 deletions(-) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt index 0af703b924..416cae6f8c 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt @@ -14,7 +14,6 @@ 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.resources.albums.CopyFileToAlbumRemoteOperation import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.DavException import org.apache.jackrabbit.webdav.Status diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt index 615384fea6..946b58e80f 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt @@ -49,7 +49,7 @@ class CreateNewAlbumRemoteOperation @JvmOverloads constructor( RemoteOperationResult(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS) } else { result = RemoteOperationResult(mkCol.succeeded(), mkCol) - result.setResultData(null) + result.resultData = null } Log_OC.d(TAG, "Create album $newAlbumName : ${result.logMessage}") diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt index ba0b57e83e..be43aac3f7 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt @@ -27,7 +27,7 @@ class PhotoAlbumEntry(response: MultiStatusResponse) { private val dateRange: String? companion object { - val dateFormat = SimpleDateFormat("MMM yyyy", Locale.US) + private val dateFormat = SimpleDateFormat("MMM yyyy", Locale.US) } init { @@ -51,7 +51,7 @@ class PhotoAlbumEntry(response: MultiStatusResponse) { private fun parseInt(value: String?): Int { return try { value?.toInt() ?: 0 - } catch (e: NumberFormatException) { + } catch (_: NumberFormatException) { 0 } } @@ -59,7 +59,7 @@ class PhotoAlbumEntry(response: MultiStatusResponse) { private fun parseLong(value: String?): Long { return try { value?.toLong() ?: 0L - } catch (e: NumberFormatException) { + } catch (_: NumberFormatException) { 0L } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt index 37b54c14ce..254ec49215 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt @@ -14,7 +14,6 @@ import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.common.utils.WebDavFileUtils -import com.owncloud.android.lib.resources.albums.ReadAlbumItemsRemoteOperation import com.owncloud.android.lib.resources.files.model.RemoteFile import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.DavConstants diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt index 9ce71d7f77..92da03c81d 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt @@ -45,23 +45,16 @@ class ReadAlbumsRemoteOperation @JvmOverloads constructor( ) val isSuccess = status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK if (isSuccess) { - val multiStatus = propfind.responseBodyAsMultiStatus - val albumsList: MutableList = ArrayList() - for (response in multiStatus.responses) { - val st = response.status[0].statusCode - if (st == HttpStatus.SC_OK) { - val entry = PhotoAlbumEntry(response) - albumsList.add(entry) - } - } + val albumsList = propfind.responseBodyAsMultiStatus.responses + .filter { it.status[0].statusCode == HttpStatus.SC_OK } + .map { res -> PhotoAlbumEntry(res) } result = RemoteOperationResult>(true, propfind) - result.setResultData(albumsList) + result.resultData = albumsList } else { result = RemoteOperationResult>(false, propfind) client.exhaustResponse(propfind.responseBodyAsStream) } - } catch (var13: Exception) { - val e = var13 + } catch (e: Exception) { result = RemoteOperationResult>(e) Log_OC.e(TAG, "Read album failed: ${result.logMessage}", result.exception) } finally { diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt index bff94b2040..f1026480d0 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt @@ -13,7 +13,6 @@ 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.Log_OC -import com.owncloud.android.lib.resources.albums.RemoveAlbumRemoteOperation import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.client.methods.DeleteMethod diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt index fe1def4129..27fe07c8a0 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt @@ -13,7 +13,6 @@ 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.Log_OC -import com.owncloud.android.lib.resources.albums.RenameAlbumRemoteOperation import org.apache.jackrabbit.webdav.client.methods.MoveMethod class RenameAlbumRemoteOperation @JvmOverloads constructor( @@ -52,7 +51,6 @@ class RenameAlbumRemoteOperation @JvmOverloads constructor( "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}" ) client.exhaustResponse(move.responseBodyAsStream) - return result } } catch (e: Exception) { result = RemoteOperationResult(e) @@ -61,7 +59,6 @@ class RenameAlbumRemoteOperation @JvmOverloads constructor( "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}", e ) - return result } finally { move?.releaseConnection() } From e45d42a3865555126f50c40ea737622d4ddd34dc Mon Sep 17 00:00:00 2001 From: A117870935 Date: Fri, 6 Jun 2025 14:12:51 +0530 Subject: [PATCH 06/15] Fix Signed-off-by: Surinder Kumar --- .../albums/CopyFileToAlbumRemoteOperation.kt | 95 ++++++------- .../albums/CreateNewAlbumRemoteOperation.kt | 94 ++++++------- .../lib/resources/albums/PhotoAlbumEntry.kt | 29 ++-- .../albums/ReadAlbumItemsRemoteOperation.kt | 130 +++++++++--------- .../albums/ReadAlbumsRemoteOperation.kt | 96 ++++++------- .../albums/RemoveAlbumFileRemoteOperation.kt | 75 +++++----- .../albums/RemoveAlbumRemoteOperation.kt | 91 ++++++------ .../albums/RenameAlbumRemoteOperation.kt | 101 +++++++------- .../ToggleAlbumFavoriteRemoteOperation.kt | 93 +++++++------ .../resources/albums/PhotoAlbumEntryTest.kt | 3 +- 10 files changed, 416 insertions(+), 391 deletions(-) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt index 416cae6f8c..8d7971b91b 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt @@ -39,60 +39,62 @@ class CopyFileToAlbumRemoteOperation @JvmOverloads constructor( * @param client Client object to communicate with the remote ownCloud server. */ @Deprecated("Deprecated in Java") + @Suppress("TooGenericExceptionCaught") override fun run(client: OwnCloudClient): RemoteOperationResult { /** check parameters */ + var result: RemoteOperationResult if (mTargetRemotePath == mSrcRemotePath) { // nothing to do! - return RemoteOperationResult(ResultCode.OK) - } + result = RemoteOperationResult(ResultCode.OK) + } else if (mTargetRemotePath.startsWith(mSrcRemotePath)) { + result = RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT) + } else { + /** perform remote operation */ + var copyMethod: CopyMethod? = null + try { + copyMethod = CopyMethod( + client.getFilesDavUri(this.mSrcRemotePath), + "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ + WebdavUtils.encodePath( + mTargetRemotePath + ) + }", + false + ) + val status = client.executeMethod( + copyMethod, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) - if (mTargetRemotePath.startsWith(mSrcRemotePath)) { - return RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT) - } + /** process response */ + result = when (status) { + HttpStatus.SC_MULTI_STATUS -> processPartialError(copyMethod) + HttpStatus.SC_PRECONDITION_FAILED -> { + client.exhaustResponse(copyMethod.responseBodyAsStream) + RemoteOperationResult(ResultCode.INVALID_OVERWRITE) + } - /** perform remote operation */ - var copyMethod: CopyMethod? = null - var result: RemoteOperationResult - try { - copyMethod = CopyMethod( - client.getFilesDavUri(this.mSrcRemotePath), - "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ - WebdavUtils.encodePath( - mTargetRemotePath - ) - }", - false - ) - val status = client.executeMethod( - copyMethod, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) + else -> { + client.exhaustResponse(copyMethod.responseBodyAsStream) + RemoteOperationResult(isSuccess(status), copyMethod) + } + } - /** process response */ - if (status == HttpStatus.SC_MULTI_STATUS) { - result = processPartialError(copyMethod) - } else if (status == HttpStatus.SC_PRECONDITION_FAILED) { - result = RemoteOperationResult(ResultCode.INVALID_OVERWRITE) - client.exhaustResponse(copyMethod.responseBodyAsStream) - } else { - result = RemoteOperationResult(isSuccess(status), copyMethod) - client.exhaustResponse(copyMethod.responseBodyAsStream) + Log.i( + TAG, + "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}" + ) + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log.e( + TAG, + "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}", e + ) + } finally { + copyMethod?.releaseConnection() } - - Log.i( - TAG, - "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}" - ) - } catch (e: Exception) { - result = RemoteOperationResult(e) - Log.e( - TAG, - "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}", e - ) - } finally { - copyMethod?.releaseConnection() } return result @@ -128,7 +130,7 @@ class CopyFileToAlbumRemoteOperation @JvmOverloads constructor( var i = 0 while (i < responses.size && !failFound) { status = responses[i].status - failFound = (!status.isNullOrEmpty() && status[0].statusCode > 299 + failFound = (!status.isNullOrEmpty() && status[0].statusCode > FAILED_STATUS_CODE ) i++ } @@ -147,5 +149,6 @@ class CopyFileToAlbumRemoteOperation @JvmOverloads constructor( companion object { private val TAG: String = CopyFileToAlbumRemoteOperation::class.java.simpleName + private const val FAILED_STATUS_CODE = 299 } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt index 946b58e80f..23f98b9b95 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt @@ -13,58 +13,60 @@ 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.Log_OC -import com.owncloud.android.lib.resources.albums.CreateNewAlbumRemoteOperation import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.client.methods.MkColMethod -class CreateNewAlbumRemoteOperation @JvmOverloads constructor( - val newAlbumName: String, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut -) : - RemoteOperation() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Deprecated("Deprecated in Java") - override fun run(client: OwnCloudClient): RemoteOperationResult { - var mkCol: MkColMethod? = null - var result: RemoteOperationResult - try { - mkCol = MkColMethod( - "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ - WebdavUtils.encodePath( - newAlbumName +class CreateNewAlbumRemoteOperation + @JvmOverloads + constructor( + val newAlbumName: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation() { + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Deprecated("Deprecated in Java") + @Suppress("TooGenericExceptionCaught") + override fun run(client: OwnCloudClient): RemoteOperationResult { + var mkCol: MkColMethod? = null + var result: RemoteOperationResult + try { + mkCol = + MkColMethod( + "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ + WebdavUtils.encodePath( + newAlbumName + ) + }" ) - }" - ) - client.executeMethod( - mkCol, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - if (HttpStatus.SC_METHOD_NOT_ALLOWED == mkCol.statusCode) { - result = - RemoteOperationResult(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS) - } else { - result = RemoteOperationResult(mkCol.succeeded(), mkCol) - result.resultData = null + client.executeMethod( + mkCol, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + if (HttpStatus.SC_METHOD_NOT_ALLOWED == mkCol.statusCode) { + result = + RemoteOperationResult(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS) + } else { + result = RemoteOperationResult(mkCol.succeeded(), mkCol) + result.resultData = null + } + + Log_OC.d(TAG, "Create album $newAlbumName : ${result.logMessage}") + client.exhaustResponse(mkCol.responseBodyAsStream) + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e(TAG, "Create album $newAlbumName : ${result.logMessage}", e) + } finally { + mkCol?.releaseConnection() } - Log_OC.d(TAG, "Create album $newAlbumName : ${result.logMessage}") - client.exhaustResponse(mkCol.responseBodyAsStream) - } catch (e: Exception) { - result = RemoteOperationResult(e) - Log_OC.e(TAG, "Create album $newAlbumName : ${result.logMessage}", e) - } finally { - mkCol?.releaseConnection() + return result } - return result - } - - companion object { - private val TAG: String = CreateNewAlbumRemoteOperation::class.java.simpleName + companion object { + private val TAG: String = CreateNewAlbumRemoteOperation::class.java.simpleName + } } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt index be43aac3f7..734a9c7f21 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt @@ -19,7 +19,9 @@ import java.text.SimpleDateFormat import java.util.Date import java.util.Locale -class PhotoAlbumEntry(response: MultiStatusResponse) { +class PhotoAlbumEntry( + response: MultiStatusResponse +) { val href: String val lastPhoto: Long val nbItems: Int @@ -28,6 +30,7 @@ class PhotoAlbumEntry(response: MultiStatusResponse) { companion object { private val dateFormat = SimpleDateFormat("MMM yyyy", Locale.US) + private const val MILLIS = 1000L } init { @@ -42,27 +45,28 @@ class PhotoAlbumEntry(response: MultiStatusResponse) { this.dateRange = parseString(properties, WebdavEntry.PROPERTY_DATE_RANGE) } - private fun parseString(props: DavPropertySet, name: String): String? { + private fun parseString( + props: DavPropertySet, + name: String + ): String? { val propName = DavPropertyName.create(name, Namespace.getNamespace("nc", WebdavEntry.NAMESPACE_NC)) val prop = props[propName] return if (prop != null && prop.value != null) prop.value.toString() else null } - private fun parseInt(value: String?): Int { - return try { + private fun parseInt(value: String?): Int = + try { value?.toInt() ?: 0 } catch (_: NumberFormatException) { 0 } - } - private fun parseLong(value: String?): Long { - return try { + private fun parseLong(value: String?): Long = + try { value?.toLong() ?: 0L } catch (_: NumberFormatException) { 0L } - } val albumName: String get() { @@ -79,13 +83,14 @@ class PhotoAlbumEntry(response: MultiStatusResponse) { return try { val obj = JSONObject(dateRange ?: return dateFormat.format(currentDate)) val startTimestamp = obj.optLong("start", 0) - if (startTimestamp > 0) - dateFormat.format(Date(startTimestamp * 1000L)) - else + if (startTimestamp > 0) { + dateFormat.format(Date(startTimestamp * MILLIS)) + } else { dateFormat.format(currentDate) + } } catch (e: JSONException) { e.printStackTrace() dateFormat.format(currentDate) } } -} \ No newline at end of file +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt index 254ec49215..bb586bb04e 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt @@ -19,83 +19,77 @@ import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.DavConstants import org.apache.jackrabbit.webdav.client.methods.PropFindMethod -class ReadAlbumItemsRemoteOperation @JvmOverloads constructor( - private val mRemotePath: String, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut -) : - RemoteOperation>() { - @Deprecated("Deprecated in Java") - override fun run(client: OwnCloudClient): RemoteOperationResult> { - var result: RemoteOperationResult>? = null - var query: PropFindMethod? = null - val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ - WebdavUtils.encodePath( - mRemotePath - ) - }" - try { - // remote request - query = PropFindMethod( - url, - WebdavUtils.getAllPropSet(), // PropFind Properties - DavConstants.DEPTH_1 - ) - val status = client.executeMethod( - query, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) +class ReadAlbumItemsRemoteOperation + @JvmOverloads + constructor( + private val mRemotePath: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation>() { + @Deprecated("Deprecated in Java") + @Suppress("TooGenericExceptionCaught") + override fun run(client: OwnCloudClient): RemoteOperationResult> { + var result: RemoteOperationResult>? = null + var query: PropFindMethod? = null + val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ + WebdavUtils.encodePath( + mRemotePath + ) + }" + try { + // remote request + query = + PropFindMethod( + url, + WebdavUtils.getAllPropSet(), // PropFind Properties + DavConstants.DEPTH_1 + ) + val status = + client.executeMethod( + query, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) - // check and process response - val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) + // check and process response + val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) - if (isSuccess) { - // get data from remote folder - val dataInServer = query.responseBodyAsMultiStatus - val mFolderAndFiles = WebDavFileUtils().readAlbumData(dataInServer, client) + result = + if (isSuccess) { + // get data from remote folder + val dataInServer = query.responseBodyAsMultiStatus + val mFolderAndFiles = WebDavFileUtils().readAlbumData(dataInServer, client) - // Result of the operation - result = RemoteOperationResult(true, query) - // Add data to the result - if (result.isSuccess) { - result.resultData = mFolderAndFiles - } - } else { - // synchronization failed - client.exhaustResponse(query.responseBodyAsStream) - result = RemoteOperationResult(false, query) - } - } catch (e: Exception) { - result = RemoteOperationResult(e) - } finally { - query?.releaseConnection() + // Result of the operation + RemoteOperationResult>(true, query).apply { + // Add data to the result + resultData = mFolderAndFiles + } + } else { + // synchronization failed + client.exhaustResponse(query.responseBodyAsStream) + RemoteOperationResult(false, query) + } + } catch (e: Exception) { + result = RemoteOperationResult(e) + } finally { + query?.releaseConnection() - if (result == null) { - result = RemoteOperationResult>(Exception("unknown error")) - Log_OC.e( - TAG, - "Synchronized $mRemotePath: failed" - ) - } else { + result = result ?: RemoteOperationResult>(Exception("unknown error")).also { + Log_OC.e(TAG, "Synchronized $mRemotePath: failed") + } if (result.isSuccess) { Log_OC.i(TAG, "Synchronized $mRemotePath : ${result.logMessage}") + } else if (result.isException) { + Log_OC.e(TAG, "Synchronized $mRemotePath : ${result.logMessage}", result.exception) } else { - if (result.isException) { - Log_OC.e( - TAG, "Synchronized $mRemotePath : ${result.logMessage}", - result.exception - ) - } else { - Log_OC.e(TAG, "Synchronized $mRemotePath : ${result.logMessage}") - } + Log_OC.e(TAG, "Synchronized $mRemotePath : ${result.logMessage}") } } - } - return result - } + return result + } - companion object { - private val TAG: String = ReadAlbumItemsRemoteOperation::class.java.simpleName + companion object { + private val TAG: String = ReadAlbumItemsRemoteOperation::class.java.simpleName + } } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt index 92da03c81d..b7605e1b40 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt @@ -18,53 +18,57 @@ import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.DavConstants import org.apache.jackrabbit.webdav.client.methods.PropFindMethod -class ReadAlbumsRemoteOperation @JvmOverloads constructor( - private val mAlbumRemotePath: String? = null, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut -) : - RemoteOperation>() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Deprecated("Deprecated in Java") - override fun run(client: OwnCloudClient): RemoteOperationResult> { - var propfind: PropFindMethod? = null - var result: RemoteOperationResult> - var url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" - if (!TextUtils.isEmpty(mAlbumRemotePath)) { - url += WebdavUtils.encodePath(mAlbumRemotePath) - } - try { - propfind = PropFindMethod(url, WebdavUtils.getAlbumPropSet(), DavConstants.DEPTH_1) - val status = client.executeMethod( - propfind, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - val isSuccess = status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK - if (isSuccess) { - val albumsList = propfind.responseBodyAsMultiStatus.responses - .filter { it.status[0].statusCode == HttpStatus.SC_OK } - .map { res -> PhotoAlbumEntry(res) } - result = RemoteOperationResult>(true, propfind) - result.resultData = albumsList - } else { - result = RemoteOperationResult>(false, propfind) - client.exhaustResponse(propfind.responseBodyAsStream) +class ReadAlbumsRemoteOperation + @JvmOverloads + constructor( + private val mAlbumRemotePath: String? = null, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation>() { + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Deprecated("Deprecated in Java") + @Suppress("TooGenericExceptionCaught") + override fun run(client: OwnCloudClient): RemoteOperationResult> { + var propfind: PropFindMethod? = null + var result: RemoteOperationResult> + var url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" + if (!TextUtils.isEmpty(mAlbumRemotePath)) { + url += WebdavUtils.encodePath(mAlbumRemotePath) + } + try { + propfind = PropFindMethod(url, WebdavUtils.getAlbumPropSet(), DavConstants.DEPTH_1) + val status = + client.executeMethod( + propfind, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + val isSuccess = status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK + if (isSuccess) { + val albumsList = + propfind.responseBodyAsMultiStatus.responses + .filter { it.status[0].statusCode == HttpStatus.SC_OK } + .map { res -> PhotoAlbumEntry(res) } + result = RemoteOperationResult>(true, propfind) + result.resultData = albumsList + } else { + result = RemoteOperationResult>(false, propfind) + client.exhaustResponse(propfind.responseBodyAsStream) + } + } catch (e: Exception) { + result = RemoteOperationResult>(e) + Log_OC.e(TAG, "Read album failed: ${result.logMessage}", result.exception) + } finally { + propfind?.releaseConnection() } - } catch (e: Exception) { - result = RemoteOperationResult>(e) - Log_OC.e(TAG, "Read album failed: ${result.logMessage}", result.exception) - } finally { - propfind?.releaseConnection() - } - return result - } + return result + } - companion object { - private val TAG: String = ReadAlbumsRemoteOperation::class.java.simpleName + companion object { + private val TAG: String = ReadAlbumsRemoteOperation::class.java.simpleName + } } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt index 8ffa6f1af5..843c0913db 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt @@ -17,42 +17,47 @@ import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.client.methods.DeleteMethod -class RemoveAlbumFileRemoteOperation @JvmOverloads constructor( - private val mRemotePath: String, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut -) : - RemoteOperation() { - @Deprecated("Deprecated in Java") - override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult - var delete: DeleteMethod? = null - val webDavUrl = "${client.davUri}/photos/" - val encodedPath = ("${client.userId}${Uri.encode(this.mRemotePath)}").replace("%2F", "/") - val fullFilePath = "$webDavUrl$encodedPath" +class RemoveAlbumFileRemoteOperation + @JvmOverloads + constructor( + private val mRemotePath: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation() { + @Deprecated("Deprecated in Java") + @Suppress("TooGenericExceptionCaught") + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + var delete: DeleteMethod? = null + val webDavUrl = "${client.davUri}/photos/" + val encodedPath = ("${client.userId}${Uri.encode(this.mRemotePath)}").replace("%2F", "/") + val fullFilePath = "$webDavUrl$encodedPath" - try { - delete = DeleteMethod(fullFilePath) - val status = client.executeMethod( - delete, - sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut - ) - delete.responseBodyAsString - result = RemoteOperationResult( - delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, - delete - ) - Log_OC.i(TAG, "Remove ${this.mRemotePath} : ${result.logMessage}") - } catch (e: Exception) { - result = RemoteOperationResult(e) - Log_OC.e(TAG, "Remove ${this.mRemotePath} : ${result.logMessage}", e) - } finally { - delete?.releaseConnection() - } + try { + delete = DeleteMethod(fullFilePath) + val status = + client.executeMethod( + delete, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + delete.responseBodyAsString + result = + RemoteOperationResult( + delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, + delete + ) + Log_OC.i(TAG, "Remove ${this.mRemotePath} : ${result.logMessage}") + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e(TAG, "Remove ${this.mRemotePath} : ${result.logMessage}", e) + } finally { + delete?.releaseConnection() + } - return result - } + return result + } - companion object { - private val TAG: String = RemoveFileRemoteOperation::class.java.simpleName + companion object { + private val TAG: String = RemoveFileRemoteOperation::class.java.simpleName + } } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt index f1026480d0..c62183c2f8 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt @@ -16,51 +16,56 @@ import com.owncloud.android.lib.common.utils.Log_OC import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.client.methods.DeleteMethod -class RemoveAlbumRemoteOperation @JvmOverloads constructor( - private val albumName: String, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut -) : - RemoteOperation() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Deprecated("Deprecated in Java") - override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult - var delete: DeleteMethod? = null +class RemoveAlbumRemoteOperation + @JvmOverloads + constructor( + private val albumName: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation() { + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Deprecated("Deprecated in Java") + @Suppress("TooGenericExceptionCaught") + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + var delete: DeleteMethod? = null - try { - delete = DeleteMethod( - "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ - WebdavUtils.encodePath( - albumName + try { + delete = + DeleteMethod( + "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ + WebdavUtils.encodePath( + albumName + ) + }" ) - }" - ) - val status = client.executeMethod( - delete, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - delete.responseBodyAsString - result = RemoteOperationResult( - delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, - delete - ) - Log_OC.i(TAG, "Remove ${this.albumName} : ${result.logMessage}") - } catch (e: Exception) { - result = RemoteOperationResult(e) - Log_OC.e(TAG, "Remove ${this.albumName} : ${result.logMessage}", e) - } finally { - delete?.releaseConnection() - } + val status = + client.executeMethod( + delete, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + delete.responseBodyAsString + result = + RemoteOperationResult( + delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, + delete + ) + Log_OC.i(TAG, "Remove ${this.albumName} : ${result.logMessage}") + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e(TAG, "Remove ${this.albumName} : ${result.logMessage}", e) + } finally { + delete?.releaseConnection() + } - return result - } + return result + } - companion object { - private val TAG: String = RemoveAlbumRemoteOperation::class.java.simpleName + companion object { + private val TAG: String = RemoveAlbumRemoteOperation::class.java.simpleName + } } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt index 27fe07c8a0..78ad87671a 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt @@ -15,58 +15,63 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.utils.Log_OC import org.apache.jackrabbit.webdav.client.methods.MoveMethod -class RenameAlbumRemoteOperation @JvmOverloads constructor( - private val mOldRemotePath: String, - val newAlbumName: String, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut -) : - RemoteOperation() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Deprecated("Deprecated in Java") - override fun run(client: OwnCloudClient): RemoteOperationResult? { - var result: RemoteOperationResult? = null - var move: MoveMethod? = null - val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" - try { - if (this.newAlbumName != this.mOldRemotePath) { - move = MoveMethod( - "$url${WebdavUtils.encodePath(mOldRemotePath)}", "$url${ - WebdavUtils.encodePath( - newAlbumName +class RenameAlbumRemoteOperation + @JvmOverloads + constructor( + private val mOldRemotePath: String, + val newAlbumName: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation() { + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Deprecated("Deprecated in Java") + @Suppress("TooGenericExceptionCaught") + override fun run(client: OwnCloudClient): RemoteOperationResult? { + var result: RemoteOperationResult? = null + var move: MoveMethod? = null + val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" + try { + if (this.newAlbumName != this.mOldRemotePath) { + move = + MoveMethod( + "$url${WebdavUtils.encodePath(mOldRemotePath)}", + "$url${ + WebdavUtils.encodePath( + newAlbumName + ) + }", + true ) - }", true - ) - client.executeMethod( - move, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - result = RemoteOperationResult(move.succeeded(), move) - Log_OC.i( + client.executeMethod( + move, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + result = RemoteOperationResult(move.succeeded(), move) + Log_OC.i( + TAG, + "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}" + ) + client.exhaustResponse(move.responseBodyAsStream) + } + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e( TAG, - "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}" + "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}", + e ) - client.exhaustResponse(move.responseBodyAsStream) + } finally { + move?.releaseConnection() } - } catch (e: Exception) { - result = RemoteOperationResult(e) - Log_OC.e( - TAG, - "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}", - e - ) - } finally { - move?.releaseConnection() - } - return result - } + return result + } - companion object { - private val TAG: String = RenameAlbumRemoteOperation::class.java.simpleName + companion object { + private val TAG: String = RenameAlbumRemoteOperation::class.java.simpleName + } } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt index be1952b338..c503344007 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt @@ -21,53 +21,56 @@ import org.apache.jackrabbit.webdav.property.DefaultDavProperty import org.apache.jackrabbit.webdav.xml.Namespace import java.io.IOException -class ToggleAlbumFavoriteRemoteOperation @JvmOverloads constructor( - private val makeItFavorited: Boolean, - private val filePath: String, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut -) : - RemoteOperation() { - @Deprecated("Deprecated in Java") - override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult - var propPatchMethod: PropPatchMethod? = null - val newProps = DavPropertySet() - val removeProperties = DavPropertyNameSet() - if (this.makeItFavorited) { - val favoriteProperty = DefaultDavProperty( - "oc:favorite", - "1", - Namespace.getNamespace(WebdavEntry.NAMESPACE_OC) - ) - newProps.add(favoriteProperty) - } else { - removeProperties.add("oc:favorite", Namespace.getNamespace(WebdavEntry.NAMESPACE_OC)) - } +class ToggleAlbumFavoriteRemoteOperation + @JvmOverloads + constructor( + private val makeItFavorited: Boolean, + private val filePath: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation() { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + var propPatchMethod: PropPatchMethod? = null + val newProps = DavPropertySet() + val removeProperties = DavPropertyNameSet() + if (this.makeItFavorited) { + val favoriteProperty = + DefaultDavProperty( + "oc:favorite", + "1", + Namespace.getNamespace(WebdavEntry.NAMESPACE_OC) + ) + newProps.add(favoriteProperty) + } else { + removeProperties.add("oc:favorite", Namespace.getNamespace(WebdavEntry.NAMESPACE_OC)) + } - val webDavUrl = "${client.davUri}/photos/" - val encodedPath = ("${client.userId}${Uri.encode(this.filePath)}").replace("%2F", "/") - val fullFilePath = "$webDavUrl$encodedPath" + val webDavUrl = "${client.davUri}/photos/" + val encodedPath = ("${client.userId}${Uri.encode(this.filePath)}").replace("%2F", "/") + val fullFilePath = "$webDavUrl$encodedPath" - try { - propPatchMethod = PropPatchMethod(fullFilePath, newProps, removeProperties) - val status = client.executeMethod( - propPatchMethod, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) - if (isSuccess) { - result = RemoteOperationResult(true, status, propPatchMethod.responseHeaders) - } else { - client.exhaustResponse(propPatchMethod.responseBodyAsStream) - result = RemoteOperationResult(false, status, propPatchMethod.responseHeaders) + try { + propPatchMethod = PropPatchMethod(fullFilePath, newProps, removeProperties) + val status = + client.executeMethod( + propPatchMethod, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) + if (isSuccess) { + result = RemoteOperationResult(true, status, propPatchMethod.responseHeaders) + } else { + client.exhaustResponse(propPatchMethod.responseBodyAsStream) + result = RemoteOperationResult(false, status, propPatchMethod.responseHeaders) + } + } catch (e: IOException) { + result = RemoteOperationResult(e) + } finally { + propPatchMethod?.releaseConnection() } - } catch (e: IOException) { - result = RemoteOperationResult(e) - } finally { - propPatchMethod?.releaseConnection() - } - return result + return result + } } -} \ No newline at end of file diff --git a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt index f7757395d8..ad46c92046 100644 --- a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt +++ b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt @@ -12,7 +12,6 @@ import org.junit.Assert.assertEquals import org.junit.Test class PhotoAlbumEntryTest { - @Test fun testAlbumName_withTrailingSlash() { val entry = createTestEntry("/remote.php/dav/photos/user_id/albums/vacation2024/") @@ -54,4 +53,4 @@ class PhotoAlbumEntryTest { val response = MultiStatusResponse(href, 200) return PhotoAlbumEntry(response) } -} \ No newline at end of file +} From 6bc9757ecad7a124da6df08dee9c1b24f2f0ad91 Mon Sep 17 00:00:00 2001 From: Surinder Kumar Date: Thu, 19 Jun 2025 20:15:03 +0530 Subject: [PATCH 07/15] Fix Copyright Signed-off-by: Surinder Kumar --- .../lib/resources/albums/CopyFileToAlbumRemoteOperation.kt | 2 +- .../lib/resources/albums/CreateNewAlbumRemoteOperation.kt | 2 +- .../owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt | 2 +- .../lib/resources/albums/ReadAlbumItemsRemoteOperation.kt | 2 +- .../android/lib/resources/albums/ReadAlbumsRemoteOperation.kt | 2 +- .../lib/resources/albums/RemoveAlbumFileRemoteOperation.kt | 2 +- .../android/lib/resources/albums/RemoveAlbumRemoteOperation.kt | 2 +- .../android/lib/resources/albums/RenameAlbumRemoteOperation.kt | 2 +- .../lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt | 2 +- .../android/lib/resources/albums/PhotoAlbumEntryTest.kt | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt index 8d7971b91b..9272a98208 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt @@ -2,7 +2,7 @@ * Nextcloud Android Library * * SPDX-FileCopyrightText: 2025 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt index 23f98b9b95..6d69d53a05 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt @@ -2,7 +2,7 @@ * Nextcloud Android Library * * SPDX-FileCopyrightText: 2025 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt index 734a9c7f21..168ec0d6be 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt @@ -2,7 +2,7 @@ * Nextcloud Android Library * * SPDX-FileCopyrightText: 2025 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt index bb586bb04e..40deed2e70 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt @@ -2,7 +2,7 @@ * Nextcloud Android Library * * SPDX-FileCopyrightText: 2025 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt index b7605e1b40..c472b34e8c 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt @@ -2,7 +2,7 @@ * Nextcloud Android Library * * SPDX-FileCopyrightText: 2025 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt index 843c0913db..51c5ee2efc 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt @@ -2,7 +2,7 @@ * Nextcloud Android Library * * SPDX-FileCopyrightText: 2025 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt index c62183c2f8..b9114ac8f2 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt @@ -2,7 +2,7 @@ * Nextcloud Android Library * * SPDX-FileCopyrightText: 2025 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt index 78ad87671a..6dff04e913 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt @@ -2,7 +2,7 @@ * Nextcloud Android Library * * SPDX-FileCopyrightText: 2025 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt index c503344007..b2253c69ca 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt @@ -2,7 +2,7 @@ * Nextcloud Android Library * * SPDX-FileCopyrightText: 2025 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums diff --git a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt index ad46c92046..f80c20d70a 100644 --- a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt +++ b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt @@ -2,7 +2,7 @@ * Nextcloud Android Library * * SPDX-FileCopyrightText: 2025 TSI-mc - * SPDX-License-Identifier: AGPL-3.0-or-later + * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums From 4590036481eaa8ebb6a51c3c140c4c37780c17a4 Mon Sep 17 00:00:00 2001 From: Surinder Kumar Date: Thu, 10 Jul 2025 13:56:16 +0530 Subject: [PATCH 08/15] Fix collections presize Signed-off-by: Surinder Kumar --- .../com/owncloud/android/lib/common/utils/WebDavFileUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java b/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java index bcfb80c55d..0ca9d16013 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java +++ b/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java @@ -81,8 +81,8 @@ public List readAlbumData(MultiStatus remoteData, OwnCloudClient cli return Collections.emptyList(); } - List files = new ArrayList<>(); final var responses = remoteData.getResponses(); + List files = new ArrayList<>(Math.max(0, responses.length - 1)); // reading from 1 as 0th item will be just the root album path for (int i = 1; i < responses.length; i++) { From 3caf01d4eaf1ab11b6f323f6ce27f5473eaee319 Mon Sep 17 00:00:00 2001 From: Surinder Kumar Date: Thu, 22 Jan 2026 21:08:36 +0530 Subject: [PATCH 09/15] minor albums api fixes Signed-off-by: Surinder Kumar --- .../lib/common/utils/WebDavFileUtils.java | 11 +- .../albums/CopyFileToAlbumRemoteOperation.kt | 4 +- .../lib/resources/albums/PhotoAlbumEntry.kt | 34 +++--- .../albums/RemoveAlbumFileRemoteOperation.kt | 10 +- .../albums/RemoveAlbumRemoteOperation.kt | 3 +- .../albums/RenameAlbumRemoteOperation.kt | 115 ++++++++++-------- .../ToggleAlbumFavoriteRemoteOperation.kt | 26 ++-- 7 files changed, 109 insertions(+), 94 deletions(-) diff --git a/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java b/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java index 0ca9d16013..b6e24a32a6 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java +++ b/library/src/main/java/com/owncloud/android/lib/common/utils/WebDavFileUtils.java @@ -1,7 +1,7 @@ /* * Nextcloud Android Library * - * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors * SPDX-FileCopyrightText: 2017 Mario Danic * SPDX-License-Identifier: MIT @@ -72,11 +72,16 @@ public ArrayList readData(MultiStatus remoteData, * folder and its direct children. * @param client Client instance to the remote server where the data were * retrieved. - * @return + * @return list of remote file */ public List readAlbumData(MultiStatus remoteData, OwnCloudClient client) { String baseUrl = client.getBaseUri() + "/remote.php/dav/photos/" + client.getUserId(); - String encodedPath = Uri.parse(baseUrl).getEncodedPath(); + Uri uri = Uri.parse(baseUrl); + if (uri == null) { + return Collections.emptyList(); + } + + String encodedPath = uri.getEncodedPath(); if (encodedPath == null) { return Collections.emptyList(); } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt index 9272a98208..10a005380f 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt @@ -1,7 +1,7 @@ /* * Nextcloud Android Library * - * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums @@ -47,8 +47,6 @@ class CopyFileToAlbumRemoteOperation @JvmOverloads constructor( if (mTargetRemotePath == mSrcRemotePath) { // nothing to do! result = RemoteOperationResult(ResultCode.OK) - } else if (mTargetRemotePath.startsWith(mSrcRemotePath)) { - result = RemoteOperationResult(ResultCode.INVALID_COPY_INTO_DESCENDANT) } else { /** perform remote operation */ var copyMethod: CopyMethod? = null diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt index 168ec0d6be..366b5c0a66 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt @@ -1,7 +1,7 @@ /* * Nextcloud Android Library * - * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ @@ -15,9 +15,8 @@ import org.apache.jackrabbit.webdav.property.DavPropertySet import org.apache.jackrabbit.webdav.xml.Namespace import org.json.JSONException import org.json.JSONObject -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale +import java.net.URLDecoder +import java.nio.charset.StandardCharsets class PhotoAlbumEntry( response: MultiStatusResponse @@ -29,7 +28,6 @@ class PhotoAlbumEntry( private val dateRange: String? companion object { - private val dateFormat = SimpleDateFormat("MMM yyyy", Locale.US) private const val MILLIS = 1000L } @@ -70,27 +68,27 @@ class PhotoAlbumEntry( val albumName: String get() { - return href - .removeSuffix("/") - .substringAfterLast("/") - .takeIf { it.isNotEmpty() } ?: "" + // use decoder to show correct path + return URLDecoder.decode( + href + .removeSuffix("/") + .substringAfterLast("/") + .takeIf { it.isNotEmpty() } ?: "", StandardCharsets.UTF_8.name()) } - val createdDate: String + val createdDate: Long get() { - val currentDate = Date(System.currentTimeMillis()) - + val defaultTimeStamp = System.currentTimeMillis() return try { - val obj = JSONObject(dateRange ?: return dateFormat.format(currentDate)) + val obj = JSONObject(dateRange ?: return defaultTimeStamp) val startTimestamp = obj.optLong("start", 0) if (startTimestamp > 0) { - dateFormat.format(Date(startTimestamp * MILLIS)) + startTimestamp * MILLIS } else { - dateFormat.format(currentDate) + defaultTimeStamp } - } catch (e: JSONException) { - e.printStackTrace() - dateFormat.format(currentDate) + } catch (_: JSONException) { + defaultTimeStamp } } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt index 51c5ee2efc..4d9f53977f 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt @@ -1,7 +1,7 @@ /* * Nextcloud Android Library * - * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums @@ -10,6 +10,7 @@ import android.net.Uri import com.nextcloud.common.SessionTimeOut import com.nextcloud.common.defaultSessionTimeOut import com.owncloud.android.lib.common.OwnCloudClient +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.Log_OC @@ -29,7 +30,7 @@ class RemoveAlbumFileRemoteOperation var result: RemoteOperationResult var delete: DeleteMethod? = null val webDavUrl = "${client.davUri}/photos/" - val encodedPath = ("${client.userId}${Uri.encode(this.mRemotePath)}").replace("%2F", "/") + val encodedPath = "${client.userId}${WebdavUtils.encodePath(this.mRemotePath)}" val fullFilePath = "$webDavUrl$encodedPath" try { @@ -40,15 +41,14 @@ class RemoveAlbumFileRemoteOperation sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut ) - delete.responseBodyAsString result = - RemoteOperationResult( + RemoteOperationResult( delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, delete ) Log_OC.i(TAG, "Remove ${this.mRemotePath} : ${result.logMessage}") } catch (e: Exception) { - result = RemoteOperationResult(e) + result = RemoteOperationResult(e) Log_OC.e(TAG, "Remove ${this.mRemotePath} : ${result.logMessage}", e) } finally { delete?.releaseConnection() diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt index b9114ac8f2..4142a64e76 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt @@ -1,7 +1,7 @@ /* * Nextcloud Android Library * - * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums @@ -48,7 +48,6 @@ class RemoveAlbumRemoteOperation sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut ) - delete.responseBodyAsString result = RemoteOperationResult( delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt index 6dff04e913..c5c622f12a 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt @@ -1,7 +1,7 @@ /* * Nextcloud Android Library * - * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums @@ -13,65 +13,72 @@ 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.Log_OC +import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.client.methods.MoveMethod class RenameAlbumRemoteOperation - @JvmOverloads - constructor( - private val mOldRemotePath: String, - val newAlbumName: String, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut - ) : RemoteOperation() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Deprecated("Deprecated in Java") - @Suppress("TooGenericExceptionCaught") - override fun run(client: OwnCloudClient): RemoteOperationResult? { - var result: RemoteOperationResult? = null - var move: MoveMethod? = null - val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" - try { - if (this.newAlbumName != this.mOldRemotePath) { - move = - MoveMethod( - "$url${WebdavUtils.encodePath(mOldRemotePath)}", - "$url${ - WebdavUtils.encodePath( - newAlbumName - ) - }", - true +@JvmOverloads +constructor( + private val mOldRemotePath: String, + val newAlbumName: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut +) : RemoteOperation() { + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Deprecated("Deprecated in Java") + @Suppress("TooGenericExceptionCaught") + override fun run(client: OwnCloudClient): RemoteOperationResult? { + if (this.newAlbumName == this.mOldRemotePath) { + return RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + } + + var result: RemoteOperationResult? = null + var move: MoveMethod? = null + val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" + try { + move = + MoveMethod( + "$url${WebdavUtils.encodePath(mOldRemotePath)}", + "$url${ + WebdavUtils.encodePath( + newAlbumName ) - client.executeMethod( - move, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - result = RemoteOperationResult(move.succeeded(), move) - Log_OC.i( - TAG, - "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}" - ) - client.exhaustResponse(move.responseBodyAsStream) - } - } catch (e: Exception) { - result = RemoteOperationResult(e) - Log_OC.e( - TAG, - "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}", - e + }", + false ) - } finally { - move?.releaseConnection() + client.executeMethod( + move, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + result = RemoteOperationResult(move.succeeded(), move) + Log_OC.i( + TAG, + "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}" + ) + // album name already exist + if (!result.isSuccess && result.httpCode == HttpStatus.SC_PRECONDITION_FAILED) { + result = RemoteOperationResult(RemoteOperationResult.ResultCode.INVALID_OVERWRITE) } - - return result + client.exhaustResponse(move.responseBodyAsStream) + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e( + TAG, + "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}", + e + ) + } finally { + move?.releaseConnection() } - companion object { - private val TAG: String = RenameAlbumRemoteOperation::class.java.simpleName - } + return result + } + + companion object { + private val TAG: String = RenameAlbumRemoteOperation::class.java.simpleName } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt index b2253c69ca..9968bee554 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt @@ -1,18 +1,19 @@ /* * Nextcloud Android Library * - * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums -import android.net.Uri import com.nextcloud.common.SessionTimeOut import com.nextcloud.common.defaultSessionTimeOut import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.network.WebdavEntry +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.resources.files.ToggleFavoriteRemoteOperation import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod import org.apache.jackrabbit.webdav.property.DavPropertyNameSet @@ -24,30 +25,37 @@ import java.io.IOException class ToggleAlbumFavoriteRemoteOperation @JvmOverloads constructor( - private val makeItFavorited: Boolean, + private val markAsFavorite: Boolean, private val filePath: String, private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut ) : RemoteOperation() { @Deprecated("Deprecated in Java") + @Suppress("ReturnCount") override fun run(client: OwnCloudClient): RemoteOperationResult { + if (filePath.isEmpty() || filePath.isBlank()) { + return RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + } + + // when file is in local db the remotePath will be actual path instead of albums path + // to perform operation we have to call files dav uri + if (!filePath.contains("/albums/")) { + return ToggleFavoriteRemoteOperation(markAsFavorite, filePath).execute(client) + } + var result: RemoteOperationResult var propPatchMethod: PropPatchMethod? = null val newProps = DavPropertySet() val removeProperties = DavPropertyNameSet() - if (this.makeItFavorited) { val favoriteProperty = DefaultDavProperty( "oc:favorite", - "1", + if(markAsFavorite) "1" else "0", Namespace.getNamespace(WebdavEntry.NAMESPACE_OC) ) newProps.add(favoriteProperty) - } else { - removeProperties.add("oc:favorite", Namespace.getNamespace(WebdavEntry.NAMESPACE_OC)) - } val webDavUrl = "${client.davUri}/photos/" - val encodedPath = ("${client.userId}${Uri.encode(this.filePath)}").replace("%2F", "/") + val encodedPath = "${client.userId}${WebdavUtils.encodePath(this.filePath)}" val fullFilePath = "$webDavUrl$encodedPath" try { From c26cec443340b01769c14b645eb2ab7b19d71abf Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 19 Feb 2026 14:28:08 +0100 Subject: [PATCH 10/15] force push Signed-off-by: alperozturk96 --- .../android/lib/resources/assistant/v2/AssistantV2Tests.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt index 67921f7695..9e9f678d6e 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt @@ -3,6 +3,7 @@ * * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors * SPDX-FileCopyrightText: 2024 Alper Ozturk + * SPDX-License-Identifier: MIT */ From 9b8a269b1bd2baaa4a9b1bb6d077980e786ab031 Mon Sep 17 00:00:00 2001 From: Surinder Kumar Date: Mon, 4 May 2026 12:42:39 +0530 Subject: [PATCH 11/15] albums link share operation added Signed-off-by: Surinder Kumar --- .../lib/resources/albums/PhotoAlbumEntry.kt | 69 +++++++++++++ .../PublicShareLinkAlbumRemoteOperation.kt | 98 +++++++++++++++++++ .../albums/ReadAlbumsRemoteOperation.kt | 4 +- .../resources/albums/PhotoAlbumEntryTest.kt | 4 +- 4 files changed, 171 insertions(+), 4 deletions(-) create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt index 366b5c0a66..c064213e11 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt @@ -8,6 +8,7 @@ package com.owncloud.android.lib.resources.albums import com.owncloud.android.lib.common.network.WebdavEntry +import com.owncloud.android.lib.resources.shares.ShareType import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.MultiStatusResponse import org.apache.jackrabbit.webdav.property.DavPropertyName @@ -15,10 +16,13 @@ import org.apache.jackrabbit.webdav.property.DavPropertySet import org.apache.jackrabbit.webdav.xml.Namespace import org.json.JSONException import org.json.JSONObject +import org.w3c.dom.Element import java.net.URLDecoder import java.nio.charset.StandardCharsets class PhotoAlbumEntry( + // required for providing album share link url + baseUri: String, response: MultiStatusResponse ) { val href: String @@ -26,12 +30,18 @@ class PhotoAlbumEntry( val nbItems: Int val location: String? private val dateRange: String? + var collaborators = arrayOf() + private set + + private var shareBaseUri: String? = null companion object { private const val MILLIS = 1000L } init { + // will be used to provide full share link for album + shareBaseUri = "$baseUri/apps/photos/public/" href = response.href @@ -41,6 +51,7 @@ class PhotoAlbumEntry( this.nbItems = parseInt(parseString(properties, WebdavEntry.PROPERTY_NB_ITEMS)) this.location = parseString(properties, WebdavEntry.PROPERTY_LOCATION) this.dateRange = parseString(properties, WebdavEntry.PROPERTY_DATE_RANGE) + parseCollaborators(properties) } private fun parseString( @@ -66,6 +77,62 @@ class PhotoAlbumEntry( 0L } + private fun parseCollaborators(properties: DavPropertySet) { + val collaboratorsProp = + properties[WebdavEntry.PROPERTY_COLLABORATORS, Namespace.getNamespace("nc", WebdavEntry.NAMESPACE_NC)] + if (collaboratorsProp != null && collaboratorsProp.value != null) { + if (collaboratorsProp.value is ArrayList<*>) { + val list = collaboratorsProp.value as ArrayList<*> + val tempList: MutableList = ArrayList() + for (i in list.indices) { + val element = list[i] as Element + val collaborator = createCollaborators(element) + tempList.add(collaborator) + } + collaborators = tempList.toTypedArray() + } else { + // single item or empty + val element = collaboratorsProp.value as Element + val collaborator = createCollaborators(element) + collaborators = arrayOf(collaborator) + } + } + } + + private fun createCollaborators(element: Element): Collaborators { + val id = extractId(element) + val label = extractLabel(element) + val shareType = extractShareType(element) + return Collaborators(id, label, shareType, "$shareBaseUri$id") + } + + private fun extractLabel(element: Element): String { + val displayName = element.getElementsByTagName(WebdavEntry.COLLABORATORS_SHARE_LABEL).item(0) + return if (displayName != null && displayName.firstChild != null) { + displayName.firstChild.nodeValue + } else { + "" + } + } + + private fun extractId(element: Element): String { + val userId = element.getElementsByTagName(WebdavEntry.SHAREES_ID).item(0) + return if (userId != null && userId.firstChild != null) { + userId.firstChild.nodeValue + } else { + "" + } + } + + private fun extractShareType(element: Element): ShareType { + val shareType = element.getElementsByTagName(WebdavEntry.SHAREES_SHARE_TYPE).item(0) + if (shareType != null && shareType.firstChild != null) { + val value = shareType.firstChild.nodeValue.toInt() + return ShareType.fromValue(value) + } + return ShareType.NO_SHARED + } + val albumName: String get() { // use decoder to show correct path @@ -92,3 +159,5 @@ class PhotoAlbumEntry( } } } + +data class Collaborators(val id: String?, val label: String?, val type: ShareType?, val shareLink: String?) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt new file mode 100644 index 0000000000..3e2a77dc77 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt @@ -0,0 +1,98 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 TSI-mc + * SPDX-License-Identifier: MIT + */ +package com.owncloud.android.lib.resources.albums + +import com.nextcloud.common.SessionTimeOut +import com.nextcloud.common.defaultSessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.network.WebdavEntry.Companion.COLLABORATORS_SHARE_LABEL +import com.owncloud.android.lib.common.network.WebdavEntry.Companion.NAMESPACE_NC +import com.owncloud.android.lib.common.network.WebdavEntry.Companion.SHAREES_ID +import com.owncloud.android.lib.common.network.WebdavEntry.Companion.SHAREES_SHARE_TYPE +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.resources.shares.ShareType +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod +import org.apache.jackrabbit.webdav.property.DavPropertyNameSet +import org.apache.jackrabbit.webdav.property.DavPropertySet +import org.apache.jackrabbit.webdav.property.DefaultDavProperty +import org.apache.jackrabbit.webdav.xml.Namespace +import org.json.JSONArray +import org.json.JSONObject +import java.io.IOException + +class PublicShareLinkAlbumRemoteOperation +@JvmOverloads +constructor( + private val albumName: String, + private val isCreateShare: Boolean, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut +) : RemoteOperation() { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + var propPatchMethod: PropPatchMethod? = null + + val collaboratorsArray = JSONArray() + + // for create new share link the data should be in json + // [{"id":"","label":"Public link","type":3}] + // for removing share pass empty array [] + if (isCreateShare) { + val collaboratorsObject = JSONObject() + // empty while creating share + collaboratorsObject.put(SHAREES_ID, "") + // default public link + collaboratorsObject.put(COLLABORATORS_SHARE_LABEL, "Public Link") + collaboratorsObject.put(SHAREES_SHARE_TYPE, ShareType.PUBLIC_LINK.value) + + collaboratorsArray.put(collaboratorsObject) + } + + val removeProperties = DavPropertyNameSet() + + val newProps = DavPropertySet() + val collaboratorsProperty = + DefaultDavProperty( + "nc:collaborators", + collaboratorsArray.toString(), + Namespace.getNamespace(NAMESPACE_NC) + ) + newProps.add(collaboratorsProperty) + + val fullUri = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ + WebdavUtils.encodePath( + albumName + ) + }" + + try { + propPatchMethod = PropPatchMethod(fullUri, newProps, removeProperties) + val status = + client.executeMethod( + propPatchMethod, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) + if (isSuccess) { + result = RemoteOperationResult(true, status, propPatchMethod.responseHeaders) + } else { + client.exhaustResponse(propPatchMethod.responseBodyAsStream) + result = RemoteOperationResult(false, status, propPatchMethod.responseHeaders) + } + } catch (e: IOException) { + result = RemoteOperationResult(e) + } finally { + propPatchMethod?.releaseConnection() + } + + return result + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt index c472b34e8c..643a4563ea 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt @@ -1,7 +1,7 @@ /* * Nextcloud Android Library * - * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-FileCopyrightText: 2025-2026 TSI-mc * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums @@ -51,7 +51,7 @@ class ReadAlbumsRemoteOperation val albumsList = propfind.responseBodyAsMultiStatus.responses .filter { it.status[0].statusCode == HttpStatus.SC_OK } - .map { res -> PhotoAlbumEntry(res) } + .map { res -> PhotoAlbumEntry(client.baseUri.toString(), res) } result = RemoteOperationResult>(true, propfind) result.resultData = albumsList } else { diff --git a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt index f80c20d70a..a2ddd83fc5 100644 --- a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt +++ b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt @@ -1,7 +1,7 @@ /* * Nextcloud Android Library * - * SPDX-FileCopyrightText: 2025 TSI-mc + * SPDX-FileCopyrightText: 2025-2026 TSI-mc * SPDX-License-Identifier: MIT */ @@ -51,6 +51,6 @@ class PhotoAlbumEntryTest { // Helper method to create a stub entry private fun createTestEntry(href: String): PhotoAlbumEntry { val response = MultiStatusResponse(href, 200) - return PhotoAlbumEntry(response) + return PhotoAlbumEntry("https://www.example.com/",response) } } From 634cafa2eee393fb00b1a4d3affa7d096890f55c Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 11 Aug 2026 08:25:41 +0200 Subject: [PATCH 12/15] wip Signed-off-by: alperozturk96 --- .../com/owncloud/android/lib/common/network/WebdavEntry.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt index 1c5026aacc..45178b9ed2 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavEntry.kt @@ -684,6 +684,12 @@ class WebdavEntry constructor( const val PROPERTY_QUOTA_AVAILABLE_BYTES = "quota-available-bytes" const val EXTENDED_PROPERTY_SHARE_TYPES = "share-types" const val EXTENDED_PROPERTY_SHARE_ATTRIBUTES = "share-attributes" + const val PROPERTY_LAST_PHOTO = "last-photo" + const val PROPERTY_NB_ITEMS = "nbItems" + const val PROPERTY_LOCATION = "location" + const val PROPERTY_DATE_RANGE = "dateRange" + const val PROPERTY_COLLABORATORS = "collaborators" + const val COLLABORATORS_SHARE_LABEL = "label" private const val IS_ENCRYPTED = "1" private const val CODE_PROP_NOT_FOUND = 404 } From 0ef5738916eb0f0e71200591bcb61d198bad53d5 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 12 Aug 2026 14:19:39 +0200 Subject: [PATCH 13/15] wip Signed-off-by: alperozturk96 --- library/build.gradle | 2 +- .../android/lib/common/network/WebdavUtils.java | 17 +++++++++++++++++ .../albums/ReadAlbumItemsRemoteOperation.kt | 2 +- sample_client/build.gradle | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 396e807c5c..8e19e2a19d 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -18,7 +18,7 @@ buildscript { junit_version = '4.13.2' } dependencies { - classpath 'com.android.tools.build:gradle:9.2.1' + classpath 'com.android.tools.build:gradle:9.3.1' classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.5.9' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" diff --git a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java index bec0baefb0..64581045ce 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java +++ b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java @@ -238,6 +238,23 @@ public static DavPropertyNameSet getAlbumPropSet() { return propertySet; } + /** + * Builds a DavPropertyNameSet for the items of a photo album. + *

+ * Same as {@link #getAllPropSet()} but without {@code oc:permissions}: the photos DAV endpoint cannot resolve + * that property for album items and answers the whole PROPFIND with HTTP 500 (server side TypeError) when it + * is requested. + * + * @return properties supported by the album items endpoint + */ + public static DavPropertyNameSet getAlbumItemPropSet() { + DavPropertyNameSet propSet = new DavPropertyNameSet(getAllPropSet()); + propSet.remove(DavPropertyName.create(WebdavEntry.EXTENDED_PROPERTY_NAME_PERMISSIONS, + Namespace.getNamespace(WebdavEntry.NAMESPACE_OC))); + + return propSet; + } + /** * diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt index 40deed2e70..2803f8c89d 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt @@ -40,7 +40,7 @@ class ReadAlbumItemsRemoteOperation query = PropFindMethod( url, - WebdavUtils.getAllPropSet(), // PropFind Properties + WebdavUtils.getAlbumItemPropSet(), // PropFind Properties DavConstants.DEPTH_1 ) val status = diff --git a/sample_client/build.gradle b/sample_client/build.gradle index f2065110fc..4150b47551 100644 --- a/sample_client/build.gradle +++ b/sample_client/build.gradle @@ -11,7 +11,7 @@ buildscript { dependencies { - classpath 'com.android.tools.build:gradle:9.2.1' + classpath 'com.android.tools.build:gradle:9.3.1' classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.5.9' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.8" From d56b375c831fd56cb446fdb573d3d81455b46a08 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 12 Aug 2026 16:06:05 +0200 Subject: [PATCH 14/15] wip Signed-off-by: alperozturk96 --- library/build.gradle | 2 +- .../assistant/v2/AssistantV2Tests.kt | 1 - .../albums/CopyFileToAlbumRemoteOperation.kt | 224 +++++++++--------- .../lib/resources/albums/PhotoAlbumEntry.kt | 11 +- .../PublicShareLinkAlbumRemoteOperation.kt | 116 ++++----- .../albums/RenameAlbumRemoteOperation.kt | 118 ++++----- .../ToggleAlbumFavoriteRemoteOperation.kt | 14 +- .../resources/albums/PhotoAlbumEntryTest.kt | 2 +- sample_client/build.gradle | 2 +- 9 files changed, 249 insertions(+), 241 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 8e19e2a19d..396e807c5c 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -18,7 +18,7 @@ buildscript { junit_version = '4.13.2' } dependencies { - classpath 'com.android.tools.build:gradle:9.3.1' + classpath 'com.android.tools.build:gradle:9.2.1' classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.5.9' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version" diff --git a/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt b/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt index 9e9f678d6e..67921f7695 100644 --- a/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt +++ b/library/src/androidTest/java/com/owncloud/android/lib/resources/assistant/v2/AssistantV2Tests.kt @@ -3,7 +3,6 @@ * * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors * SPDX-FileCopyrightText: 2024 Alper Ozturk - * SPDX-License-Identifier: MIT */ diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt index 10a005380f..a21670ccec 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt @@ -27,126 +27,128 @@ import java.io.IOException * * Allows renaming the moving file/folder at the same time. */ -class CopyFileToAlbumRemoteOperation @JvmOverloads constructor( - private val mSrcRemotePath: String, - private val mTargetRemotePath: String, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut -) : - RemoteOperation() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Deprecated("Deprecated in Java") - @Suppress("TooGenericExceptionCaught") - override fun run(client: OwnCloudClient): RemoteOperationResult { - /** check parameters */ - - var result: RemoteOperationResult - if (mTargetRemotePath == mSrcRemotePath) { - // nothing to do! - result = RemoteOperationResult(ResultCode.OK) - } else { - /** perform remote operation */ - var copyMethod: CopyMethod? = null - try { - copyMethod = CopyMethod( - client.getFilesDavUri(this.mSrcRemotePath), - "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ - WebdavUtils.encodePath( - mTargetRemotePath +class CopyFileToAlbumRemoteOperation + @JvmOverloads + constructor( + private val mSrcRemotePath: String, + private val mTargetRemotePath: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation() { + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Deprecated("Deprecated in Java") + @Suppress("TooGenericExceptionCaught") + override fun run(client: OwnCloudClient): RemoteOperationResult { + // check parameters + var result: RemoteOperationResult + if (mTargetRemotePath == mSrcRemotePath) { + // nothing to do! + result = RemoteOperationResult(ResultCode.OK) + } else { + // perform remote operation + var copyMethod: CopyMethod? = null + val albumsUrl = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" + try { + copyMethod = + CopyMethod( + client.getFilesDavUri(this.mSrcRemotePath), + "$albumsUrl${WebdavUtils.encodePath(mTargetRemotePath)}", + false + ) + val status = + client.executeMethod( + copyMethod, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut ) - }", - false - ) - val status = client.executeMethod( - copyMethod, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - /** process response */ - result = when (status) { - HttpStatus.SC_MULTI_STATUS -> processPartialError(copyMethod) - HttpStatus.SC_PRECONDITION_FAILED -> { - client.exhaustResponse(copyMethod.responseBodyAsStream) - RemoteOperationResult(ResultCode.INVALID_OVERWRITE) - } + // process response + result = + when (status) { + HttpStatus.SC_MULTI_STATUS -> { + processPartialError(copyMethod) + } - else -> { - client.exhaustResponse(copyMethod.responseBodyAsStream) - RemoteOperationResult(isSuccess(status), copyMethod) - } - } + HttpStatus.SC_PRECONDITION_FAILED -> { + client.exhaustResponse(copyMethod.responseBodyAsStream) + RemoteOperationResult(ResultCode.INVALID_OVERWRITE) + } + + else -> { + client.exhaustResponse(copyMethod.responseBodyAsStream) + RemoteOperationResult(isSuccess(status), copyMethod) + } + } - Log.i( - TAG, - "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}" - ) - } catch (e: Exception) { - result = RemoteOperationResult(e) - Log.e( - TAG, - "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}", e - ) - } finally { - copyMethod?.releaseConnection() + Log.i( + TAG, + "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}" + ) + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log.e( + TAG, + "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}", + e + ) + } finally { + copyMethod?.releaseConnection() + } } - } - return result - } + return result + } - /** - * Analyzes a multistatus response from the OC server to generate an appropriate result. - * - * - * In WebDAV, a COPY request on collections (folders) can be PARTIALLY successful: some - * children are copied, some other aren't. - * - * - * According to the WebDAV specification, a multistatus response SHOULD NOT include partial - * successes (201, 204) nor for descendants of already failed children (424) in the response - * entity. But SHOULD NOT != MUST NOT, so take carefully. - * - * @param copyMethod Copy operation just finished with a multistatus response - * @return A result for the [CopyFileToAlbumRemoteOperation] caller - * @throws java.io.IOException If the response body could not be parsed - * @throws org.apache.jackrabbit.webdav.DavException If the status code is other than MultiStatus or if obtaining - * the response XML document fails - */ - @Throws(IOException::class, DavException::class) - private fun processPartialError(copyMethod: CopyMethod): RemoteOperationResult { - // Adding a list of failed descendants to the result could be interesting; or maybe not. - // For the moment, let's take the easy way. - /** check that some error really occurred */ + /** + * Analyzes a multistatus response from the OC server to generate an appropriate result. + * + * + * In WebDAV, a COPY request on collections (folders) can be PARTIALLY successful: some + * children are copied, some other aren't. + * + * + * According to the WebDAV specification, a multistatus response SHOULD NOT include partial + * successes (201, 204) nor for descendants of already failed children (424) in the response + * entity. But SHOULD NOT != MUST NOT, so take carefully. + * + * @param copyMethod Copy operation just finished with a multistatus response + * @return A result for the [CopyFileToAlbumRemoteOperation] caller + * @throws java.io.IOException If the response body could not be parsed + * @throws org.apache.jackrabbit.webdav.DavException If the status code is other than MultiStatus + * or if obtaining the response XML document fails + */ + @Throws(IOException::class, DavException::class) + private fun processPartialError(copyMethod: CopyMethod): RemoteOperationResult { + // Adding a list of failed descendants to the result could be interesting; or maybe not. + // For the moment, let's take the easy way. + // check that some error really occurred + val responses = copyMethod.responseBodyAsMultiStatus.responses + var status: Array? + var failFound = false + var i = 0 + while (i < responses.size && !failFound) { + status = responses[i].status + failFound = (!status.isNullOrEmpty() && status[0].statusCode > FAILED_STATUS_CODE) + i++ + } + val result: RemoteOperationResult = + if (failFound) { + RemoteOperationResult(ResultCode.PARTIAL_COPY_DONE) + } else { + RemoteOperationResult(true, copyMethod) + } - val responses = copyMethod.responseBodyAsMultiStatus.responses - var status: Array? - var failFound = false - var i = 0 - while (i < responses.size && !failFound) { - status = responses[i].status - failFound = (!status.isNullOrEmpty() && status[0].statusCode > FAILED_STATUS_CODE - ) - i++ - } - val result: RemoteOperationResult = if (failFound) { - RemoteOperationResult(ResultCode.PARTIAL_COPY_DONE) - } else { - RemoteOperationResult(true, copyMethod) + return result } - return result - } + private fun isSuccess(status: Int): Boolean = + status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT - private fun isSuccess(status: Int): Boolean { - return status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT - } - - companion object { - private val TAG: String = CopyFileToAlbumRemoteOperation::class.java.simpleName - private const val FAILED_STATUS_CODE = 299 + companion object { + private val TAG: String = CopyFileToAlbumRemoteOperation::class.java.simpleName + private const val FAILED_STATUS_CODE = 299 + } } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt index c064213e11..2d15318522 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt @@ -140,7 +140,9 @@ class PhotoAlbumEntry( href .removeSuffix("/") .substringAfterLast("/") - .takeIf { it.isNotEmpty() } ?: "", StandardCharsets.UTF_8.name()) + .takeIf { it.isNotEmpty() } ?: "", + StandardCharsets.UTF_8.name() + ) } val createdDate: Long @@ -160,4 +162,9 @@ class PhotoAlbumEntry( } } -data class Collaborators(val id: String?, val label: String?, val type: ShareType?, val shareLink: String?) +data class Collaborators( + val id: String?, + val label: String?, + val type: ShareType?, + val shareLink: String? +) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt index 3e2a77dc77..3d88aaa711 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt @@ -28,71 +28,71 @@ import org.json.JSONObject import java.io.IOException class PublicShareLinkAlbumRemoteOperation -@JvmOverloads -constructor( - private val albumName: String, - private val isCreateShare: Boolean, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut -) : RemoteOperation() { - @Deprecated("Deprecated in Java") - override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult - var propPatchMethod: PropPatchMethod? = null + @JvmOverloads + constructor( + private val albumName: String, + private val isCreateShare: Boolean, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation() { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + var result: RemoteOperationResult + var propPatchMethod: PropPatchMethod? = null - val collaboratorsArray = JSONArray() + val collaboratorsArray = JSONArray() - // for create new share link the data should be in json - // [{"id":"","label":"Public link","type":3}] - // for removing share pass empty array [] - if (isCreateShare) { - val collaboratorsObject = JSONObject() - // empty while creating share - collaboratorsObject.put(SHAREES_ID, "") - // default public link - collaboratorsObject.put(COLLABORATORS_SHARE_LABEL, "Public Link") - collaboratorsObject.put(SHAREES_SHARE_TYPE, ShareType.PUBLIC_LINK.value) + // for create new share link the data should be in json + // [{"id":"","label":"Public link","type":3}] + // for removing share pass empty array [] + if (isCreateShare) { + val collaboratorsObject = JSONObject() + // empty while creating share + collaboratorsObject.put(SHAREES_ID, "") + // default public link + collaboratorsObject.put(COLLABORATORS_SHARE_LABEL, "Public Link") + collaboratorsObject.put(SHAREES_SHARE_TYPE, ShareType.PUBLIC_LINK.value) - collaboratorsArray.put(collaboratorsObject) - } - - val removeProperties = DavPropertyNameSet() + collaboratorsArray.put(collaboratorsObject) + } - val newProps = DavPropertySet() - val collaboratorsProperty = - DefaultDavProperty( - "nc:collaborators", - collaboratorsArray.toString(), - Namespace.getNamespace(NAMESPACE_NC) - ) - newProps.add(collaboratorsProperty) + val removeProperties = DavPropertyNameSet() - val fullUri = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ - WebdavUtils.encodePath( - albumName - ) - }" + val newProps = DavPropertySet() + val collaboratorsProperty = + DefaultDavProperty( + "nc:collaborators", + collaboratorsArray.toString(), + Namespace.getNamespace(NAMESPACE_NC) + ) + newProps.add(collaboratorsProperty) - try { - propPatchMethod = PropPatchMethod(fullUri, newProps, removeProperties) - val status = - client.executeMethod( - propPatchMethod, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut + val fullUri = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ + WebdavUtils.encodePath( + albumName ) - val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) - if (isSuccess) { - result = RemoteOperationResult(true, status, propPatchMethod.responseHeaders) - } else { - client.exhaustResponse(propPatchMethod.responseBodyAsStream) - result = RemoteOperationResult(false, status, propPatchMethod.responseHeaders) + }" + + try { + propPatchMethod = PropPatchMethod(fullUri, newProps, removeProperties) + val status = + client.executeMethod( + propPatchMethod, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) + if (isSuccess) { + result = RemoteOperationResult(true, status, propPatchMethod.responseHeaders) + } else { + client.exhaustResponse(propPatchMethod.responseBodyAsStream) + result = RemoteOperationResult(false, status, propPatchMethod.responseHeaders) + } + } catch (e: IOException) { + result = RemoteOperationResult(e) + } finally { + propPatchMethod?.releaseConnection() } - } catch (e: IOException) { - result = RemoteOperationResult(e) - } finally { - propPatchMethod?.releaseConnection() - } - return result + return result + } } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt index c5c622f12a..7d3d86b255 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt @@ -17,68 +17,68 @@ import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.client.methods.MoveMethod class RenameAlbumRemoteOperation -@JvmOverloads -constructor( - private val mOldRemotePath: String, - val newAlbumName: String, - private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut -) : RemoteOperation() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ - @Deprecated("Deprecated in Java") - @Suppress("TooGenericExceptionCaught") - override fun run(client: OwnCloudClient): RemoteOperationResult? { - if (this.newAlbumName == this.mOldRemotePath) { - return RemoteOperationResult(RemoteOperationResult.ResultCode.OK) - } + @JvmOverloads + constructor( + private val mOldRemotePath: String, + val newAlbumName: String, + private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut + ) : RemoteOperation() { + /** + * Performs the operation. + * + * @param client Client object to communicate with the remote ownCloud server. + */ + @Deprecated("Deprecated in Java") + @Suppress("TooGenericExceptionCaught") + override fun run(client: OwnCloudClient): RemoteOperationResult? { + if (this.newAlbumName == this.mOldRemotePath) { + return RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + } - var result: RemoteOperationResult? = null - var move: MoveMethod? = null - val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" - try { - move = - MoveMethod( - "$url${WebdavUtils.encodePath(mOldRemotePath)}", - "$url${ - WebdavUtils.encodePath( - newAlbumName - ) - }", - false + var result: RemoteOperationResult? = null + var move: MoveMethod? = null + val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" + try { + move = + MoveMethod( + "$url${WebdavUtils.encodePath(mOldRemotePath)}", + "$url${ + WebdavUtils.encodePath( + newAlbumName + ) + }", + false + ) + client.executeMethod( + move, + sessionTimeOut.readTimeOut, + sessionTimeOut.connectionTimeOut + ) + result = RemoteOperationResult(move.succeeded(), move) + Log_OC.i( + TAG, + "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}" + ) + // album name already exist + if (!result.isSuccess && result.httpCode == HttpStatus.SC_PRECONDITION_FAILED) { + result = RemoteOperationResult(RemoteOperationResult.ResultCode.INVALID_OVERWRITE) + } + client.exhaustResponse(move.responseBodyAsStream) + } catch (e: Exception) { + result = RemoteOperationResult(e) + Log_OC.e( + TAG, + "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}", + e ) - client.executeMethod( - move, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - result = RemoteOperationResult(move.succeeded(), move) - Log_OC.i( - TAG, - "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}" - ) - // album name already exist - if (!result.isSuccess && result.httpCode == HttpStatus.SC_PRECONDITION_FAILED) { - result = RemoteOperationResult(RemoteOperationResult.ResultCode.INVALID_OVERWRITE) + } finally { + move?.releaseConnection() } - client.exhaustResponse(move.responseBodyAsStream) - } catch (e: Exception) { - result = RemoteOperationResult(e) - Log_OC.e( - TAG, - "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}", - e - ) - } finally { - move?.releaseConnection() - } - return result - } + return result + } - companion object { - private val TAG: String = RenameAlbumRemoteOperation::class.java.simpleName + companion object { + private val TAG: String = RenameAlbumRemoteOperation::class.java.simpleName + } } -} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt index 9968bee554..5b66b14d11 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt @@ -46,13 +46,13 @@ class ToggleAlbumFavoriteRemoteOperation var propPatchMethod: PropPatchMethod? = null val newProps = DavPropertySet() val removeProperties = DavPropertyNameSet() - val favoriteProperty = - DefaultDavProperty( - "oc:favorite", - if(markAsFavorite) "1" else "0", - Namespace.getNamespace(WebdavEntry.NAMESPACE_OC) - ) - newProps.add(favoriteProperty) + val favoriteProperty = + DefaultDavProperty( + "oc:favorite", + if (markAsFavorite) "1" else "0", + Namespace.getNamespace(WebdavEntry.NAMESPACE_OC) + ) + newProps.add(favoriteProperty) val webDavUrl = "${client.davUri}/photos/" val encodedPath = "${client.userId}${WebdavUtils.encodePath(this.filePath)}" diff --git a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt index a2ddd83fc5..d3a9b45f11 100644 --- a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt +++ b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt @@ -51,6 +51,6 @@ class PhotoAlbumEntryTest { // Helper method to create a stub entry private fun createTestEntry(href: String): PhotoAlbumEntry { val response = MultiStatusResponse(href, 200) - return PhotoAlbumEntry("https://www.example.com/",response) + return PhotoAlbumEntry("https://www.example.com/", response) } } diff --git a/sample_client/build.gradle b/sample_client/build.gradle index 4150b47551..f2065110fc 100644 --- a/sample_client/build.gradle +++ b/sample_client/build.gradle @@ -11,7 +11,7 @@ buildscript { dependencies { - classpath 'com.android.tools.build:gradle:9.3.1' + classpath 'com.android.tools.build:gradle:9.2.1' classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.5.9' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.8" From 0b6409e102481b7f58639825c3a1af49190a9825 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 14 Aug 2026 11:36:53 +0200 Subject: [PATCH 15/15] simplify Signed-off-by: alperozturk96 --- .../lib/common/network/WebdavUtils.java | 5 +- .../android/lib/resources/albums/AlbumsDav.kt | 66 +++++++ .../lib/resources/albums/Collaborator.kt | 17 ++ .../albums/CopyFileToAlbumRemoteOperation.kt | 154 +++++---------- .../albums/CreateNewAlbumRemoteOperation.kt | 53 ++---- .../lib/resources/albums/PhotoAlbumEntry.kt | 176 ++++++------------ .../PublicShareLinkAlbumRemoteOperation.kt | 94 +++------- .../albums/ReadAlbumItemsRemoteOperation.kt | 88 ++++----- .../albums/ReadAlbumsRemoteOperation.kt | 73 ++++---- .../albums/RemoveAlbumFileRemoteOperation.kt | 47 ++--- .../albums/RemoveAlbumRemoteOperation.kt | 50 ++--- .../albums/RenameAlbumRemoteOperation.kt | 75 +++----- .../ToggleAlbumFavoriteRemoteOperation.kt | 73 +++----- .../resources/albums/PhotoAlbumEntryTest.kt | 45 +++-- 14 files changed, 415 insertions(+), 601 deletions(-) create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/AlbumsDav.kt create mode 100644 library/src/main/java/com/owncloud/android/lib/resources/albums/Collaborator.kt diff --git a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java index 64581045ce..338d7fafdb 100644 --- a/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java +++ b/library/src/main/java/com/owncloud/android/lib/common/network/WebdavUtils.java @@ -227,9 +227,9 @@ public static DavPropertyNameSet getChunksPropSet() { public static DavPropertyNameSet getAlbumPropSet() { DavPropertyNameSet propertySet = new DavPropertyNameSet(); - Namespace ncNamespace = Namespace.getNamespace("nc",WebdavEntry.NAMESPACE_NC); + Namespace ncNamespace = Namespace.getNamespace("nc", WebdavEntry.NAMESPACE_NC); - propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_LAST_PHOTO,ncNamespace)); + propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_LAST_PHOTO, ncNamespace)); propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_NB_ITEMS, ncNamespace)); propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_LOCATION, ncNamespace)); propertySet.add(DavPropertyName.create(WebdavEntry.PROPERTY_DATE_RANGE, ncNamespace)); @@ -255,7 +255,6 @@ public static DavPropertyNameSet getAlbumItemPropSet() { return propSet; } - /** * * @param rawEtag diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/AlbumsDav.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/AlbumsDav.kt new file mode 100644 index 0000000000..ee52bf0050 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/AlbumsDav.kt @@ -0,0 +1,66 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ +package com.owncloud.android.lib.resources.albums + +import com.nextcloud.common.SessionTimeOut +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import org.apache.commons.httpclient.HttpStatus +import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod +import org.apache.jackrabbit.webdav.property.DavProperty +import org.apache.jackrabbit.webdav.property.DavPropertyNameSet +import org.apache.jackrabbit.webdav.property.DavPropertySet +import java.io.IOException + +internal const val ALBUMS_PATH = "/albums" + +private const val PHOTOS_PATH = "/photos/" + +/** + * Root of the photos DAV endpoint: `…/remote.php/dav/photos/`. + */ +internal val OwnCloudClient.photosDavUri: String + get() = "$davUri$PHOTOS_PATH$userId" + +internal val OwnCloudClient.albumsDavUri: String + get() = "$photosDavUri$ALBUMS_PATH" + +internal fun OwnCloudClient.albumUri(albumPath: String): String = "$albumsDavUri${WebdavUtils.encodePath(albumPath)}" + +internal fun OwnCloudClient.photosUri(remotePath: String): String = "$photosDavUri${WebdavUtils.encodePath(remotePath)}" + +/** + * The photos endpoint answers a successful PROPFIND/PROPPATCH with either 207 or 200. + */ +internal fun isMultiStatusOrOk(status: Int): Boolean = + status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK + +/** + * PROPPATCHes a single property onto a photos DAV resource. + */ +internal fun OwnCloudClient.setPhotosProperty( + uri: String, + property: DavProperty<*>, + sessionTimeOut: SessionTimeOut +): RemoteOperationResult { + var propPatch: PropPatchMethod? = null + return try { + val newProperties = DavPropertySet().apply { add(property) } + propPatch = PropPatchMethod(uri, newProperties, DavPropertyNameSet()) + val status = executeMethod(propPatch, sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut) + val success = isMultiStatusOrOk(status) + if (!success) { + exhaustResponse(propPatch.responseBodyAsStream) + } + RemoteOperationResult(success, status, propPatch.responseHeaders) + } catch (e: IOException) { + RemoteOperationResult(e) + } finally { + propPatch?.releaseConnection() + } +} diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/Collaborator.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/Collaborator.kt new file mode 100644 index 0000000000..6c773de348 --- /dev/null +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/Collaborator.kt @@ -0,0 +1,17 @@ +/* + * Nextcloud Android Library + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: MIT + */ + +package com.owncloud.android.lib.resources.albums + +import com.owncloud.android.lib.resources.shares.ShareType + +data class Collaborator( + val id: String, + val label: String, + val type: ShareType, + val shareLink: String +) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt index a21670ccec..d823b29491 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CopyFileToAlbumRemoteOperation.kt @@ -1,154 +1,96 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums -import android.util.Log import com.nextcloud.common.SessionTimeOut import com.nextcloud.common.defaultSessionTimeOut import com.owncloud.android.lib.common.OwnCloudClient -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.Log_OC import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.DavException -import org.apache.jackrabbit.webdav.Status import org.apache.jackrabbit.webdav.client.methods.CopyMethod import java.io.IOException -/** - * 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. - */ class CopyFileToAlbumRemoteOperation @JvmOverloads constructor( - private val mSrcRemotePath: String, - private val mTargetRemotePath: String, + private val srcRemotePath: String, + private val targetRemotePath: String, private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut ) : RemoteOperation() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ @Deprecated("Deprecated in Java") - @Suppress("TooGenericExceptionCaught") + @Suppress("TooGenericExceptionCaught", "DEPRECATION") override fun run(client: OwnCloudClient): RemoteOperationResult { - // check parameters - var result: RemoteOperationResult - if (mTargetRemotePath == mSrcRemotePath) { - // nothing to do! - result = RemoteOperationResult(ResultCode.OK) - } else { - // perform remote operation - var copyMethod: CopyMethod? = null - val albumsUrl = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" - try { - copyMethod = - CopyMethod( - client.getFilesDavUri(this.mSrcRemotePath), - "$albumsUrl${WebdavUtils.encodePath(mTargetRemotePath)}", - false - ) - val status = - client.executeMethod( - copyMethod, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) + if (targetRemotePath == srcRemotePath) { + return RemoteOperationResult(ResultCode.OK) + } - // process response - result = - when (status) { - HttpStatus.SC_MULTI_STATUS -> { - processPartialError(copyMethod) - } + var copy: CopyMethod? = null + return try { + copy = CopyMethod(client.getFilesDavUri(srcRemotePath), client.albumUri(targetRemotePath), false) + val status = client.executeMethod(copy, sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut) - HttpStatus.SC_PRECONDITION_FAILED -> { - client.exhaustResponse(copyMethod.responseBodyAsStream) - RemoteOperationResult(ResultCode.INVALID_OVERWRITE) - } + when (status) { + HttpStatus.SC_MULTI_STATUS -> { + processPartialError(copy) + } - else -> { - client.exhaustResponse(copyMethod.responseBodyAsStream) - RemoteOperationResult(isSuccess(status), copyMethod) - } - } + HttpStatus.SC_PRECONDITION_FAILED -> { + client.exhaustResponse(copy.responseBodyAsStream) + RemoteOperationResult(ResultCode.INVALID_OVERWRITE) + } - Log.i( - TAG, - "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}" - ) - } catch (e: Exception) { - result = RemoteOperationResult(e) - Log.e( - TAG, - "Copy $mSrcRemotePath to $mTargetRemotePath : ${result.logMessage}", - e - ) - } finally { - copyMethod?.releaseConnection() + else -> { + client.exhaustResponse(copy.responseBodyAsStream) + RemoteOperationResult(isSuccess(status), copy) + } + }.also { + Log_OC.i(TAG, "Copy $srcRemotePath to $targetRemotePath : ${it.logMessage}") } + } catch (e: Exception) { + failure(e) + } finally { + copy?.releaseConnection() } - - return result } /** - * Analyzes a multistatus response from the OC server to generate an appropriate result. - * - * - * In WebDAV, a COPY request on collections (folders) can be PARTIALLY successful: some - * children are copied, some other aren't. - * - * - * According to the WebDAV specification, a multistatus response SHOULD NOT include partial - * successes (201, 204) nor for descendants of already failed children (424) in the response - * entity. But SHOULD NOT != MUST NOT, so take carefully. - * - * @param copyMethod Copy operation just finished with a multistatus response - * @return A result for the [CopyFileToAlbumRemoteOperation] caller - * @throws java.io.IOException If the response body could not be parsed - * @throws org.apache.jackrabbit.webdav.DavException If the status code is other than MultiStatus - * or if obtaining the response XML document fails + * A COPY of a collection can be partially successful: some children are copied, some are not. */ @Throws(IOException::class, DavException::class) - private fun processPartialError(copyMethod: CopyMethod): RemoteOperationResult { - // Adding a list of failed descendants to the result could be interesting; or maybe not. - // For the moment, let's take the easy way. - // check that some error really occurred - val responses = copyMethod.responseBodyAsMultiStatus.responses - var status: Array? - var failFound = false - var i = 0 - while (i < responses.size && !failFound) { - status = responses[i].status - failFound = (!status.isNullOrEmpty() && status[0].statusCode > FAILED_STATUS_CODE) - i++ - } - val result: RemoteOperationResult = - if (failFound) { - RemoteOperationResult(ResultCode.PARTIAL_COPY_DONE) - } else { - RemoteOperationResult(true, copyMethod) + private fun processPartialError(copy: CopyMethod): RemoteOperationResult { + val partiallyFailed = + copy.responseBodyAsMultiStatus.responses.any { response -> + val statusCode = response.status?.firstOrNull()?.statusCode ?: 0 + statusCode > MAX_SUCCESS_STATUS_CODE } - return result + return if (partiallyFailed) { + RemoteOperationResult(ResultCode.PARTIAL_COPY_DONE) + } else { + RemoteOperationResult(true, copy) + } } private fun isSuccess(status: Int): Boolean = status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult = + RemoteOperationResult(e).also { + Log_OC.e(TAG, "Copy $srcRemotePath to $targetRemotePath : ${it.logMessage}", e) + } + companion object { private val TAG: String = CopyFileToAlbumRemoteOperation::class.java.simpleName - private const val FAILED_STATUS_CODE = 299 + private const val MAX_SUCCESS_STATUS_CODE = 299 } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt index 6d69d53a05..df093ba502 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/CreateNewAlbumRemoteOperation.kt @@ -1,6 +1,7 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-License-Identifier: MIT */ @@ -9,9 +10,9 @@ package com.owncloud.android.lib.resources.albums import com.nextcloud.common.SessionTimeOut import com.nextcloud.common.defaultSessionTimeOut import com.owncloud.android.lib.common.OwnCloudClient -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.Log_OC import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.client.methods.MkColMethod @@ -22,50 +23,36 @@ class CreateNewAlbumRemoteOperation val newAlbumName: String, private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut ) : RemoteOperation() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ @Deprecated("Deprecated in Java") - @Suppress("TooGenericExceptionCaught") + @Suppress("TooGenericExceptionCaught", "DEPRECATION") override fun run(client: OwnCloudClient): RemoteOperationResult { var mkCol: MkColMethod? = null - var result: RemoteOperationResult - try { - mkCol = - MkColMethod( - "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ - WebdavUtils.encodePath( - newAlbumName - ) - }" - ) - client.executeMethod( - mkCol, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - if (HttpStatus.SC_METHOD_NOT_ALLOWED == mkCol.statusCode) { - result = - RemoteOperationResult(RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS) - } else { - result = RemoteOperationResult(mkCol.succeeded(), mkCol) - result.resultData = null - } + return try { + mkCol = MkColMethod(client.albumUri(newAlbumName)) + client.executeMethod(mkCol, sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut) + val result = + if (mkCol.statusCode == HttpStatus.SC_METHOD_NOT_ALLOWED) { + RemoteOperationResult(ResultCode.FOLDER_ALREADY_EXISTS) + } else { + RemoteOperationResult(mkCol.succeeded(), mkCol) + } Log_OC.d(TAG, "Create album $newAlbumName : ${result.logMessage}") client.exhaustResponse(mkCol.responseBodyAsStream) + result } catch (e: Exception) { - result = RemoteOperationResult(e) - Log_OC.e(TAG, "Create album $newAlbumName : ${result.logMessage}", e) + failure(e) } finally { mkCol?.releaseConnection() } - - return result } + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult = + RemoteOperationResult(e).also { + Log_OC.e(TAG, "Create album $newAlbumName : ${it.logMessage}", e) + } + companion object { private val TAG: String = CreateNewAlbumRemoteOperation::class.java.simpleName } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt index 2d15318522..4c6fbd1f38 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntry.kt @@ -1,6 +1,7 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ @@ -14,157 +15,86 @@ import org.apache.jackrabbit.webdav.MultiStatusResponse import org.apache.jackrabbit.webdav.property.DavPropertyName import org.apache.jackrabbit.webdav.property.DavPropertySet import org.apache.jackrabbit.webdav.xml.Namespace -import org.json.JSONException import org.json.JSONObject import org.w3c.dom.Element import java.net.URLDecoder import java.nio.charset.StandardCharsets class PhotoAlbumEntry( - // required for providing album share link url baseUri: String, response: MultiStatusResponse ) { - val href: String + val href: String = response.href val lastPhoto: Long val nbItems: Int val location: String? - private val dateRange: String? - var collaborators = arrayOf() - private set - - private var shareBaseUri: String? = null + val collaborators: List - companion object { - private const val MILLIS = 1000L - } + private val dateRange: String? init { - // will be used to provide full share link for album - shareBaseUri = "$baseUri/apps/photos/public/" - - href = response.href - val properties = response.getProperties(HttpStatus.SC_OK) - this.lastPhoto = parseLong(parseString(properties, WebdavEntry.PROPERTY_LAST_PHOTO)) - this.nbItems = parseInt(parseString(properties, WebdavEntry.PROPERTY_NB_ITEMS)) - this.location = parseString(properties, WebdavEntry.PROPERTY_LOCATION) - this.dateRange = parseString(properties, WebdavEntry.PROPERTY_DATE_RANGE) - parseCollaborators(properties) + lastPhoto = properties.ncString(WebdavEntry.PROPERTY_LAST_PHOTO)?.toLongOrNull() ?: 0L + nbItems = properties.ncString(WebdavEntry.PROPERTY_NB_ITEMS)?.toIntOrNull() ?: 0 + location = properties.ncString(WebdavEntry.PROPERTY_LOCATION) + dateRange = properties.ncString(WebdavEntry.PROPERTY_DATE_RANGE) + collaborators = properties.collaborators(baseUri + SHARE_LINK_PATH) } - private fun parseString( - props: DavPropertySet, - name: String - ): String? { - val propName = DavPropertyName.create(name, Namespace.getNamespace("nc", WebdavEntry.NAMESPACE_NC)) - val prop = props[propName] - return if (prop != null && prop.value != null) prop.value.toString() else null - } + val albumName: String + get() = + URLDecoder.decode( + href.trimEnd(PATH_SEPARATOR).substringAfterLast(PATH_SEPARATOR), + StandardCharsets.UTF_8.name() + ) - private fun parseInt(value: String?): Int = - try { - value?.toInt() ?: 0 - } catch (_: NumberFormatException) { - 0 + /** + * Start of the album date range, falling back to now when the server did not report one. + */ + val createdDate: Long + get() { + val start = dateRange?.let { runCatching { JSONObject(it).optLong(JSON_KEY_START) }.getOrNull() } ?: 0L + return if (start > 0) start * MILLIS else System.currentTimeMillis() } - private fun parseLong(value: String?): Long = - try { - value?.toLong() ?: 0L - } catch (_: NumberFormatException) { - 0L - } + private fun DavPropertySet.ncString(name: String): String? = + this[DavPropertyName.create(name, NC_NAMESPACE)]?.value?.toString() - private fun parseCollaborators(properties: DavPropertySet) { - val collaboratorsProp = - properties[WebdavEntry.PROPERTY_COLLABORATORS, Namespace.getNamespace("nc", WebdavEntry.NAMESPACE_NC)] - if (collaboratorsProp != null && collaboratorsProp.value != null) { - if (collaboratorsProp.value is ArrayList<*>) { - val list = collaboratorsProp.value as ArrayList<*> - val tempList: MutableList = ArrayList() - for (i in list.indices) { - val element = list[i] as Element - val collaborator = createCollaborators(element) - tempList.add(collaborator) - } - collaborators = tempList.toTypedArray() - } else { - // single item or empty - val element = collaboratorsProp.value as Element - val collaborator = createCollaborators(element) - collaborators = arrayOf(collaborator) + private fun DavPropertySet.collaborators(shareBaseUri: String): List { + val value = this[WebdavEntry.PROPERTY_COLLABORATORS, NC_NAMESPACE]?.value + val elements = + when (value) { + is Collection<*> -> value.filterIsInstance() + is Element -> listOf(value) + else -> emptyList() } - } - } - - private fun createCollaborators(element: Element): Collaborators { - val id = extractId(element) - val label = extractLabel(element) - val shareType = extractShareType(element) - return Collaborators(id, label, shareType, "$shareBaseUri$id") + return elements.map { it.toCollaborator(shareBaseUri) } } - private fun extractLabel(element: Element): String { - val displayName = element.getElementsByTagName(WebdavEntry.COLLABORATORS_SHARE_LABEL).item(0) - return if (displayName != null && displayName.firstChild != null) { - displayName.firstChild.nodeValue - } else { - "" - } + private fun Element.toCollaborator(shareBaseUri: String): Collaborator { + val id = textOf(WebdavEntry.SHAREES_ID).orEmpty() + return Collaborator( + id = id, + label = textOf(WebdavEntry.COLLABORATORS_SHARE_LABEL).orEmpty(), + type = + textOf(WebdavEntry.SHAREES_SHARE_TYPE)?.toIntOrNull()?.let(ShareType::fromValue) + ?: ShareType.NO_SHARED, + shareLink = shareBaseUri + id + ) } - private fun extractId(element: Element): String { - val userId = element.getElementsByTagName(WebdavEntry.SHAREES_ID).item(0) - return if (userId != null && userId.firstChild != null) { - userId.firstChild.nodeValue - } else { - "" - } - } + private fun Element.textOf(tagName: String): String? = + getElementsByTagName(tagName) + .item(0) + ?.firstChild + ?.nodeValue - private fun extractShareType(element: Element): ShareType { - val shareType = element.getElementsByTagName(WebdavEntry.SHAREES_SHARE_TYPE).item(0) - if (shareType != null && shareType.firstChild != null) { - val value = shareType.firstChild.nodeValue.toInt() - return ShareType.fromValue(value) - } - return ShareType.NO_SHARED + companion object { + private const val MILLIS = 1000L + private const val PATH_SEPARATOR = '/' + private const val JSON_KEY_START = "start" + private const val SHARE_LINK_PATH = "/apps/photos/public/" + private val NC_NAMESPACE = Namespace.getNamespace("nc", WebdavEntry.NAMESPACE_NC) } - - val albumName: String - get() { - // use decoder to show correct path - return URLDecoder.decode( - href - .removeSuffix("/") - .substringAfterLast("/") - .takeIf { it.isNotEmpty() } ?: "", - StandardCharsets.UTF_8.name() - ) - } - - val createdDate: Long - get() { - val defaultTimeStamp = System.currentTimeMillis() - return try { - val obj = JSONObject(dateRange ?: return defaultTimeStamp) - val startTimestamp = obj.optLong("start", 0) - if (startTimestamp > 0) { - startTimestamp * MILLIS - } else { - defaultTimeStamp - } - } catch (_: JSONException) { - defaultTimeStamp - } - } } - -data class Collaborators( - val id: String?, - val label: String?, - val type: ShareType?, - val shareLink: String? -) diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt index 3d88aaa711..5ed2712cc5 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/PublicShareLinkAlbumRemoteOperation.kt @@ -1,6 +1,7 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ @@ -9,24 +10,18 @@ package com.owncloud.android.lib.resources.albums import com.nextcloud.common.SessionTimeOut import com.nextcloud.common.defaultSessionTimeOut import com.owncloud.android.lib.common.OwnCloudClient -import com.owncloud.android.lib.common.network.WebdavEntry.Companion.COLLABORATORS_SHARE_LABEL -import com.owncloud.android.lib.common.network.WebdavEntry.Companion.NAMESPACE_NC -import com.owncloud.android.lib.common.network.WebdavEntry.Companion.SHAREES_ID -import com.owncloud.android.lib.common.network.WebdavEntry.Companion.SHAREES_SHARE_TYPE -import com.owncloud.android.lib.common.network.WebdavUtils +import com.owncloud.android.lib.common.network.WebdavEntry import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.shares.ShareType -import org.apache.commons.httpclient.HttpStatus -import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod -import org.apache.jackrabbit.webdav.property.DavPropertyNameSet -import org.apache.jackrabbit.webdav.property.DavPropertySet import org.apache.jackrabbit.webdav.property.DefaultDavProperty import org.apache.jackrabbit.webdav.xml.Namespace import org.json.JSONArray import org.json.JSONObject -import java.io.IOException +/** + * Creates or removes the public share link of an album, depending on [isCreateShare]. + */ class PublicShareLinkAlbumRemoteOperation @JvmOverloads constructor( @@ -35,64 +30,33 @@ class PublicShareLinkAlbumRemoteOperation private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut ) : RemoteOperation() { @Deprecated("Deprecated in Java") - override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult - var propPatchMethod: PropPatchMethod? = null - - val collaboratorsArray = JSONArray() - - // for create new share link the data should be in json - // [{"id":"","label":"Public link","type":3}] - // for removing share pass empty array [] + override fun run(client: OwnCloudClient): RemoteOperationResult = + client.setPhotosProperty(client.albumUri(albumName), collaboratorsProperty(), sessionTimeOut) + + /** + * The server expects `[{"id":"","label":"Public link","type":3}]` to create a link and an empty + * array to remove it again. + */ + private fun collaboratorsProperty(): DefaultDavProperty { + val collaborators = JSONArray() if (isCreateShare) { - val collaboratorsObject = JSONObject() - // empty while creating share - collaboratorsObject.put(SHAREES_ID, "") - // default public link - collaboratorsObject.put(COLLABORATORS_SHARE_LABEL, "Public Link") - collaboratorsObject.put(SHAREES_SHARE_TYPE, ShareType.PUBLIC_LINK.value) - - collaboratorsArray.put(collaboratorsObject) - } - - val removeProperties = DavPropertyNameSet() - - val newProps = DavPropertySet() - val collaboratorsProperty = - DefaultDavProperty( - "nc:collaborators", - collaboratorsArray.toString(), - Namespace.getNamespace(NAMESPACE_NC) - ) - newProps.add(collaboratorsProperty) - - val fullUri = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ - WebdavUtils.encodePath( - albumName + collaborators.put( + JSONObject() + .put(WebdavEntry.SHAREES_ID, "") + .put(WebdavEntry.COLLABORATORS_SHARE_LABEL, PUBLIC_LINK_LABEL) + .put(WebdavEntry.SHAREES_SHARE_TYPE, ShareType.PUBLIC_LINK.value) ) - }" - - try { - propPatchMethod = PropPatchMethod(fullUri, newProps, removeProperties) - val status = - client.executeMethod( - propPatchMethod, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) - if (isSuccess) { - result = RemoteOperationResult(true, status, propPatchMethod.responseHeaders) - } else { - client.exhaustResponse(propPatchMethod.responseBodyAsStream) - result = RemoteOperationResult(false, status, propPatchMethod.responseHeaders) - } - } catch (e: IOException) { - result = RemoteOperationResult(e) - } finally { - propPatchMethod?.releaseConnection() } - return result + return DefaultDavProperty( + COLLABORATORS_PROPERTY, + collaborators.toString(), + Namespace.getNamespace(WebdavEntry.NAMESPACE_NC) + ) + } + + companion object { + private const val COLLABORATORS_PROPERTY = "nc:collaborators" + private const val PUBLIC_LINK_LABEL = "Public Link" } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt index 2803f8c89d..8ff203c5c3 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumItemsRemoteOperation.kt @@ -1,6 +1,7 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2025 TSI-mc * SPDX-License-Identifier: MIT */ @@ -15,78 +16,55 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.common.utils.WebDavFileUtils import com.owncloud.android.lib.resources.files.model.RemoteFile -import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.DavConstants import org.apache.jackrabbit.webdav.client.methods.PropFindMethod class ReadAlbumItemsRemoteOperation @JvmOverloads constructor( - private val mRemotePath: String, + private val remotePath: String, private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut ) : RemoteOperation>() { @Deprecated("Deprecated in Java") @Suppress("TooGenericExceptionCaught") override fun run(client: OwnCloudClient): RemoteOperationResult> { - var result: RemoteOperationResult>? = null - var query: PropFindMethod? = null - val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ - WebdavUtils.encodePath( - mRemotePath - ) - }" - try { - // remote request - query = - PropFindMethod( - url, - WebdavUtils.getAlbumItemPropSet(), // PropFind Properties - DavConstants.DEPTH_1 - ) - val status = - client.executeMethod( - query, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) + var propFind: PropFindMethod? = null + val result = + try { + propFind = + PropFindMethod( + client.albumUri(remotePath), + WebdavUtils.getAlbumItemPropSet(), + DavConstants.DEPTH_1 + ) + val status = + client.executeMethod(propFind, sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut) - // check and process response - val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) - - result = - if (isSuccess) { - // get data from remote folder - val dataInServer = query.responseBodyAsMultiStatus - val mFolderAndFiles = WebDavFileUtils().readAlbumData(dataInServer, client) - - // Result of the operation - RemoteOperationResult>(true, query).apply { - // Add data to the result - resultData = mFolderAndFiles + if (isMultiStatusOrOk(status)) { + RemoteOperationResult>(true, propFind).apply { + resultData = WebDavFileUtils().readAlbumData(propFind.responseBodyAsMultiStatus, client) } } else { - // synchronization failed - client.exhaustResponse(query.responseBodyAsStream) - RemoteOperationResult(false, query) + client.exhaustResponse(propFind.responseBodyAsStream) + RemoteOperationResult(false, propFind) } - } catch (e: Exception) { - result = RemoteOperationResult(e) - } finally { - query?.releaseConnection() - - result = result ?: RemoteOperationResult>(Exception("unknown error")).also { - Log_OC.e(TAG, "Synchronized $mRemotePath: failed") - } - if (result.isSuccess) { - Log_OC.i(TAG, "Synchronized $mRemotePath : ${result.logMessage}") - } else if (result.isException) { - Log_OC.e(TAG, "Synchronized $mRemotePath : ${result.logMessage}", result.exception) - } else { - Log_OC.e(TAG, "Synchronized $mRemotePath : ${result.logMessage}") + } catch (e: Exception) { + RemoteOperationResult(e) + } finally { + propFind?.releaseConnection() } - } - return result + return result.also { log(it) } + } + + @Suppress("DEPRECATION") + private fun log(result: RemoteOperationResult>) { + val message = "Synchronized $remotePath : ${result.logMessage}" + when { + result.isSuccess -> Log_OC.i(TAG, message) + result.isException -> Log_OC.e(TAG, message, result.exception) + else -> Log_OC.e(TAG, message) + } } companion object { diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt index 643a4563ea..99dee4ac7c 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ReadAlbumsRemoteOperation.kt @@ -1,12 +1,12 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2025-2026 TSI-mc * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums -import android.text.TextUtils import com.nextcloud.common.SessionTimeOut import com.nextcloud.common.defaultSessionTimeOut import com.owncloud.android.lib.common.OwnCloudClient @@ -18,56 +18,57 @@ import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.DavConstants import org.apache.jackrabbit.webdav.client.methods.PropFindMethod +/** + * Reads every album of the user, or a single one when [albumRemotePath] is given. + */ class ReadAlbumsRemoteOperation @JvmOverloads constructor( - private val mAlbumRemotePath: String? = null, + private val albumRemotePath: String? = null, private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut ) : RemoteOperation>() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ @Deprecated("Deprecated in Java") - @Suppress("TooGenericExceptionCaught") + @Suppress("TooGenericExceptionCaught", "DEPRECATION") override fun run(client: OwnCloudClient): RemoteOperationResult> { - var propfind: PropFindMethod? = null - var result: RemoteOperationResult> - var url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" - if (!TextUtils.isEmpty(mAlbumRemotePath)) { - url += WebdavUtils.encodePath(mAlbumRemotePath) - } - try { - propfind = PropFindMethod(url, WebdavUtils.getAlbumPropSet(), DavConstants.DEPTH_1) + var propFind: PropFindMethod? = null + return try { + val url = + if (albumRemotePath.isNullOrEmpty()) { + client.albumsDavUri + } else { + client.albumUri(albumRemotePath) + } + propFind = PropFindMethod(url, WebdavUtils.getAlbumPropSet(), DavConstants.DEPTH_1) val status = - client.executeMethod( - propfind, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - val isSuccess = status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK - if (isSuccess) { - val albumsList = - propfind.responseBodyAsMultiStatus.responses - .filter { it.status[0].statusCode == HttpStatus.SC_OK } - .map { res -> PhotoAlbumEntry(client.baseUri.toString(), res) } - result = RemoteOperationResult>(true, propfind) - result.resultData = albumsList + client.executeMethod(propFind, sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut) + + if (isMultiStatusOrOk(status)) { + RemoteOperationResult>(true, propFind).apply { + resultData = propFind.albums(client) + } } else { - result = RemoteOperationResult>(false, propfind) - client.exhaustResponse(propfind.responseBodyAsStream) + client.exhaustResponse(propFind.responseBodyAsStream) + RemoteOperationResult(false, propFind) } } catch (e: Exception) { - result = RemoteOperationResult>(e) - Log_OC.e(TAG, "Read album failed: ${result.logMessage}", result.exception) + failure(e) } finally { - propfind?.releaseConnection() + propFind?.releaseConnection() } - - return result } + private fun PropFindMethod.albums(client: OwnCloudClient): List = + responseBodyAsMultiStatus + .responses + .filter { it.status?.firstOrNull()?.statusCode == HttpStatus.SC_OK } + .map { PhotoAlbumEntry(client.baseUri.toString(), it) } + + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult> = + RemoteOperationResult>(e).also { + Log_OC.e(TAG, "Read album failed: ${it.logMessage}", it.exception) + } + companion object { private val TAG: String = ReadAlbumsRemoteOperation::class.java.simpleName } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt index 4d9f53977f..9a5d2a4052 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumFileRemoteOperation.kt @@ -1,63 +1,54 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ package com.owncloud.android.lib.resources.albums -import android.net.Uri import com.nextcloud.common.SessionTimeOut import com.nextcloud.common.defaultSessionTimeOut import com.owncloud.android.lib.common.OwnCloudClient -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.Log_OC -import com.owncloud.android.lib.resources.files.RemoveFileRemoteOperation import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.client.methods.DeleteMethod class RemoveAlbumFileRemoteOperation @JvmOverloads constructor( - private val mRemotePath: String, + private val remotePath: String, private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut ) : RemoteOperation() { @Deprecated("Deprecated in Java") - @Suppress("TooGenericExceptionCaught") + @Suppress("TooGenericExceptionCaught", "DEPRECATION") override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult var delete: DeleteMethod? = null - val webDavUrl = "${client.davUri}/photos/" - val encodedPath = "${client.userId}${WebdavUtils.encodePath(this.mRemotePath)}" - val fullFilePath = "$webDavUrl$encodedPath" + return try { + delete = DeleteMethod(client.photosUri(remotePath)) + val status = client.executeMethod(delete, sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut) - try { - delete = DeleteMethod(fullFilePath) - val status = - client.executeMethod( - delete, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - result = - RemoteOperationResult( - delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, - delete - ) - Log_OC.i(TAG, "Remove ${this.mRemotePath} : ${result.logMessage}") + // an already missing file is not an error for the caller + val removed = delete.succeeded() || status == HttpStatus.SC_NOT_FOUND + RemoteOperationResult(removed, delete).also { + Log_OC.i(TAG, "Remove $remotePath : ${it.logMessage}") + } } catch (e: Exception) { - result = RemoteOperationResult(e) - Log_OC.e(TAG, "Remove ${this.mRemotePath} : ${result.logMessage}", e) + failure(e) } finally { delete?.releaseConnection() } - - return result } + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult = + RemoteOperationResult(e).also { + Log_OC.e(TAG, "Remove $remotePath : ${it.logMessage}", e) + } + companion object { - private val TAG: String = RemoveFileRemoteOperation::class.java.simpleName + private val TAG: String = RemoveAlbumFileRemoteOperation::class.java.simpleName } } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt index 4142a64e76..aca5f2e8a1 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RemoveAlbumRemoteOperation.kt @@ -1,6 +1,7 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ @@ -9,7 +10,6 @@ package com.owncloud.android.lib.resources.albums import com.nextcloud.common.SessionTimeOut import com.nextcloud.common.defaultSessionTimeOut import com.owncloud.android.lib.common.OwnCloudClient -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.Log_OC @@ -22,48 +22,32 @@ class RemoveAlbumRemoteOperation private val albumName: String, private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut ) : RemoteOperation() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ @Deprecated("Deprecated in Java") - @Suppress("TooGenericExceptionCaught") + @Suppress("TooGenericExceptionCaught", "DEPRECATION") override fun run(client: OwnCloudClient): RemoteOperationResult { - var result: RemoteOperationResult var delete: DeleteMethod? = null + return try { + delete = DeleteMethod(client.albumUri(albumName)) + val status = client.executeMethod(delete, sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut) - try { - delete = - DeleteMethod( - "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums${ - WebdavUtils.encodePath( - albumName - ) - }" - ) - val status = - client.executeMethod( - delete, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - result = - RemoteOperationResult( - delete.succeeded() || status == HttpStatus.SC_NOT_FOUND, - delete - ) - Log_OC.i(TAG, "Remove ${this.albumName} : ${result.logMessage}") + // an already missing album is not an error for the caller + val removed = delete.succeeded() || status == HttpStatus.SC_NOT_FOUND + RemoteOperationResult(removed, delete).also { + Log_OC.i(TAG, "Remove $albumName : ${it.logMessage}") + } } catch (e: Exception) { - result = RemoteOperationResult(e) - Log_OC.e(TAG, "Remove ${this.albumName} : ${result.logMessage}", e) + failure(e) } finally { delete?.releaseConnection() } - - return result } + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult = + RemoteOperationResult(e).also { + Log_OC.e(TAG, "Remove $albumName : ${it.logMessage}", e) + } + companion object { private val TAG: String = RemoveAlbumRemoteOperation::class.java.simpleName } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt index 7d3d86b255..a3efb50d4e 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/RenameAlbumRemoteOperation.kt @@ -1,6 +1,7 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ @@ -9,9 +10,9 @@ package com.owncloud.android.lib.resources.albums import com.nextcloud.common.SessionTimeOut import com.nextcloud.common.defaultSessionTimeOut import com.owncloud.android.lib.common.OwnCloudClient -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.Log_OC import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.client.methods.MoveMethod @@ -19,65 +20,47 @@ import org.apache.jackrabbit.webdav.client.methods.MoveMethod class RenameAlbumRemoteOperation @JvmOverloads constructor( - private val mOldRemotePath: String, + private val oldRemotePath: String, val newAlbumName: String, private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut ) : RemoteOperation() { - /** - * Performs the operation. - * - * @param client Client object to communicate with the remote ownCloud server. - */ @Deprecated("Deprecated in Java") - @Suppress("TooGenericExceptionCaught") - override fun run(client: OwnCloudClient): RemoteOperationResult? { - if (this.newAlbumName == this.mOldRemotePath) { - return RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + @Suppress("TooGenericExceptionCaught", "DEPRECATION") + override fun run(client: OwnCloudClient): RemoteOperationResult { + if (newAlbumName == oldRemotePath) { + return RemoteOperationResult(ResultCode.OK) } - var result: RemoteOperationResult? = null var move: MoveMethod? = null - val url = "${client.baseUri}/remote.php/dav/photos/${client.userId}/albums" - try { - move = - MoveMethod( - "$url${WebdavUtils.encodePath(mOldRemotePath)}", - "$url${ - WebdavUtils.encodePath( - newAlbumName - ) - }", - false - ) - client.executeMethod( - move, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - result = RemoteOperationResult(move.succeeded(), move) - Log_OC.i( - TAG, - "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}" - ) - // album name already exist - if (!result.isSuccess && result.httpCode == HttpStatus.SC_PRECONDITION_FAILED) { - result = RemoteOperationResult(RemoteOperationResult.ResultCode.INVALID_OVERWRITE) - } + return try { + move = MoveMethod(client.albumUri(oldRemotePath), client.albumUri(newAlbumName), false) + client.executeMethod(move, sessionTimeOut.readTimeOut, sessionTimeOut.connectionTimeOut) + + val renamed = RemoteOperationResult(move.succeeded(), move) + Log_OC.i(TAG, "Rename $oldRemotePath to $newAlbumName : ${renamed.logMessage}") + + val result = + if (renamed.httpCode == HttpStatus.SC_PRECONDITION_FAILED) { + // the target album name is already taken + RemoteOperationResult(ResultCode.INVALID_OVERWRITE) + } else { + renamed + } client.exhaustResponse(move.responseBodyAsStream) + result } catch (e: Exception) { - result = RemoteOperationResult(e) - Log_OC.e( - TAG, - "Rename ${this.mOldRemotePath} to ${this.newAlbumName} : ${result.logMessage}", - e - ) + failure(e) } finally { move?.releaseConnection() } - - return result } + @Suppress("DEPRECATION") + private fun failure(e: Exception): RemoteOperationResult = + RemoteOperationResult(e).also { + Log_OC.e(TAG, "Rename $oldRemotePath to $newAlbumName : ${it.logMessage}", e) + } + companion object { private val TAG: String = RenameAlbumRemoteOperation::class.java.simpleName } diff --git a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt index 5b66b14d11..9d3fb8f05c 100644 --- a/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt +++ b/library/src/main/java/com/owncloud/android/lib/resources/albums/ToggleAlbumFavoriteRemoteOperation.kt @@ -1,6 +1,7 @@ /* * Nextcloud Android Library * + * SPDX-FileCopyrightText: 2026 Alper Ozturk * SPDX-FileCopyrightText: 2026 TSI-mc * SPDX-License-Identifier: MIT */ @@ -10,17 +11,12 @@ import com.nextcloud.common.SessionTimeOut import com.nextcloud.common.defaultSessionTimeOut import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.network.WebdavEntry -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.resources.files.ToggleFavoriteRemoteOperation -import org.apache.commons.httpclient.HttpStatus -import org.apache.jackrabbit.webdav.client.methods.PropPatchMethod -import org.apache.jackrabbit.webdav.property.DavPropertyNameSet -import org.apache.jackrabbit.webdav.property.DavPropertySet import org.apache.jackrabbit.webdav.property.DefaultDavProperty import org.apache.jackrabbit.webdav.xml.Namespace -import java.io.IOException class ToggleAlbumFavoriteRemoteOperation @JvmOverloads @@ -30,55 +26,32 @@ class ToggleAlbumFavoriteRemoteOperation private val sessionTimeOut: SessionTimeOut = defaultSessionTimeOut ) : RemoteOperation() { @Deprecated("Deprecated in Java") - @Suppress("ReturnCount") + @Suppress("DEPRECATION") override fun run(client: OwnCloudClient): RemoteOperationResult { - if (filePath.isEmpty() || filePath.isBlank()) { - return RemoteOperationResult(RemoteOperationResult.ResultCode.OK) + if (filePath.isBlank()) { + return RemoteOperationResult(ResultCode.OK) } - // when file is in local db the remotePath will be actual path instead of albums path - // to perform operation we have to call files dav uri - if (!filePath.contains("/albums/")) { - return ToggleFavoriteRemoteOperation(markAsFavorite, filePath).execute(client) + // a file taken from the local db carries its files path, not its album path, so it has to + // go through the files endpoint instead + return if (filePath.contains(ALBUM_PATH_SEGMENT)) { + client.setPhotosProperty(client.photosUri(filePath), favoriteProperty(), sessionTimeOut) + } else { + ToggleFavoriteRemoteOperation(markAsFavorite, filePath).execute(client) } + } - var result: RemoteOperationResult - var propPatchMethod: PropPatchMethod? = null - val newProps = DavPropertySet() - val removeProperties = DavPropertyNameSet() - val favoriteProperty = - DefaultDavProperty( - "oc:favorite", - if (markAsFavorite) "1" else "0", - Namespace.getNamespace(WebdavEntry.NAMESPACE_OC) - ) - newProps.add(favoriteProperty) - - val webDavUrl = "${client.davUri}/photos/" - val encodedPath = "${client.userId}${WebdavUtils.encodePath(this.filePath)}" - val fullFilePath = "$webDavUrl$encodedPath" - - try { - propPatchMethod = PropPatchMethod(fullFilePath, newProps, removeProperties) - val status = - client.executeMethod( - propPatchMethod, - sessionTimeOut.readTimeOut, - sessionTimeOut.connectionTimeOut - ) - val isSuccess = (status == HttpStatus.SC_MULTI_STATUS || status == HttpStatus.SC_OK) - if (isSuccess) { - result = RemoteOperationResult(true, status, propPatchMethod.responseHeaders) - } else { - client.exhaustResponse(propPatchMethod.responseBodyAsStream) - result = RemoteOperationResult(false, status, propPatchMethod.responseHeaders) - } - } catch (e: IOException) { - result = RemoteOperationResult(e) - } finally { - propPatchMethod?.releaseConnection() - } + private fun favoriteProperty(): DefaultDavProperty = + DefaultDavProperty( + FAVORITE_PROPERTY, + if (markAsFavorite) FAVORITE else NOT_FAVORITE, + Namespace.getNamespace(WebdavEntry.NAMESPACE_OC) + ) - return result + companion object { + private const val ALBUM_PATH_SEGMENT = "$ALBUMS_PATH/" + private const val FAVORITE_PROPERTY = "oc:favorite" + private const val FAVORITE = "1" + private const val NOT_FAVORITE = "0" } } diff --git a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt index d3a9b45f11..7610aaba41 100644 --- a/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt +++ b/library/src/test/java/com/owncloud/android/lib/resources/albums/PhotoAlbumEntryTest.kt @@ -7,50 +7,49 @@ package com.owncloud.android.lib.resources.albums +import org.apache.commons.httpclient.HttpStatus import org.apache.jackrabbit.webdav.MultiStatusResponse import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue import org.junit.Test class PhotoAlbumEntryTest { @Test - fun testAlbumName_withTrailingSlash() { - val entry = createTestEntry("/remote.php/dav/photos/user_id/albums/vacation2024/") - assertEquals("vacation2024", entry.albumName) + fun albumNameFromHrefWithTrailingSlash() { + assertEquals("vacation2024", entryOf("/remote.php/dav/photos/user_id/albums/vacation2024/").albumName) } @Test - fun testAlbumName_withoutTrailingSlash() { - val entry = createTestEntry("/remote.php/dav/photos/user_id/albums/vacation2024") - assertEquals("vacation2024", entry.albumName) + fun albumNameFromHrefWithoutTrailingSlash() { + assertEquals("vacation2024", entryOf("/remote.php/dav/photos/user_id/albums/vacation2024").albumName) } @Test - fun testAlbumName_nestedPath() { - val entry = createTestEntry("/remote.php/dav/photos/user_id/albums/travel/europe/") - assertEquals("europe", entry.albumName) + fun albumNameFromNestedHref() { + assertEquals("europe", entryOf("/remote.php/dav/photos/user_id/albums/travel/europe/").albumName) } @Test - fun testAlbumName_singleSlash() { - val entry = createTestEntry("/") - assertEquals("", entry.albumName) + fun albumNameIsEmptyForHrefWithoutName() { + assertEquals("", entryOf("/").albumName) + assertEquals("", entryOf("///").albumName) } @Test - fun testAlbumName_onlySlashes() { - val entry = createTestEntry("///") - assertEquals("", entry.albumName) + fun albumNameFromHrefWithoutSeparator() { + assertEquals("holiday", entryOf("holiday").albumName) } @Test - fun testAlbumName_noSlash() { - val entry = createTestEntry("holiday") - assertEquals("holiday", entry.albumName) - } + fun albumWithoutPropertiesFallsBackToDefaults() { + val entry = entryOf("/remote.php/dav/photos/user_id/albums/vacation2024/") - // Helper method to create a stub entry - private fun createTestEntry(href: String): PhotoAlbumEntry { - val response = MultiStatusResponse(href, 200) - return PhotoAlbumEntry("https://www.example.com/", response) + assertEquals(0L, entry.lastPhoto) + assertEquals(0, entry.nbItems) + assertEquals(null, entry.location) + assertTrue(entry.collaborators.isEmpty()) } + + private fun entryOf(href: String): PhotoAlbumEntry = + PhotoAlbumEntry("https://www.example.com", MultiStatusResponse(href, HttpStatus.SC_OK)) }