From 4d1bc6275fc5c808f060b99dee440f48c5343b00 Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:24:49 +0100 Subject: [PATCH 01/19] Extend capabilities by files download limit Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../86.json | 30 +++++++++++++++---- .../database/entity/CapabilityEntity.kt | 4 +++ .../client/database/entity/ShareEntity.kt | 6 +++- .../com/owncloud/android/db/ProviderMeta.java | 4 +++ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json index 3f02565046cf..223c205aa90c 100644 --- a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 86, - "identityHash": "a657c5557784ebaff12f7709fc3fd6dd", + "identityHash": "9d5fd23c8ad44a5331aba97622c75caa", "entities": [ { "tableName": "arbitrary_data", @@ -44,7 +44,7 @@ }, { "tableName": "capabilities", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `recommendation` INTEGER)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER)", "fields": [ { "fieldPath": "id", @@ -395,8 +395,14 @@ "notNull": false }, { - "fieldPath": "recommendation", - "columnName": "recommendation", + "fieldPath": "filesDownloadLimit", + "columnName": "files_download_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "filesDownloadLimitDefault", + "columnName": "files_download_limit_default", "affinity": "INTEGER", "notNull": false } @@ -851,7 +857,7 @@ }, { "tableName": "ocshares", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `file_source` INTEGER, `item_source` INTEGER, `share_type` INTEGER, `shate_with` TEXT, `path` TEXT, `permissions` INTEGER, `shared_date` INTEGER, `expiration_date` INTEGER, `token` TEXT, `shared_with_display_name` TEXT, `is_directory` INTEGER, `user_id` TEXT, `id_remote_shared` INTEGER, `owner_share` TEXT, `is_password_protected` INTEGER, `note` TEXT, `hide_download` INTEGER, `share_link` TEXT, `share_label` TEXT, `download_limit_limit` INTEGER, `download_limit_count` INTEGER)", "fields": [ { "fieldPath": "id", @@ -972,6 +978,18 @@ "columnName": "share_label", "affinity": "TEXT", "notNull": false + }, + { + "fieldPath": "downloadLimitLimit", + "columnName": "download_limit_limit", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "downloadLimitCount", + "columnName": "download_limit_count", + "affinity": "INTEGER", + "notNull": false } ], "primaryKey": { @@ -1301,7 +1319,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a657c5557784ebaff12f7709fc3fd6dd')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9d5fd23c8ad44a5331aba97622c75caa')" ] } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt index 2f87b56e05bf..4662877862ca 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/CapabilityEntity.kt @@ -131,6 +131,10 @@ data class CapabilityEntity( val forbiddenFileNameExtensions: Int?, @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES) val forbiddenFilenameBaseNames: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT) + val filesDownloadLimit: Int?, + @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT) + val filesDownloadLimitDefault: Int?, @ColumnInfo(name = ProviderTableMeta.CAPABILITIES_RECOMMENDATION) val recommendation: Int? ) diff --git a/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt index b24c21c873d3..1c397ced030d 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/ShareEntity.kt @@ -54,5 +54,9 @@ data class ShareEntity( @ColumnInfo(name = ProviderTableMeta.OCSHARES_SHARE_LINK) val shareLink: String?, @ColumnInfo(name = ProviderTableMeta.OCSHARES_SHARE_LABEL) - val shareLabel: String? + val shareLabel: String?, + @ColumnInfo(name = ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT) + val downloadLimitLimit: Int?, + @ColumnInfo(name = ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT) + val downloadLimitCount: Int? ) diff --git a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java index 3029055e07de..db2302caa79b 100644 --- a/app/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/app/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -199,6 +199,8 @@ static public class ProviderTableMeta implements BaseColumns { public static final String OCSHARES_HIDE_DOWNLOAD = "hide_download"; public static final String OCSHARES_SHARE_LINK = "share_link"; public static final String OCSHARES_SHARE_LABEL = "share_label"; + public static final String OCSHARES_DOWNLOADLIMIT_LIMIT = "download_limit_limit"; + public static final String OCSHARES_DOWNLOADLIMIT_COUNT = "download_limit_count"; public static final String OCSHARES_DEFAULT_SORT_ORDER = OCSHARES_FILE_SOURCE + " collate nocase asc"; @@ -268,6 +270,8 @@ static public class ProviderTableMeta implements BaseColumns { public static final String CAPABILITIES_FORBIDDEN_FILENAMES = "forbidden_filenames"; public static final String CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS = "forbidden_filename_extensions"; public static final String CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES = "forbidden_filename_basenames"; + public static final String CAPABILITIES_FILES_DOWNLOAD_LIMIT = "files_download_limit"; + public static final String CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT = "files_download_limit_default"; //Columns of Uploads table public static final String UPLOADS_LOCAL_PATH = "local_path"; From ec1e97ae93d1679b7eec35f00ccda132755c0b1f Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:27:57 +0100 Subject: [PATCH 02/19] Add operations for files download limit Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../GetFilesDownloadLimitOperation.kt | 27 +++++++++++++++ .../SetFilesDownloadLimitOperation.kt | 34 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt create mode 100644 app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt diff --git a/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt new file mode 100644 index 000000000000..7e2bbcd9e521 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt @@ -0,0 +1,27 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 ZetaTom <70907959+zetatom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations + +import com.nextcloud.android.lib.resources.files.FileDownloadLimit +import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation +import com.nextcloud.common.NextcloudClient +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.operations.common.SyncOperation + +class GetFilesDownloadLimitOperation(val share: OCShare, storageManager: FileDataStorageManager) : SyncOperation(storageManager) { + override fun run(client: NextcloudClient): RemoteOperationResult> { + val token = share.token!! + val operation = GetFilesDownloadLimitRemoteOperation(token) + + val result = operation.execute(client) + + return result + } +} \ No newline at end of file diff --git a/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt new file mode 100644 index 000000000000..1a582b47aff4 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt @@ -0,0 +1,34 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 ZetaTom <70907959+zetatom@users.noreply.github.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.operations + +import com.nextcloud.android.lib.resources.files.RemoveFilesDownloadLimitRemoteOperation +import com.nextcloud.android.lib.resources.files.SetFilesDownloadLimitRemoteOperation +import com.nextcloud.common.NextcloudClient +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult + +class SetFilesDownloadLimitOperation( + private val shareId: Long, + private val newLimit: Int, + private val fileDataStorageManager: FileDataStorageManager +) : RemoteOperation() { + override fun run(client: NextcloudClient): RemoteOperationResult { + val share = fileDataStorageManager.getShareById(shareId) + val token = share?.token ?: return RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) + + return if (newLimit > 0) { + val operation = SetFilesDownloadLimitRemoteOperation(token, newLimit) + client.execute(operation) + } else { + val operation = RemoveFilesDownloadLimitRemoteOperation(token) + client.execute(operation) + } + } +} \ No newline at end of file From f87fddbe0c99d2153a412c3a61692290c73a422e Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:29:49 +0100 Subject: [PATCH 03/19] Refactor GetSharesForFileOperation Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- ...etSharesForFileOperation.java => GetSharesForFileOperation.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/owncloud/android/operations/{GetSharesForFileOperation.java => GetSharesForFileOperation.kt} (100%) diff --git a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.java b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt similarity index 100% rename from app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.java rename to app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt From a6b5c7a51f5ea9f8a845770be089a042706bc01d Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:29:49 +0100 Subject: [PATCH 04/19] Get files download limit on refresh Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../datamodel/FileDataStorageManager.java | 13 ++- .../operations/GetSharesForFileOperation.kt | 104 +++++++++--------- .../operations/RefreshFolderOperation.java | 27 +---- .../android/services/OperationsService.java | 13 ++- .../ui/helpers/FileOperationsHelper.java | 10 ++ 5 files changed, 87 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 05267332fa6a..d44d983fb817 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -32,6 +32,7 @@ import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; +import com.nextcloud.android.lib.resources.files.FileDownloadLimit; import com.nextcloud.client.account.User; import com.nextcloud.client.database.NextcloudDatabase; import com.nextcloud.client.database.dao.FileDao; @@ -1556,6 +1557,11 @@ private ContentValues createContentValueForShare(OCShare share) { contentValues.put(ProviderTableMeta.OCSHARES_SHARE_LINK, share.getShareLink()); contentValues.put(ProviderTableMeta.OCSHARES_SHARE_LABEL, share.getLabel()); + if (share.getFileDownloadLimit() != null) { + contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT, share.getFileDownloadLimit().getLimit()); + contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT, share.getFileDownloadLimit().getCount()); + } + return contentValues; } @@ -1580,6 +1586,8 @@ private OCShare createShareInstance(Cursor cursor) { share.setShareLink(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_LINK)); share.setLabel(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_LABEL)); + share.setFileDownloadLimit(new FileDownloadLimit(share.getToken(), getInt(cursor, ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT), getInt(cursor, ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT))); + return share; } @@ -2294,6 +2302,8 @@ private ContentValues createContentValues(String accountName, OCCapability capab contentValues.put(ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAMES, capability.getForbiddenFilenamesJson()); contentValues.put(ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS, capability.getForbiddenFilenameExtensionJson()); contentValues.put(ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES, capability.getForbiddenFilenameBaseNamesJson()); + contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT, capability.getFilesDownloadLimit().getValue()); + contentValues.put(ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT, capability.getFilesDownloadLimitDefault()); contentValues.put(ProviderTableMeta.CAPABILITIES_RECOMMENDATION, capability.getRecommendations().getValue()); @@ -2470,7 +2480,8 @@ private OCCapability createCapabilityInstance(Cursor cursor) { capability.setForbiddenFilenamesJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FILENAMES)); capability.setForbiddenFilenameExtensionJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_EXTENSIONS)); capability.setForbiddenFilenameBaseNamesJson(getString(cursor, ProviderTableMeta.CAPABILITIES_FORBIDDEN_FORBIDDEN_FILENAME_BASE_NAMES)); - + capability.setFilesDownloadLimit(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT)); + capability.setFilesDownloadLimitDefault(getInt(cursor, ProviderTableMeta.CAPABILITIES_FILES_DOWNLOAD_LIMIT_DEFAULT)); capability.setRecommendations(getBoolean(cursor, ProviderTableMeta.CAPABILITIES_RECOMMENDATION)); } diff --git a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt index 869f9dbbc534..dd6f36da0b98 100644 --- a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt +++ b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt @@ -7,74 +7,72 @@ * SPDX-FileCopyrightText: 2015 David A. Velasco * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) */ -package com.owncloud.android.operations; +package com.owncloud.android.operations -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.shares.GetSharesForFileRemoteOperation; -import com.owncloud.android.lib.resources.shares.OCShare; -import com.owncloud.android.operations.common.SyncOperation; - -import java.util.ArrayList; +import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.shares.GetSharesForFileRemoteOperation +import com.owncloud.android.lib.resources.shares.OCShare +import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.operations.common.SyncOperation /** * Provide a list shares for a specific file. */ -public class GetSharesForFileOperation extends SyncOperation { - - private static final String TAG = GetSharesForFileOperation.class.getSimpleName(); - - private final String path; - private final boolean reshares; - private final boolean subfiles; +class GetSharesForFileOperation +/** + * Constructor + * + * @param path Path to file or folder + * @param reshares If set to false (default), only shares from the current user are returned If set to true, all + * shares from the given file are returned + * @param subfiles If set to false (default), lists only the folder being shared If set to true, all shared files + * within the folder are returned. + */ + ( + private val path: String, + private val reshares: Boolean, + private val subfiles: Boolean, + storageManager: FileDataStorageManager +) : SyncOperation(storageManager) { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult> { + val getSharesForFileRemoteOperation = GetSharesForFileRemoteOperation(path, reshares, subfiles) - /** - * Constructor - * - * @param path Path to file or folder - * @param reshares If set to false (default), only shares from the current user are returned If set to true, all - * shares from the given file are returned - * @param subfiles If set to false (default), lists only the folder being shared If set to true, all shared files - * within the folder are returned. - */ - public GetSharesForFileOperation(String path, - boolean reshares, - boolean subfiles, - FileDataStorageManager storageManager) { - super(storageManager); + val result = getSharesForFileRemoteOperation.execute(client) - this.path = path; - this.reshares = reshares; - this.subfiles = subfiles; - } + if (result.isSuccess) { + // Update DB with the response + val shares = result.resultData + Log_OC.d(TAG, "File = " + path + " Share list size " + shares.size) - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - GetSharesForFileRemoteOperation operation = new GetSharesForFileRemoteOperation(path, - reshares, - subfiles); - RemoteOperationResult result = operation.execute(client); + val capability = storageManager.getCapability(storageManager.user) - if (result.isSuccess()) { + if (capability.filesDownloadLimit.isTrue && shares.any { it.shareType == ShareType.PUBLIC_LINK }) { + val getFilesDownloadLimitRemoteOperation = GetFilesDownloadLimitRemoteOperation(path, subfiles) + val remoteOperationResult = getFilesDownloadLimitRemoteOperation.execute(client) + val fileDownloadLimits = remoteOperationResult.resultData - // Update DB with the response - Log_OC.d(TAG, "File = " + path + " Share list size " + result.getData().size()); - ArrayList shares = new ArrayList(); - for (Object obj : result.getData()) { - shares.add((OCShare) obj); + fileDownloadLimits.forEach { downloadLimit -> + shares.find { share -> + share.token == downloadLimit.token + }?.fileDownloadLimit = downloadLimit + } } - getStorageManager().saveSharesDB(shares); - - } else if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) { + storageManager.saveSharesDB(shares) + } else if (result.code == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) { // no share on the file - remove local shares - getStorageManager().removeSharesForFile(path); - + storageManager.removeSharesForFile(path) } - return result; + return result } + companion object { + private val TAG: String = GetSharesForFileOperation::class.java.simpleName + } } diff --git a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java index 78c367c4ba33..916ec1331f01 100644 --- a/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/RefreshFolderOperation.java @@ -36,9 +36,6 @@ import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation; import com.owncloud.android.lib.resources.files.ReadFolderRemoteOperation; import com.owncloud.android.lib.resources.files.model.RemoteFile; -import com.owncloud.android.lib.resources.shares.GetSharesForFileRemoteOperation; -import com.owncloud.android.lib.resources.shares.OCShare; -import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.E2EVersion; import com.owncloud.android.lib.resources.users.GetPredefinedStatusesRemoteOperation; import com.owncloud.android.lib.resources.users.PredefinedStatus; @@ -817,28 +814,8 @@ private void startContentSynchronizations(List filesTo * operation. */ private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { - RemoteOperationResult result; - - // remote request - GetSharesForFileRemoteOperation operation = - new GetSharesForFileRemoteOperation(mLocalFolder.getRemotePath(), true, true); - result = operation.execute(client); - - if (result.isSuccess()) { - // update local database - ArrayList shares = new ArrayList<>(); - OCShare share; - for (Object obj : result.getData()) { - share = (OCShare) obj; - - if (ShareType.NO_SHARED != share.getShareType()) { - shares.add(share); - } - } - fileDataStorageManager.saveSharesInFolder(shares, mLocalFolder); - } - - return result; + GetSharesForFileOperation operation = new GetSharesForFileOperation(mLocalFolder.getRemotePath(), true, true, fileDataStorageManager); + return operation.execute(client); } /** diff --git a/app/src/main/java/com/owncloud/android/services/OperationsService.java b/app/src/main/java/com/owncloud/android/services/OperationsService.java index 0cd2d5e30a31..e4801c97f240 100644 --- a/app/src/main/java/com/owncloud/android/services/OperationsService.java +++ b/app/src/main/java/com/owncloud/android/services/OperationsService.java @@ -56,6 +56,7 @@ import com.owncloud.android.operations.MoveFileOperation; import com.owncloud.android.operations.RemoveFileOperation; import com.owncloud.android.operations.RenameFileOperation; +import com.owncloud.android.operations.SetFilesDownloadLimitOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFolderOperation; import com.owncloud.android.operations.UnshareOperation; @@ -65,7 +66,6 @@ import com.owncloud.android.operations.UpdateShareViaLinkOperation; import java.io.IOException; -import java.util.Iterator; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -100,6 +100,7 @@ public class OperationsService extends Service { public static final String EXTRA_SHARE_ID = "SHARE_ID"; public static final String EXTRA_SHARE_NOTE = "SHARE_NOTE"; public static final String EXTRA_IN_BACKGROUND = "IN_BACKGROUND"; + public static final String EXTRA_FILES_DOWNLOAD_LIMIT = "FILES_DOWNLOAD_LIMIT"; public static final String ACTION_CREATE_SHARE_VIA_LINK = "CREATE_SHARE_VIA_LINK"; public static final String ACTION_CREATE_SECURE_FILE_DROP = "CREATE_SECURE_FILE_DROP"; @@ -120,6 +121,7 @@ public class OperationsService extends Service { public static final String ACTION_COPY_FILE = "COPY_FILE"; public static final String ACTION_CHECK_CURRENT_CREDENTIALS = "CHECK_CURRENT_CREDENTIALS"; public static final String ACTION_RESTORE_VERSION = "RESTORE_VERSION"; + public static final String ACTION_UPDATE_FILES_DOWNLOAD_LIMIT = "UPDATE_FILES_DOWNLOAD_LIMIT"; private ServiceHandler mOperationsHandler; private OperationsServiceBinder mOperationsBinder; @@ -735,6 +737,15 @@ private Pair newOperation(Intent operationIntent) { fileVersion.getFileName()); break; + case ACTION_UPDATE_FILES_DOWNLOAD_LIMIT: + shareId = operationIntent.getLongExtra(EXTRA_SHARE_ID, -1); + int newLimit = operationIntent.getIntExtra(EXTRA_FILES_DOWNLOAD_LIMIT, -1); + + if (shareId > 0) { + operation = new SetFilesDownloadLimitOperation(shareId, newLimit, fileDataStorageManager); + } + break; + default: // do nothing break; diff --git a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 6b765c2afd2d..f6fa855dfd63 100755 --- a/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/app/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -741,6 +741,16 @@ public void updateNoteToShare(OCShare share, String note) { queueShareIntent(updateShareIntent); } + public void updateFilesDownloadLimit(OCShare share, int newLimit) { + Intent updateDownloadLimitIntent = new Intent(fileActivity, OperationsService.class); + updateDownloadLimitIntent.setAction(OperationsService.ACTION_UPDATE_FILES_DOWNLOAD_LIMIT); + updateDownloadLimitIntent.putExtra(OperationsService.EXTRA_ACCOUNT, fileActivity.getAccount()); + updateDownloadLimitIntent.putExtra(OperationsService.EXTRA_SHARE_ID, share.getId()); + updateDownloadLimitIntent.putExtra(OperationsService.EXTRA_FILES_DOWNLOAD_LIMIT, newLimit); + + queueShareIntent(updateDownloadLimitIntent); + } + /** * Helper method to update the share information * From 9f32169c2400c74b677e790d62e1c13742625b03 Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:37:31 +0100 Subject: [PATCH 05/19] Show files download limit in FileDetailsSharingProcessFragment Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../ui/adapter/LinkShareViewHolder.java | 13 +++ .../FileDetailsSharingProcessFragment.kt | 85 ++++++++++++++----- .../file_details_share_link_share_item.xml | 16 +++- .../file_details_sharing_process_fragment.xml | 31 +++++++ app/src/main/res/values/strings.xml | 3 + 5 files changed, 124 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java index f35d5ce7705e..6803d66667fa 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java @@ -19,6 +19,7 @@ import android.text.TextUtils; import android.view.View; +import com.nextcloud.android.lib.resources.files.FileDownloadLimit; import com.nextcloud.utils.mdm.MDMConfig; import com.owncloud.android.R; import com.owncloud.android.databinding.FileDetailsShareLinkShareItemBinding; @@ -76,6 +77,18 @@ public void bind(OCShare publicShare, ShareeListAdapterListener listener) { viewThemeUtils.platform.colorImageViewBackgroundAndIcon(binding.icon); } + if (publicShare.getFileDownloadLimit() != null) { + FileDownloadLimit downloadLimit = publicShare.getFileDownloadLimit(); + + if (downloadLimit.getLimit() > 0) { + int remaining = downloadLimit.getLimit() - downloadLimit.getCount(); + String text = String.format(context.getString(R.string.share_download_limit_description), remaining); + + binding.subline.setText(text); + binding.subline.setVisibility(View.VISIBLE); + } + } + String permissionName = SharingMenuHelper.getPermissionName(context, publicShare); setPermissionName(publicShare, permissionName); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt index fc806d4ab8cb..26d63110ec0a 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt @@ -27,12 +27,14 @@ import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.resources.shares.OCShare import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder import com.owncloud.android.lib.resources.shares.ShareType +import com.owncloud.android.lib.resources.status.OCCapability import com.owncloud.android.ui.activity.FileActivity import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment import com.owncloud.android.ui.fragment.util.SharingMenuHelper import com.owncloud.android.ui.helpers.FileOperationsHelper import com.owncloud.android.utils.ClipboardUtil import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.theme.CapabilityUtils import com.owncloud.android.utils.theme.ViewThemeUtils import java.text.SimpleDateFormat import java.util.Date @@ -129,6 +131,8 @@ class FileDetailsSharingProcessFragment : private var isExpDateShown: Boolean = true // show or hide expiry date option private var isSecureShare: Boolean = false + private lateinit var capabilities: OCCapability + private var expirationDatePickerFragment: ExpirationDatePickerDialogFragment? = null override fun onAttach(context: Context) { @@ -161,6 +165,7 @@ class FileDetailsSharingProcessFragment : } fileActivity = activity as FileActivity? + capabilities = CapabilityUtils.getCapability(context) requireNotNull(fileActivity) { "FileActivity may not be null" } } @@ -195,10 +200,12 @@ class FileDetailsSharingProcessFragment : viewThemeUtils.androidx.colorSwitchCompat(binding.shareProcessSetPasswordSwitch) viewThemeUtils.androidx.colorSwitchCompat(binding.shareProcessSetExpDateSwitch) + viewThemeUtils.androidx.colorSwitchCompat(binding.shareProcessSetDownloadLimitSwitch) viewThemeUtils.androidx.colorSwitchCompat(binding.shareProcessHideDownloadCheckbox) viewThemeUtils.androidx.colorSwitchCompat(binding.shareProcessChangeNameSwitch) viewThemeUtils.material.colorTextInputLayout(binding.shareProcessEnterPasswordContainer) + viewThemeUtils.material.colorTextInputLayout(binding.shareProcessSetDownloadLimitInputContainer) viewThemeUtils.material.colorTextInputLayout(binding.shareProcessChangeNameContainer) viewThemeUtils.material.colorTextInputLayout(binding.noteContainer) @@ -259,9 +266,7 @@ class FileDetailsSharingProcessFragment : // show different text for link share and other shares // because we have link to share in Public Link - val resources = requireContext().resources - - binding.shareProcessBtnNext.text = resources.getString( + binding.shareProcessBtnNext.text = getString( if (shareType == ShareType.PUBLIC_LINK) { R.string.share_copy_link } else { @@ -274,10 +279,12 @@ class FileDetailsSharingProcessFragment : showPasswordInput(binding.shareProcessSetPasswordSwitch.isChecked) updateExpirationDateView() showExpirationDateInput(binding.shareProcessSetExpDateSwitch.isChecked) + updateFileDownloadLimitView() + showFileDownloadLimitInput(binding.shareProcessSetDownloadLimitSwitch.isChecked) } private fun setupUpdateUI() { - binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.common_next) + binding.shareProcessBtnNext.text = getString(R.string.common_next) file.let { if (file?.isFolder == true) { updateViewForFolder() @@ -288,6 +295,7 @@ class FileDetailsSharingProcessFragment : } showPasswordInput(binding.shareProcessSetPasswordSwitch.isChecked) showExpirationDateInput(binding.shareProcessSetExpDateSwitch.isChecked) + showFileDownloadLimitInput(binding.shareProcessSetDownloadLimitSwitch.isChecked) } private fun updateViewForShareType() { @@ -363,29 +371,38 @@ class FileDetailsSharingProcessFragment : * update expiration date view while modifying the share */ private fun updateExpirationDateView() { - if (share != null) { - if ((share?.expirationDate ?: 0) > 0) { - chosenExpDateInMills = share?.expirationDate ?: -1 + share?.let { share -> + if (share.expirationDate > 0) { + chosenExpDateInMills = share.expirationDate binding.shareProcessSetExpDateSwitch.isChecked = true - binding.shareProcessSelectExpDate.text = ( - resources.getString( + binding.shareProcessSelectExpDate.text = getString( R.string.share_expiration_date_format, - SimpleDateFormat.getDateInstance().format(Date(share?.expirationDate ?: 0)) - ) + SimpleDateFormat.getDateInstance().format(Date(share.expirationDate)) ) } } } + private fun updateFileDownloadLimitView() { + if (capabilities.filesDownloadLimit.isTrue) { + binding.shareProcessSetDownloadLimitSwitch.visibility = View.VISIBLE + + val currentDownloadLimit = share?.fileDownloadLimit?.limit ?: capabilities.filesDownloadLimitDefault + if (currentDownloadLimit > 0) { + binding.shareProcessSetDownloadLimitSwitch.isChecked = true + showFileDownloadLimitInput(true) + binding.shareProcessSetDownloadLimitInput.setText("$currentDownloadLimit") + } + } + } + private fun updateViewForFile() { - binding.shareProcessPermissionUploadEditing.text = - requireContext().resources.getString(R.string.link_share_editing) + binding.shareProcessPermissionUploadEditing.text = getString(R.string.link_share_editing) binding.shareProcessPermissionFileDrop.visibility = View.GONE } private fun updateViewForFolder() { - binding.shareProcessPermissionUploadEditing.text = - requireContext().resources.getString(R.string.link_share_allow_upload_and_editing) + binding.shareProcessPermissionUploadEditing.text = getString(R.string.link_share_allow_upload_and_editing) binding.shareProcessPermissionFileDrop.visibility = View.VISIBLE if (isSecureShare) { binding.shareProcessPermissionFileDrop.visibility = View.GONE @@ -402,10 +419,10 @@ class FileDetailsSharingProcessFragment : binding.shareProcessEditShareLink.visibility = View.GONE binding.shareProcessGroupTwo.visibility = View.VISIBLE if (share != null) { - binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.set_note) + binding.shareProcessBtnNext.text = getString(R.string.set_note) binding.noteText.setText(share?.note) } else { - binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.send_share) + binding.shareProcessBtnNext.text = getString(R.string.send_share) binding.noteText.setText(R.string.empty) } shareProcessStep = SCREEN_TYPE_NOTE @@ -428,6 +445,9 @@ class FileDetailsSharingProcessFragment : binding.shareProcessSetExpDateSwitch.setOnCheckedChangeListener { _, isChecked -> showExpirationDateInput(isChecked) } + binding.shareProcessSetDownloadLimitSwitch.setOnCheckedChangeListener { _, isChecked -> + showFileDownloadLimitInput(isChecked) + } binding.shareProcessChangeNameSwitch.setOnCheckedChangeListener { _, isChecked -> showChangeNameInput(isChecked) } @@ -482,6 +502,15 @@ class FileDetailsSharingProcessFragment : } } + private fun showFileDownloadLimitInput(isChecked: Boolean) { + binding.shareProcessSetDownloadLimitInputContainer.visibility = if (isChecked) View.VISIBLE else View.GONE + + // reset download limit if switch is unchecked + if (!isChecked) { + binding.shareProcessSetDownloadLimitInput.setText(R.string.empty) + } + } + private fun showPasswordInput(isChecked: Boolean) { binding.shareProcessEnterPasswordContainer.visibility = if (isChecked) View.VISIBLE else View.GONE @@ -568,6 +597,19 @@ class FileDetailsSharingProcessFragment : chosenExpDateInMills, binding.shareProcessChangeName.text.toString().trim() ) + + if (capabilities.filesDownloadLimit.isTrue) { + val downloadLimitInput = binding.shareProcessSetDownloadLimitInput.text.toString().trim() + val downloadLimit = + if (binding.shareProcessSetDownloadLimitSwitch.isChecked && downloadLimitInput.isNotEmpty()) { + downloadLimitInput.toInt() + } else { + 0 + } + + fileOperationsHelper?.updateFilesDownloadLimit(share, downloadLimit) + } + // copy the share link if available if (!TextUtils.isEmpty(share?.shareLink)) { ClipboardUtil.copyToClipboard(requireActivity(), share?.shareLink) @@ -609,12 +651,9 @@ class FileDetailsSharingProcessFragment : } override fun onDateSet(year: Int, monthOfYear: Int, dayOfMonth: Int, chosenDateInMillis: Long) { - binding.shareProcessSelectExpDate.text = ( - resources.getString( - R.string.share_expiration_date_format, - SimpleDateFormat.getDateInstance().format(Date(chosenDateInMillis)) - ) - ) + binding.shareProcessSelectExpDate.text = getString( + R.string.share_expiration_date_format, SimpleDateFormat.getDateInstance().format(Date(chosenDateInMillis)) + ) this.chosenExpDateInMills = chosenDateInMillis } diff --git a/app/src/main/res/layout/file_details_share_link_share_item.xml b/app/src/main/res/layout/file_details_share_link_share_item.xml index bfb626772958..ea32fd599b39 100644 --- a/app/src/main/res/layout/file_details_share_link_share_item.xml +++ b/app/src/main/res/layout/file_details_share_link_share_item.xml @@ -13,7 +13,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/share_by_link_container" android:layout_width="match_parent" - android:layout_height="@dimen/sharee_list_item_size" + android:layout_height="wrap_content" + android:minHeight="@dimen/sharee_list_item_size" android:orientation="horizontal"> + + + + + + + + + + + + Changes to auto upload Due to new restrictions imposed by Google, we have been forced to remove an important permission. We are currently working with Google to resolve this issue and restore full functionality.\n\nTo re-enable auto upload for new photos and videos:\nSelect \"Allow all\" in the following dialogue or the system settings.\nAllow media location when prompted, as this allows Nextcloud to store location data when uploading images.\n\nThe permissions dialogue is only displayed when necessary. If in doubt, check the system settings.\n\nAuto upload will only be able to upload image and video files when using the Google Play version of the Nextcloud app.\n\nPlease check for any files that may not have been uploaded since December 2024. Manual intervention required to re-enable auto-upload + Set download limit + Download limit + %1$d downloads remaining From f787c348a1e9779ea604797cd146805287f6ce5f Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Tue, 14 Jan 2025 10:16:17 +0100 Subject: [PATCH 06/19] Fix lint Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../operations/GetFilesDownloadLimitOperation.kt | 6 ++++-- .../android/operations/GetSharesForFileOperation.kt | 12 +----------- .../operations/SetFilesDownloadLimitOperation.kt | 2 +- .../ui/fragment/FileDetailsSharingProcessFragment.kt | 7 ++++--- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt index 7e2bbcd9e521..4defa89f4af8 100644 --- a/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt +++ b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt @@ -15,7 +15,9 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.resources.shares.OCShare import com.owncloud.android.operations.common.SyncOperation -class GetFilesDownloadLimitOperation(val share: OCShare, storageManager: FileDataStorageManager) : SyncOperation(storageManager) { +class GetFilesDownloadLimitOperation(val share: OCShare, storageManager: FileDataStorageManager) : SyncOperation( + storageManager +) { override fun run(client: NextcloudClient): RemoteOperationResult> { val token = share.token!! val operation = GetFilesDownloadLimitRemoteOperation(token) @@ -24,4 +26,4 @@ class GetFilesDownloadLimitOperation(val share: OCShare, storageManager: FileDat return result } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt index dd6f36da0b98..d05377ed820b 100644 --- a/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt +++ b/app/src/main/java/com/owncloud/android/operations/GetSharesForFileOperation.kt @@ -22,17 +22,7 @@ import com.owncloud.android.operations.common.SyncOperation /** * Provide a list shares for a specific file. */ -class GetSharesForFileOperation -/** - * Constructor - * - * @param path Path to file or folder - * @param reshares If set to false (default), only shares from the current user are returned If set to true, all - * shares from the given file are returned - * @param subfiles If set to false (default), lists only the folder being shared If set to true, all shared files - * within the folder are returned. - */ - ( +class GetSharesForFileOperation( private val path: String, private val reshares: Boolean, private val subfiles: Boolean, diff --git a/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt index 1a582b47aff4..46c648dd5c5e 100644 --- a/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt +++ b/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt @@ -31,4 +31,4 @@ class SetFilesDownloadLimitOperation( client.execute(operation) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt index 26d63110ec0a..b20ca4c91dd4 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt @@ -376,9 +376,9 @@ class FileDetailsSharingProcessFragment : chosenExpDateInMills = share.expirationDate binding.shareProcessSetExpDateSwitch.isChecked = true binding.shareProcessSelectExpDate.text = getString( - R.string.share_expiration_date_format, + R.string.share_expiration_date_format, SimpleDateFormat.getDateInstance().format(Date(share.expirationDate)) - ) + ) } } } @@ -652,7 +652,8 @@ class FileDetailsSharingProcessFragment : override fun onDateSet(year: Int, monthOfYear: Int, dayOfMonth: Int, chosenDateInMillis: Long) { binding.shareProcessSelectExpDate.text = getString( - R.string.share_expiration_date_format, SimpleDateFormat.getDateInstance().format(Date(chosenDateInMillis)) + R.string.share_expiration_date_format, + SimpleDateFormat.getDateInstance().format(Date(chosenDateInMillis)) ) this.chosenExpDateInMills = chosenDateInMillis } From c0c9f05930d9631109686ea32165fb749181ab9e Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:34:01 +0100 Subject: [PATCH 07/19] Add screenshot tests for files download limit Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../fragment/FileDetailSharingFragmentIT.kt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt index d4c5de954adf..2fc0e30238a0 100644 --- a/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt +++ b/app/src/androidTest/java/com/owncloud/android/ui/fragment/FileDetailSharingFragmentIT.kt @@ -22,6 +22,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withText import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultBaseUtils.matchesCheckNames import com.google.android.apps.common.testing.accessibility.framework.AccessibilityCheckResultUtils.matchesViews import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.nextcloud.android.lib.resources.files.FileDownloadLimit import com.nextcloud.test.RetryTestRule import com.nextcloud.test.TestActivity import com.owncloud.android.AbstractIT @@ -93,6 +94,51 @@ class FileDetailSharingFragmentIT : AbstractIT() { show(file) } + @Test + @ScreenshotTest + fun listSharesDownloadLimit() { + OCShare(file.decryptedRemotePath).apply { + remoteId = 1 + shareType = ShareType.PUBLIC_LINK + token = "AAAAAAAAAAAAAAA" + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 2 + shareType = ShareType.PUBLIC_LINK + token = "BBBBBBBBBBBBBBB" + fileDownloadLimit = FileDownloadLimit("BBBBBBBBBBBBBBB", 0, 0) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 3 + shareType = ShareType.PUBLIC_LINK + token = "CCCCCCCCCCCCCCC" + fileDownloadLimit = FileDownloadLimit("CCCCCCCCCCCCCCC", 10, 0) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 4 + shareType = ShareType.PUBLIC_LINK + token = "DDDDDDDDDDDDDDD" + fileDownloadLimit = FileDownloadLimit("DDDDDDDDDDDDDDD", 10, 5) + activity.storageManager.saveShare(this) + } + + OCShare(file.decryptedRemotePath).apply { + remoteId = 5 + shareType = ShareType.PUBLIC_LINK + token = "FFFFFFFFFFFFFFF" + fileDownloadLimit = FileDownloadLimit("FFFFFFFFFFFFFFF", 10, 10) + activity.storageManager.saveShare(this) + } + + show(file) + } + /** * Use same values as {@link OCFileListFragmentStaticServerIT showSharedFiles } */ From e4443f0949d869c803016ea55c33d7b4944942b2 Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:29:35 +0100 Subject: [PATCH 08/19] Do not allow to set download limit for folders Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../android/operations/GetFilesDownloadLimitOperation.kt | 2 +- .../android/ui/fragment/FileDetailsSharingProcessFragment.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt index 4defa89f4af8..2f13bc4f4ead 100644 --- a/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt +++ b/app/src/main/java/com/owncloud/android/operations/GetFilesDownloadLimitOperation.kt @@ -19,7 +19,7 @@ class GetFilesDownloadLimitOperation(val share: OCShare, storageManager: FileDat storageManager ) { override fun run(client: NextcloudClient): RemoteOperationResult> { - val token = share.token!! + val token = share.token ?: return RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) val operation = GetFilesDownloadLimitRemoteOperation(token) val result = operation.execute(client) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt index b20ca4c91dd4..ef64f7e0bc80 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt @@ -384,7 +384,7 @@ class FileDetailsSharingProcessFragment : } private fun updateFileDownloadLimitView() { - if (capabilities.filesDownloadLimit.isTrue) { + if (capabilities.filesDownloadLimit.isTrue && share?.isFolder == false) { binding.shareProcessSetDownloadLimitSwitch.visibility = View.VISIBLE val currentDownloadLimit = share?.fileDownloadLimit?.limit ?: capabilities.filesDownloadLimitDefault From f93ce96cb0059f536671a730a41d382ed702d3e8 Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Wed, 15 Jan 2025 13:27:18 +0100 Subject: [PATCH 09/19] Use string quantities Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../com/owncloud/android/ui/adapter/LinkShareViewHolder.java | 2 +- app/src/main/res/values/strings.xml | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java index 6803d66667fa..2b2e6f146a51 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java @@ -82,7 +82,7 @@ public void bind(OCShare publicShare, ShareeListAdapterListener listener) { if (downloadLimit.getLimit() > 0) { int remaining = downloadLimit.getLimit() - downloadLimit.getCount(); - String text = String.format(context.getString(R.string.share_download_limit_description), remaining); + String text = context.getResources().getQuantityString(R.plurals.share_download_limit_description, remaining, remaining); binding.subline.setText(text); binding.subline.setVisibility(View.VISIBLE); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 50ccd5127a3e..257ef2ac637f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1306,5 +1306,8 @@ Manual intervention required to re-enable auto-upload Set download limit Download limit - %1$d downloads remaining + + %1$d download remaining + %1$d downloads remaining + From bd6468ace3799f02c7a3714403bdb4c853d81c8d Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Mon, 20 Jan 2025 17:24:01 +0100 Subject: [PATCH 10/19] Constrain text input length Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../main/res/layout/file_details_sharing_process_fragment.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/file_details_sharing_process_fragment.xml b/app/src/main/res/layout/file_details_sharing_process_fragment.xml index dc25f42f2495..14af3dff721c 100644 --- a/app/src/main/res/layout/file_details_sharing_process_fragment.xml +++ b/app/src/main/res/layout/file_details_sharing_process_fragment.xml @@ -164,7 +164,8 @@ android:ems="10" android:gravity="top" android:importantForAutofill="no" - android:inputType="number"> + android:inputType="number" + android:maxLength="9"> From 7555f996811e879c98080b7570ba38f6c94a2478 Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Mon, 20 Jan 2025 17:39:17 +0100 Subject: [PATCH 11/19] Implement suggestions Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../owncloud/android/datamodel/FileDataStorageManager.java | 7 ++++--- .../ui/fragment/FileDetailsSharingProcessFragment.kt | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index d44d983fb817..5cb0dbafb420 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -1557,9 +1557,10 @@ private ContentValues createContentValueForShare(OCShare share) { contentValues.put(ProviderTableMeta.OCSHARES_SHARE_LINK, share.getShareLink()); contentValues.put(ProviderTableMeta.OCSHARES_SHARE_LABEL, share.getLabel()); - if (share.getFileDownloadLimit() != null) { - contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT, share.getFileDownloadLimit().getLimit()); - contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT, share.getFileDownloadLimit().getCount()); + FileDownloadLimit downloadLimit = share.getFileDownloadLimit(); + if (downloadLimit != null) { + contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT, downloadLimit.getLimit()); + contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT, downloadLimit.getCount()); } return contentValues; diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt index ef64f7e0bc80..d82e6200629b 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt @@ -21,6 +21,7 @@ import androidx.fragment.app.Fragment import com.nextcloud.client.di.Injectable import com.nextcloud.utils.extensions.getParcelableArgument import com.nextcloud.utils.extensions.getSerializableArgument +import com.nextcloud.utils.extensions.setVisibleIf import com.owncloud.android.R import com.owncloud.android.databinding.FileDetailsSharingProcessFragmentBinding import com.owncloud.android.datamodel.OCFile @@ -503,7 +504,7 @@ class FileDetailsSharingProcessFragment : } private fun showFileDownloadLimitInput(isChecked: Boolean) { - binding.shareProcessSetDownloadLimitInputContainer.visibility = if (isChecked) View.VISIBLE else View.GONE + binding.shareProcessSetDownloadLimitInputContainer.setVisibleIf(isChecked) // reset download limit if switch is unchecked if (!isChecked) { From 9ca02254ff87517ecba2c068e19acf73c38897ee Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Tue, 21 Jan 2025 10:32:41 +0100 Subject: [PATCH 12/19] Update library version Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a451158c520b..e6e18d149cbe 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ */ buildscript { ext { - androidLibraryVersion ="40fea7dfa5" + androidLibraryVersion ="bf5fa888cf51896a20297818e04b3e59be4c4f93" androidPluginVersion = '8.8.0' androidxMediaVersion = '1.5.1' androidxTestVersion = "1.6.1" From bb946abef5d001533e7528bb229a54c839b2481b Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Tue, 21 Jan 2025 11:52:27 +0100 Subject: [PATCH 13/19] update lib Signed-off-by: tobiasKaminsky --- gradle/verification-metadata.xml | 293 ++++++++++++++++++++++++------- 1 file changed, 232 insertions(+), 61 deletions(-) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index a155c3ff34e4..b195bf707dfc 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -8872,6 +8872,11 @@ + + + + + @@ -8907,6 +8912,11 @@ + + + + + @@ -8937,6 +8947,11 @@ + + + + + @@ -9082,14 +9097,6 @@ - - - - - - - - @@ -9106,20 +9113,12 @@ - - - + + + - - - - - - - - - - + + @@ -9194,14 +9193,6 @@ - - - - - - - - @@ -9290,22 +9281,6 @@ - - - - - - - - - - - - - - - - @@ -9354,14 +9329,6 @@ - - - - - - - - @@ -9466,14 +9433,6 @@ - - - - - - - - @@ -9794,6 +9753,14 @@ + + + + + + + + @@ -9810,6 +9777,14 @@ + + + + + + + + @@ -9842,6 +9817,14 @@ + + + + + + + + @@ -14029,6 +14012,14 @@ + + + + + + + + @@ -14161,6 +14152,11 @@ + + + + + @@ -14172,6 +14168,14 @@ + + + + + + + + @@ -14212,6 +14216,14 @@ + + + + + + + + @@ -14287,6 +14299,16 @@ + + + + + + + + + + @@ -14305,6 +14327,19 @@ + + + + + + + + + + + + + @@ -14425,6 +14460,11 @@ + + + + + @@ -14446,6 +14486,11 @@ + + + + + @@ -14470,6 +14515,14 @@ + + + + + + + + @@ -14485,6 +14538,11 @@ + + + + + @@ -14509,6 +14567,14 @@ + + + + + + + + @@ -14565,6 +14631,14 @@ + + + + + + + + @@ -14605,6 +14679,14 @@ + + + + + + + + @@ -14637,6 +14719,14 @@ + + + + + + + + @@ -16918,6 +17008,14 @@ + + + + + + + + @@ -17059,11 +17157,21 @@ + + + + + + + + + + @@ -17321,6 +17429,14 @@ + + + + + + + + @@ -17353,6 +17469,14 @@ + + + + + + + + @@ -17395,6 +17519,14 @@ + + + + + + + + @@ -17427,6 +17559,14 @@ + + + + + + + + @@ -17459,6 +17599,14 @@ + + + + + + + + @@ -17568,11 +17716,24 @@ + + + + + + + + + + + + + @@ -17608,6 +17769,11 @@ + + + + + @@ -17666,6 +17832,11 @@ + + + + + From 43f9cfe89f67715d866561bb6aa2aeef9bd74551 Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:54:58 +0100 Subject: [PATCH 14/19] Fix lint Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../android/datamodel/FileDataStorageManager.java | 8 ++++++-- .../android/ui/adapter/LinkShareViewHolder.java | 15 ++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 5cb0dbafb420..7993cf43ee0b 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -1576,7 +1576,8 @@ private OCShare createShareInstance(Cursor cursor) { share.setPermissions(getInt(cursor, ProviderTableMeta.OCSHARES_PERMISSIONS)); share.setSharedDate(getLong(cursor, ProviderTableMeta.OCSHARES_SHARED_DATE)); share.setExpirationDate(getLong(cursor, ProviderTableMeta.OCSHARES_EXPIRATION_DATE)); - share.setToken(getString(cursor, ProviderTableMeta.OCSHARES_TOKEN)); + String token = getString(cursor, ProviderTableMeta.OCSHARES_TOKEN); + share.setToken(token); share.setSharedWithDisplayName(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_WITH_DISPLAY_NAME)); share.setFolder(getInt(cursor, ProviderTableMeta.OCSHARES_IS_DIRECTORY) == 1); share.setUserId(getString(cursor, ProviderTableMeta.OCSHARES_USER_ID)); @@ -1587,7 +1588,10 @@ private OCShare createShareInstance(Cursor cursor) { share.setShareLink(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_LINK)); share.setLabel(getString(cursor, ProviderTableMeta.OCSHARES_SHARE_LABEL)); - share.setFileDownloadLimit(new FileDownloadLimit(share.getToken(), getInt(cursor, ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT), getInt(cursor, ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT))); + FileDownloadLimit downloadLimit = new FileDownloadLimit(token, + getInt(cursor, ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT), + getInt(cursor, ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT)); + share.setFileDownloadLimit(downloadLimit); return share; } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java index 2b2e6f146a51..f6267d95a906 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java @@ -77,16 +77,13 @@ public void bind(OCShare publicShare, ShareeListAdapterListener listener) { viewThemeUtils.platform.colorImageViewBackgroundAndIcon(binding.icon); } - if (publicShare.getFileDownloadLimit() != null) { - FileDownloadLimit downloadLimit = publicShare.getFileDownloadLimit(); + FileDownloadLimit downloadLimit = publicShare.getFileDownloadLimit(); + if (downloadLimit != null && downloadLimit.getLimit() > 0) { + int remaining = downloadLimit.getLimit() - downloadLimit.getCount(); + String text = context.getResources().getQuantityString(R.plurals.share_download_limit_description, remaining, remaining); - if (downloadLimit.getLimit() > 0) { - int remaining = downloadLimit.getLimit() - downloadLimit.getCount(); - String text = context.getResources().getQuantityString(R.plurals.share_download_limit_description, remaining, remaining); - - binding.subline.setText(text); - binding.subline.setVisibility(View.VISIBLE); - } + binding.subline.setText(text); + binding.subline.setVisibility(View.VISIBLE); } String permissionName = SharingMenuHelper.getPermissionName(context, publicShare); From ee1d5d5fa21cf286b5f4357a635f0318e653181e Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Tue, 21 Jan 2025 17:36:20 +0100 Subject: [PATCH 15/19] add screenshot Signed-off-by: tobiasKaminsky --- ...haringFragmentIT_listSharesDownloadLimit.png | Bin 0 -> 25040 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png diff --git a/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png b/app/screenshots/gplay/debug/com.owncloud.android.ui.fragment.FileDetailSharingFragmentIT_listSharesDownloadLimit.png new file mode 100644 index 0000000000000000000000000000000000000000..11855bb2577685353c9a66410e157c1cb9a64a86 GIT binary patch literal 25040 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 literal 0 HcmV?d00001 From c90274d3ce0a0859c8910a46a80eec45df00aa4b Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:40:51 +0100 Subject: [PATCH 16/19] Update verification metadata Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- gradle/verification-metadata.xml | 67 ++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index b195bf707dfc..086541b8f6cd 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -9353,6 +9353,14 @@ + + + + + + + + @@ -10394,6 +10402,14 @@ + + + + + + + + @@ -10426,6 +10442,14 @@ + + + + + + + + @@ -10458,6 +10482,14 @@ + + + + + + + + @@ -10490,6 +10522,14 @@ + + + + + + + + @@ -10522,6 +10562,14 @@ + + + + + + + + @@ -10554,6 +10602,14 @@ + + + + + + + + @@ -10594,6 +10650,14 @@ + + + + + + + + @@ -11310,6 +11374,9 @@ + + + From c2f91a10cd0bbf111b9f425b5a33bde3c2b0043f Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Wed, 22 Jan 2025 09:43:10 +0100 Subject: [PATCH 17/19] Update database (rebase) Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../86.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json index 223c205aa90c..2571f61f74b5 100644 --- a/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json +++ b/app/schemas/com.nextcloud.client.database.NextcloudDatabase/86.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 86, - "identityHash": "9d5fd23c8ad44a5331aba97622c75caa", + "identityHash": "277489b9d4a6ee84f96d09dea39591ba", "entities": [ { "tableName": "arbitrary_data", @@ -44,7 +44,7 @@ }, { "tableName": "capabilities", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER)", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`_id` INTEGER PRIMARY KEY AUTOINCREMENT, `assistant` INTEGER, `account` TEXT, `version_mayor` INTEGER, `version_minor` INTEGER, `version_micro` INTEGER, `version_string` TEXT, `version_edition` TEXT, `extended_support` INTEGER, `core_pollinterval` INTEGER, `sharing_api_enabled` INTEGER, `sharing_public_enabled` INTEGER, `sharing_public_password_enforced` INTEGER, `sharing_public_expire_date_enabled` INTEGER, `sharing_public_expire_date_days` INTEGER, `sharing_public_expire_date_enforced` INTEGER, `sharing_public_send_mail` INTEGER, `sharing_public_upload` INTEGER, `sharing_user_send_mail` INTEGER, `sharing_resharing` INTEGER, `sharing_federation_outgoing` INTEGER, `sharing_federation_incoming` INTEGER, `files_bigfilechunking` INTEGER, `files_undelete` INTEGER, `files_versioning` INTEGER, `external_links` INTEGER, `server_name` TEXT, `server_color` TEXT, `server_text_color` TEXT, `server_element_color` TEXT, `server_slogan` TEXT, `server_logo` TEXT, `background_url` TEXT, `end_to_end_encryption` INTEGER, `end_to_end_encryption_keys_exist` INTEGER, `end_to_end_encryption_api_version` TEXT, `activity` INTEGER, `background_default` INTEGER, `background_plain` INTEGER, `richdocument` INTEGER, `richdocument_mimetype_list` TEXT, `richdocument_direct_editing` INTEGER, `richdocument_direct_templates` INTEGER, `richdocument_optional_mimetype_list` TEXT, `sharing_public_ask_for_optional_password` INTEGER, `richdocument_product_name` TEXT, `direct_editing_etag` TEXT, `user_status` INTEGER, `user_status_supports_emoji` INTEGER, `etag` TEXT, `files_locking_version` TEXT, `groupfolders` INTEGER, `drop_account` INTEGER, `security_guard` INTEGER, `forbidden_filename_characters` INTEGER, `forbidden_filenames` INTEGER, `forbidden_filename_extensions` INTEGER, `forbidden_filename_basenames` INTEGER, `files_download_limit` INTEGER, `files_download_limit_default` INTEGER, `recommendation` INTEGER)", "fields": [ { "fieldPath": "id", @@ -405,6 +405,12 @@ "columnName": "files_download_limit_default", "affinity": "INTEGER", "notNull": false + }, + { + "fieldPath": "recommendation", + "columnName": "recommendation", + "affinity": "INTEGER", + "notNull": false } ], "primaryKey": { @@ -1319,7 +1325,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9d5fd23c8ad44a5331aba97622c75caa')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '277489b9d4a6ee84f96d09dea39591ba')" ] } } \ No newline at end of file From d7411426c6f0795d942903e67cdc74b64af4dfec Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Wed, 22 Jan 2025 10:20:44 +0100 Subject: [PATCH 18/19] Fix NullPointerException Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../java/com/owncloud/android/ui/activity/DrawerActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index f2f3367d19a3..ff95b9854d35 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -1288,7 +1288,8 @@ public void fetchExternalLinks(final boolean force) { Thread t = new Thread(() -> { // fetch capabilities as early as possible - if ((getCapabilities() == null || getCapabilities().getAccountName() != null && getCapabilities().getAccountName().isEmpty()) + final OCCapability capability = getCapabilities(); + if ((capability == null || capability.getAccountName() == null || !capability.getAccountName().isEmpty()) && getStorageManager() != null) { GetCapabilitiesOperation getCapabilities = new GetCapabilitiesOperation(getStorageManager()); getCapabilities.execute(getBaseContext()); From a3ab2f479ebcb1c74ca86a2ed9b5cabae8883119 Mon Sep 17 00:00:00 2001 From: ZetaTom <70907959+ZetaTom@users.noreply.github.com> Date: Wed, 22 Jan 2025 11:59:59 +0100 Subject: [PATCH 19/19] Update download limit on file details screen Signed-off-by: ZetaTom <70907959+ZetaTom@users.noreply.github.com> --- .../datamodel/FileDataStorageManager.java | 3 ++ .../SetFilesDownloadLimitOperation.kt | 33 +++++++++++++++---- .../android/services/OperationsService.java | 2 +- .../android/ui/activity/FileActivity.java | 3 +- .../ui/adapter/LinkShareViewHolder.java | 2 ++ 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index 7993cf43ee0b..c4627a3c07d2 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -1561,6 +1561,9 @@ private ContentValues createContentValueForShare(OCShare share) { if (downloadLimit != null) { contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT, downloadLimit.getLimit()); contentValues.put(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT, downloadLimit.getCount()); + } else { + contentValues.putNull(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_LIMIT); + contentValues.putNull(ProviderTableMeta.OCSHARES_DOWNLOADLIMIT_COUNT); } return contentValues; diff --git a/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt b/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt index 46c648dd5c5e..8918172c73de 100644 --- a/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt +++ b/app/src/main/java/com/owncloud/android/operations/SetFilesDownloadLimitOperation.kt @@ -7,28 +7,49 @@ package com.owncloud.android.operations +import android.content.Context +import com.nextcloud.android.lib.resources.files.GetFilesDownloadLimitRemoteOperation import com.nextcloud.android.lib.resources.files.RemoveFilesDownloadLimitRemoteOperation import com.nextcloud.android.lib.resources.files.SetFilesDownloadLimitRemoteOperation -import com.nextcloud.common.NextcloudClient +import com.nextcloud.utils.extensions.toNextcloudClient import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult class SetFilesDownloadLimitOperation( private val shareId: Long, private val newLimit: Int, - private val fileDataStorageManager: FileDataStorageManager + private val fileDataStorageManager: FileDataStorageManager, + private val context: Context ) : RemoteOperation() { - override fun run(client: NextcloudClient): RemoteOperationResult { + @Deprecated("Deprecated in Java") + override fun run(client: OwnCloudClient): RemoteOperationResult { + val nextcloudClient = client.toNextcloudClient(context) val share = fileDataStorageManager.getShareById(shareId) val token = share?.token ?: return RemoteOperationResult(RemoteOperationResult.ResultCode.SHARE_NOT_FOUND) - return if (newLimit > 0) { + val result = if (newLimit > 0) { val operation = SetFilesDownloadLimitRemoteOperation(token, newLimit) - client.execute(operation) + nextcloudClient.execute(operation) } else { val operation = RemoveFilesDownloadLimitRemoteOperation(token) - client.execute(operation) + nextcloudClient.execute(operation) } + + val path = share.path + if (result.isSuccess && path != null) { + val getFilesDownloadLimitRemoteOperation = GetFilesDownloadLimitRemoteOperation(path, false) + val remoteOperationResult = getFilesDownloadLimitRemoteOperation.execute(client) + + if (remoteOperationResult.isSuccess) { + share.fileDownloadLimit = remoteOperationResult.resultData.firstOrNull { updatedDownloadLimit -> + updatedDownloadLimit.token == share.token + } + fileDataStorageManager.saveShare(share) + } + } + + return result } } diff --git a/app/src/main/java/com/owncloud/android/services/OperationsService.java b/app/src/main/java/com/owncloud/android/services/OperationsService.java index e4801c97f240..4ccadf9d6b0a 100644 --- a/app/src/main/java/com/owncloud/android/services/OperationsService.java +++ b/app/src/main/java/com/owncloud/android/services/OperationsService.java @@ -742,7 +742,7 @@ private Pair newOperation(Intent operationIntent) { int newLimit = operationIntent.getIntExtra(EXTRA_FILES_DOWNLOAD_LIMIT, -1); if (shareId > 0) { - operation = new SetFilesDownloadLimitOperation(shareId, newLimit, fileDataStorageManager); + operation = new SetFilesDownloadLimitOperation(shareId, newLimit, fileDataStorageManager, getApplicationContext()); } break; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 8f3faef62125..84c6d4e14bf5 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -68,6 +68,7 @@ import com.owncloud.android.operations.CreateShareViaLinkOperation; import com.owncloud.android.operations.CreateShareWithShareeOperation; import com.owncloud.android.operations.GetSharesForFileOperation; +import com.owncloud.android.operations.SetFilesDownloadLimitOperation; import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.operations.SynchronizeFolderOperation; import com.owncloud.android.operations.UnshareOperation; @@ -414,7 +415,7 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result); } else if (operation instanceof CreateShareWithShareeOperation) { onUpdateShareInformation(result, R.string.sharee_add_failed); - } else if (operation instanceof UpdateShareViaLinkOperation || operation instanceof UpdateShareInfoOperation) { + } else if (operation instanceof UpdateShareViaLinkOperation || operation instanceof UpdateShareInfoOperation || operation instanceof SetFilesDownloadLimitOperation) { onUpdateShareInformation(result, R.string.updating_share_failed); } else if (operation instanceof UpdateSharePermissionsOperation) { onUpdateShareInformation(result, R.string.updating_share_failed); diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java index f6267d95a906..b2e70da44dd8 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.java @@ -84,6 +84,8 @@ public void bind(OCShare publicShare, ShareeListAdapterListener listener) { binding.subline.setText(text); binding.subline.setVisibility(View.VISIBLE); + } else { + binding.subline.setVisibility(View.GONE); } String permissionName = SharingMenuHelper.getPermissionName(context, publicShare);